开发者社区> 问答> 正文

MongoDB 和Redis的任务队列性能瓶颈

近期在重构公司内部一个重要的任务系统,由于原来的任务系统使用了MongoDB来保存任务,客户端从MongoDB来取,至于为什么用MongoDB,是一个历史问题,也是因为如果使用到MongoDB的数组查询可以减少任务数量很多次,假设这样的情况,一个md5需要针对N种情况做任务处理,如果用到MongoDB的数组,只需要将一个md5作为一条任务,其中包含一个长度为N的待处理任务列表(只有N个子任务都处理完后整个任务才算处理完毕),这样整个任务系统的数量级就变为原来的 1/N。
细节描述: 1.当MongoDB的任务数量增多的时候,数组查询相当的慢,任务数达到5K就已经不能容忍了。 2.任务处理每个md5对应的N个子任务必须要全部完成才从MongoDB中删除 3.任务在超时后可以重置
改进方案如下: 由于原有代码的耦合,不能完全抛弃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的性能会由于锁表操作急剧下降。
具体问题: (Think out of the Box)能否提出一个好的解决方案,能够保存任务状态(子任务状态),速度至少超过MongoDB的?

展开
收起
蛮大人123 2016-02-14 10:53:59 3162 0
1 条回答
写回答
取消 提交回答
  • 我说我不帅他们就打我,还说我虚伪

    个人认为题主提到的MongoDB数组查询和更新的性能问题,很可能是Schema设计上的问题。但题主并没有给出具体的设计,所以我就提出几个值得关注的点仅供参考:
    1.索引,正如楼上所说,你应该已经为数组加上了索引。但是值得注意的是,数组字段的索引比普通字段的索引体积要大很多(具体取决于数组的大小,数组越大,索引所占的空间越大)。这样就可能会导致一个问题:索引并不(完全)在内存里! 后果是,每次查询都需要涉及到额外的IO操作,性能会急剧下降。
    2.查询返回文档的大小。如果每次返回查询的文档数据量较大,而且客户端与mongodb并不处于同一机器上,那就会增加了网络传输所需的时间(不要小看这点时间),所以尽可能只返回所需要的字段。
    3.update-in-place. 由于schemaless的特性,mongodb会为每条文档记录预留一些空间给增加额外的字段或数据时使用,提高update的性能。但如果你文档的大小频繁地扩展(增加字段,增加数组长度等),那就会导致写的性能问题:mongodb需要把增长了的文档移动(move)到别的地方。(相当于从硬盘的一个位置移动另一个更空闲的位置)这时候的性能会大大下降。
    mongodb是一个内存型的数据库,如果你的热点数据都在内存上,它的性能会非常优异,而这很大程度取决与你的Schema设计。
    mongodb一直标榜的Schemaless优点误导了很多人,其实这个更多是想说明mongodb是动态的schema,而并不是不需要设计schema。

    2019-07-17 18:42:29
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
Redis集群演化的心路历程——从2.x到3.0时代 立即下载
微博的Redis定制之路 立即下载
云数据库Redis版的开源之路 立即下载