[原] 分享一下我和 MongoDB 与 Redis 那些事缘起:来自于我在近期一个项目上遇到的问题,在 Segmentfault 上发表了提问 知识背景: 对不是很熟悉 MongoDB 和 Redis 的同学做一下介绍。
整体架构如下图所示: 生产者产生任务,通过 LVS 与 RPC 服务器将任务记录到 MongoDB,消费者同样通过 RPC 服务获取任务,这是个很简单的架构,一般服务可能去掉集群都是这样的。 整个业务架构需要一个前提,任务不能丢失,也就是说任务即使失败也需要重新加入到队列,至少若干次后任然失败也要知道为什么失败 (非记录日志形式)。 很多人问为什么不直接用 RabbitMQ 或者 Redis,因为这类消息队列无法做到管理任务超时等情况,因为业务需要,也需要做一些简单的查询,这类队列是不支持某些稍复杂的查询的,而且一开始我们的任务量估计在 5KW/Day 这样,担心 Redis 扛不住,后来我发现这是个错误的假设。 问题内容如下: 问题背景: 近期在重构公司内部一个重要的任务系统,由于原来的任务系统使用了 MongoDB 来保存任务,客户端从 MongoDB 来取,至于为什么用 MongoDB,是一个历史问题,也是因为如果使用到MongoDB 的数组查询可以减少任务数量很多次,假设这样的情况,一个 md5(看做一条记录的唯一标识) 需要针对 N 种情况做任务处理,如果用到 MongoDB 的数组,只需要将一个 md5 作为一条任务,其中包含一个长度为 N 的待处理任务列表,可以使用到 MongoDB 的数组 (只有 N 个子任务都处理完后整个任务才算处理完毕),这样整个任务系统的数量级就变为原来的 1/N(如果需要用到普通的关系型数据库,可能需要创建 m*n 个任务,这样算下来我们的任务数量将可能达到一个很大的值,主要是因为处理任务的进程由于某些不确定因素无法控制,所以比较慢) 细节描述:
改进方案如下: 由于原有代码的耦合,不能完全抛弃 MongoDB,所以决定加一个 Redis 缓存。一个 md5 对应的 N 个子任务分发到 N 个 Redis 队列中(拆分子任务)。一个单独的进程从 MongoDB 中向 Redis 中将任务同步,客户端不再从 MongoDB 取任务。这样做的好处是抛弃了原有的 MongoDB 的数组查询,同步进程从 MongoDB 中取任务是按照任务的优先级偏移(已做索引)来取,所以速度比数组查询要快。这样客户端向 Redis 的 N 个队列中取子任务,把任务结果返回原来的 MongoDB 任务记录中 (根据 md5 返回子任务)。 改进过程遇到的问题: 由于任务处理端向 MongoDB 返回时候会有一个 update 操作,如果 N 个子任务都完成,就将任务从 MongoDB 中删除。这样的一个问题就是,经过测试后发现 MongoDB 在高并发写的情况下性能很低下,整个任务系统任务处理速度最大为 200/s(16 核, 16G, CentOS, 内核 2.6.32-358.6.3.el6.x86_64),原因大致为在频繁写情况下,MongoDB 的性能会由于锁表操作急剧下降 (锁表时间可以达到 60%-70%,熟悉 MongoDB 的人都知道这是多么恐怖的数字)。 具体问题: (Think out of the Box)能否提出一个好的解决方案,能够保存任务状态 (子任务状态),速度至少超过 MongoDB 的? 提出这个问题后,很感谢官方将问题发到微博首页,有一个回答我觉得可以采纳:
|