作为一些性能评估的一部分,我正在执行重复更新操作以将文档添加到MongoDB中。基于我正在执行的更新(w / upserts)的数量,我发现了执行时间的巨大非线性:
在Python中使用以下命令循环...
collection.update({'timestamp': x}, {'$set': {'value1':y, v1 : y/2, v2 : y/4}}, upsert=True)
给我这些结果......
500 document upserts 2 seconds.
1000 document upserts 3 seconds.
2000 document upserts 3 seconds.
4000 document upserts 6 seconds.
8000 document upserts 14 seconds.
16000 document upserts 77 seconds.
32000 document upserts 280 seconds.
请注意,在8k文档更新后,性能开始迅速降低,并且通过32k文档更新,我们看到吞吐量降低了6倍。为什么是这样?看起来奇怪的是,连续8次“手动”运行4k文档更新比使用Python连续执行它们快6倍。
我已经看到在mongostats中我获得了一个可笑的高锁定db比率(> 100%)和 当它运行时,top显示我> 85%的CPU使用率。我有一个i7处理器,可以为VM提供4个内核。
答案 0 :(得分:11)
您应该在“timestamp”字段中添加升序索引:
collection.ensure_index("timestamp") # shorthand for single-key, ascending index
如果此索引应包含唯一值:
collection.ensure_index("timestamp", unique=True)
由于规范未编入索引且您正在执行更新,因此数据库必须检查集合中的每个文档,以查看是否已存在该规范的任何文档。当你为500个文档(在一个空白集合中)执行此操作时,效果并不是那么糟糕......但是当你为32k执行此操作时,它会执行类似的操作(在最坏的情况下):
文件1 - 假设空白收集,肯定会插入
文件2 - 检查文件1,更新或插入
文件3 - 检查文件1-2,更新或插入
...等...
文件32000 - 检查文件1-31999,更新或插入
添加索引时,数据库不再需要检查集合中的每个文档;相反,它可以使用索引来使用B树游标而不是基本游标更快地找到任何可能的匹配。
您应该使用和不使用索引来比较collection.find({"timestamp": x}).explain()
的结果(请注意,您可能需要使用hint()
方法强制它使用索引)。关键因素是您必须迭代的文档数量(explain()
的“nscanned”结果)与您的查询匹配的文档数量(“n”键)。如果数据库只需要准确扫描匹配或接近匹配的内容,那就非常有效;如果您扫描32000个项目但只找到1个或少数几个匹配项,那效率非常低,特别是如果db必须为每个upsert 执行类似的操作。
由于您未在multi=True
调用中设置update
,因此您需要仔细检查 - 如果更新操作找到匹配的文档,则会更新它并且不会继续检查整个系列。
很抱歉链接垃圾邮件,但这些都是必读内容:
http://docs.mongodb.org/manual/core/indexes/
http://docs.mongodb.org/manual/reference/method/cursor.explain/