作为某些性能评估的一部分,我正在执行重复更新操作以将文档添加到我的 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 中我得到了高得离谱的锁定数据库比率 (>100%) 并且 top 在运行时显示 >85% 的 CPU 使用率。我有一个 i7 处理器,有 4 个内核可供 VM 使用。
最佳答案
您应该在“时间戳”字段上放置一个升序索引:
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 个或少数匹配项,那效率会非常低,尤其是当数据库必须为每个更新插入做类似的事情时。
需要仔细检查的一个值得注意的问题 - 因为您没有在 update 调用中设置 multi=True,如果更新操作找到匹配的文档,它将只更新它而不是继续检查整个集合。
抱歉链接垃圾邮件,但这些都是必读的:
http://docs.mongodb.org/manual/core/indexes/
http://docs.mongodb.org/manual/reference/method/cursor.explain/
关于python - MongoDB Update-Upsert Performance Barrier(性能跌落悬崖),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22077685/