大数据库中非常慢的 MongoDB 计数

Posted

技术标签:

【中文标题】大数据库中非常慢的 MongoDB 计数【英文标题】:Very slow MongoDB count in big database 【发布时间】:2014-06-10 12:17:31 【问题描述】:

我有一个数据库,其中包含一个包含大量文档(数百万)的集合。 在这个数据库中,我有(除其他外)字段 _VIOLATIONTYPE (int) 和 _DURATION (int)。 现在我想计算 _VIOLATIONTYPE 为 15 或更少且 _DURATION 为 10 或更少的文档数量。 为此,我执行以下 Python 脚本:

#!/usr/bin/env python
import pymongo
import timeit


client = pymongo.MongoClient('localhost', 27017)
database = client['bgp_route_leaks']

collection = database['valleys']

collection.ensure_index('_VIOLATIONTYPE', unique=False)
collection.ensure_index('_DURATION', unique=False)

start = timeit.default_timer()

cursor = collection.find('$and': ['_VIOLATIONTYPE': '$lt': 16, '_DURATION': '$lt': 10], '_DURATION': 1, '_id': 0)

print('Explain: '.format(cursor.explain()))
print('Count: '.format(cursor.count()))
print('Time: '.format(timeit.default_timer() - start))

打印出来:

Explain: u'nYields': 4, u'nscannedAllPlans': 6244545, u'allPlans': [u'cursor': u'BtreeCursor _VIOLATIONTYPE_1', u'indexBounds': u'_VIOLATIONTYPE': [[-1.7976931348623157e+308, 16]], u'nscannedObjects': 124, u'nscanned': 124, u'n': 34, u'cursor': u'BtreeCursor _DURATION_1', u'indexBounds': u'_DURATION': [[-1.7976931348623157e+308, 10]], u'nscannedObjects': 6244298, u'nscanned': 6244298, u'n': 5678070, u'cursor': u'BasicCursor', u'indexBounds': , u'nscannedObjects': 123, u'nscanned': 123, u'n': 36], u'millis': 30815, u'nChunkSkips': 0, u'server': u'area51:27017', u'n': 5678107, u'cursor': u'BtreeCursor _DURATION_1', u'scanAndOrder': False, u'indexBounds': u'_DURATION': [[-1.7976931348623157e+308, 10]], u'nscannedObjectsAllPlans': 6244545, u'isMultiKey': False, u'indexOnly': True, u'nscanned': 6244298, u'nscannedObjects': 6244298
Count: 5678107
Time: 52.4030768871

在运行时,我还在另一个窗口中执行了 db.currentOp(),它返回了


        "inprog" : [
                
                        "opid" : 15,
                        "active" : true,
                        "secs_running" : 4,
                        "op" : "query",
                        "ns" : "bgp_route_leaks.valleys",
                        "query" : 
                                "$query" : 
                                        "$and" : [
                                                
                                                        "_VIOLATIONTYPE" : 
                                                                "$lt" : 16
                                                        
                                                ,
                                                
                                                        "_DURATION" : 
                                                                "$lt" : 10
                                                        
                                                
                                        ]
                                ,
                                "$explain" : true
                        ,
                        "client" : "127.0.0.1:46819",
                        "desc" : "conn1",
                        "threadId" : "0x7fd69b31c700",
                        "connectionId" : 1,
                        "locks" : 
                                "^" : "r",
                                "^bgp_route_leaks" : "R"
                        ,
                        "waitingForLock" : false,
                        "numYields" : 5,
                        "lockStats" : 
                                "timeLockedMicros" : 
                                        "r" : NumberLong(8816104),
                                        "w" : NumberLong(0)
                                ,
                                "timeAcquiringMicros" : 
                                        "r" : NumberLong(4408723),
                                        "w" : NumberLong(0)
                                
                        
                
        ]

现在我了解到,最常见的慢速 MongoDB 查询来源是缺少索引。 但是,我确保了 _VIOLATIONTYPE 和 _DURATION 的索引,并且解释告诉我 u'indexOnly': True。 我还读到 NUMA 架构可能会减慢速度,我应该通过命令启动服务

sudo numactl --interleave=all /usr/bin/mongod --dbpath=/var/lib/mongodb
(/proc/sys/vm/zone_reclaim_mode is already set to 0)

我知道已经完成了,但是这个计数仍然需要大约一分钟,其他计数甚至更长,所以我想知道如何做才能使查询更快。

跑步

db.runCommand(compact: 'bgp_route_leaks')

在 mongo shell 中也尝试过,但没有成功。

关于如何更快地获得计数的任何建议?

MongoDB 版本是 2.4.9。

【问题讨论】:

【参考方案1】:

如果您查看 explain 输出,您将看到使用 _VIOLATIONTYPE 的查询仅扫描 124 个对象,而使用 _DURATION 的查询正在扫描 6244545 个对象。

虽然 MongoDB 2.6+ 可以使用index intersection,但复合索引总是更快。

您需要在这些字段上创建复合索引:

collection.create_index([("_VIOLATIONTYPE", ASCENDING),("_DURATION", ASCENDING)]);

编辑

在 2.4 版本中,MongoDB 的性能得到了显着提升 (JIRA-1752)。

另外,值得注意的是explain 命令正在显示查询而不是计数命令的详细信息。

很遗憾,您不能在 count 命令上使用 explain,但是针对该问题打开了 ticket。

要仅测量 count 命令的性能,您可能应该从测试中删除 explain。此外,您需要多次重复查询(100x、1000x...)并取平均值以获得正确的值。

【讨论】:

感谢您的建议,它确实提高了性能(在初始化密钥几分钟后),但没有我希望的那么多。它现在输出以下时间:22.7881669998。你知道有什么其他方法可以进一步提高速度吗?因为我需要执行多个类似的查询,目前它们需要将近一个小时才能完成。 @user3664397 - 你使用的是什么版本的 MongoDB? MongoDB版本是2.4.9,我现在已经加到原帖里了 @user3664397 - 添加了对此行为的可能解释。 我注意到,事实上,对于大多数查询来说,一半的时间仍然太多。并且获得更准确的性能值也不是那么有趣,因为它主要围绕该值,我需要它显着更快。我发现问题的原因很可能是索引值不适合 RAM 内存,并且大部分都存储在交换内存中。所以我认为最好的解决方案是减少要处理的记录数量。不过,我不确定在我的情况下我将如何处理它。感谢所有的建议。 :)

以上是关于大数据库中非常慢的 MongoDB 计数的主要内容,如果未能解决你的问题,请参考以下文章

MySQL中非常慢的子查询

Swift中非常慢的扫雷递归算法

Windows 10 中非常慢的 git clone

ModelSerializer 在 Django REST 框架中非常慢

MySQL 查询在新的 XAMPP 安装中非常慢[关闭]

Wordpress 自定义元查询搜索在 OR 关系中非常慢