问题背景: 近期在重构公司内部一个重要的任务系统,由于原来的任务系统使用了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的性能会由于锁表操作急剧下降。
具体问题: 能否提出一个好的解决方案,能够保存任务状态(子任务状态),速度至少超过MongoDB的?
仅供参考
1.索引,正如楼上所说,你应该已经为数组加上了索引。但是值得注意的是,数组字段的索引比普通字段的索引体积要大很多(具体取决于数组的大小,数组越大,索引所占的空间越大)。这样就可能会导致一个问题:索引并不(完全)在内存里! 后果是,每次查询都需要涉及到额外的IO操作,性能会急剧下降。
2.查询返回文档的大小。如果每次返回查询的文档数据量较大,而且客户端与mongodb并不处于同一机器上,那就会增加了网络传输所需的时间(不要小看这点时间),所以尽可能只返回所需要的字段。
3.update-in-place. 由于schemaless的特性,mongodb会为每条文档记录预留一些空间给增加额外的字段或数据时使用,提高update的性能。但如果你文档的大小频繁地扩展(增加字段,增加数组长度等),那就会导致写的性能问题:mongodb需要把增长了的文档移动(move)到别的地方。(相当于从硬盘的一个位置移动另一个更空闲的位置)这时候的性能会大大下降。
mongodb是一个内存型的数据库,如果你的热点数据都在内存上,它的性能会非常优异,而这很大程度取决与你的Schema设计。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。