MongoDB解释了totalKeysExamined超过限制

时间:2016-12-11 11:31:04

标签: mongodb

我有一个非常大的集合(数百万个文档),其数据看起来像:

u'timestamp': 1481454871423.0,
u'_id': ObjectId('584d351772c4d8106cc43116'),
u'data': {
       ...
    },
u'geocode': [{u'providerId': 2, u'placeId': 97459515},
             {u'providerId': 3, u'placeId': 237},
             {u'providerId': 3, u'placeId': 3}]

我想要一个针对providerId和placeId对的查询,并且只在时间戳范围内返回10条记录。

为此,我执行以下查询:

 'geocode.providerId': 3,
 'geocode.placeId': 3
 'timestamp': { '$gte': 1481454868360L,
                '$lt': 1481454954839L }

我提供一个提示,目标索引如下:

[('geocode.providerId', 1), ('geocode.placeId', 1), ('timestamp', 1)]

其中1是升序。在迭代返回的游标之前,限制为10条记录,并按时间戳递增排序(由于索引,它应该是默认状态)。

pymongo查询看起来像:

collection.find(findDic).hint(hint).sort([('timestamp', pymongo.ASCENDING)]).skip(0).limit(10)

查询解释回来看起来像:

{
u'executionStats': {
    u'executionTimeMillis': 1270,
    u'nReturned': 10,
    u'totalKeysExamined': 568686,
    u'allPlansExecution': [],
    u'executionSuccess': True,
    u'executionStages': {
        u'needYield': 0,
        u'saveState': 4442,
        u'memUsage': 54359,
        u'restoreState': 4442,
        u'memLimit': 33554432,
        u'isEOF': 1,
        u'inputStage': {
            u'needYield': 0,
            u'saveState': 4442,
            u'restoreState': 4442,
            u'isEOF': 1,
            u'inputStage': {
                u'needYield': 0,
                u'docsExamined': 284964,
                u'saveState': 4442,
                u'restoreState': 4442,
                u'isEOF': 1,
                u'inputStage': {
                    u'saveState': 4442,
                    u'isEOF': 1,
                    u'seenInvalidated': 0,
                    u'keysExamined': 568686,
                    u'nReturned': 284964,
                    u'invalidates': 0,
                    u'keyPattern': {u'geocode.providerId': 1,
                            u'timestamp': 1,
                            u'geocode.placeId': 1},
                    u'isUnique': False,
                    u'needTime': 283722,
                    u'isMultiKey': True,
                    u'executionTimeMillisEstimate': 360,
                    u'dupsTested': 568684,
                    u'restoreState': 4442,
                    u'direction': u'forward',
                    u'indexName': u'geocode.providerId_1_geocode.placeId_1_timestamp_1',
                    u'isSparse': False,
                    u'advanced': 284964,
                    u'stage': u'IXSCAN',
                    u'dupsDropped': 283720,
                    u'needYield': 0,
                    u'isPartial': False,
                    u'indexBounds': {u'geocode.providerId': [u'[3, 3]'
                            ],
                            u'timestamp': [u'[-inf.0, 1481455513378)'
                            ],
                            u'geocode.placeId': [u'[MinKey, MaxKey]'
                            ]},
                    u'works': 568687,
                    u'indexVersion': 1,
                    },
                u'nReturned': 252823,
                u'needTime': 315863,
                u'filter': {u'$and': [{u'geocode.placeId': {u'$eq': 3}},
                            {u'timestamp': {u'$gte': 1481405886510L}}]},
                u'executionTimeMillisEstimate': 970,
                u'alreadyHasObj': 0,
                u'invalidates': 0,
                u'works': 568687,
                u'advanced': 252823,
                u'stage': u'FETCH',
                },
            u'nReturned': 0,
            u'needTime': 315864,
            u'executionTimeMillisEstimate': 1150,
            u'invalidates': 0,
            u'works': 568688,
            u'advanced': 0,
            u'stage': u'SORT_KEY_GENERATOR',
            },
        u'nReturned': 10,
        u'needTime': 568688,
        u'sortPattern': {u'timestamp': 1},
        u'executionTimeMillisEstimate': 1200,
        u'limitAmount': 10,
        u'invalidates': 0,
        u'works': 568699,
        u'advanced': 10,
        u'stage': u'SORT',
        },
    u'totalDocsExamined': 284964,
    },
u'queryPlanner': {
    u'parsedQuery': {u'$and': [{u'geocode.placeId': {u'$eq': 3}},
                     {u'geocode.providerId': {u'$eq': 3}},
                     {u'timestamp': {u'$lt': 1481455513378L}},
                     {u'timestamp': {u'$gte': 1481405886510L}}]},
    u'rejectedPlans': [],
    u'namespace': u'mxp957.tweet_244de17a-aa75-4da9-a6d5-97b9281a3b55',
    u'winningPlan': {
        u'sortPattern': {u'timestamp': 1},
        u'inputStage': {u'inputStage': {u'filter': {u'$and': [{u'geocode.placeId': {u'$eq': 3}},
                        {u'timestamp': {u'$gte': 1481405886510L}}]},
                        u'inputStage': {
            u'direction': u'forward',
            u'indexName': u'geocode.providerId_1_geocode.placeId_1_timestamp_1',
            u'isUnique': False,
            u'isSparse': False,
            u'isPartial': False,
            u'indexBounds': {u'geocode.providerId': [u'[3, 3]'],
                             u'timestamp': [u'[-inf.0, 1481455513378)'
                             ],
                             u'geocode.placeId': [u'[MinKey, MaxKey]'
                             ]},
            u'isMultiKey': True,
            u'stage': u'IXSCAN',
            u'indexVersion': 1,
            u'keyPattern': {u'geocode.providerId': 1,
                            u'timestamp': 1,
                            u'geocode.placeId': 1},
            }, u'stage': u'FETCH'},
                u'stage': u'SORT_KEY_GENERATOR'},
        u'limitAmount': 10,
        u'stage': u'SORT',
        },
    u'indexFilterSet': False,
    u'plannerVersion': 1,
    },
u'ok': 1.0,
u'serverInfo': {
    u'host': u'rabbit',
    u'version': u'3.2.11',
    u'port': 27017,
    u'gitVersion': u'009580ad490190ba33d1c6253ebd8d91808923e4',
    },
}

我不明白为什么需要检查所有这些文件。在上面的例子中,集合的大小只有284587,这意味着每个记录都被查看了两次!我希望totalKeysExamined只有10,但我很难看到如何实现这一目标。

我正在使用MongoDB版本3.2.11和pymongo。

1 个答案:

答案 0 :(得分:0)

Astro提到,问题是MongoDB没有有效地使用索引。

MongoDB团队表示在以后的版本中解决了这个问题: https://jira.mongodb.org/browse/SERVER-27386

另外一个选项是从索引中删除providerId。在我的用例中,providerId是两个值之一,并且大多数时间总是相同的值。它表示用于地理编码的API;我的系统只支持两个,一次只启用一个。

请参阅解决此问题的提交: https://github.com/watfordxp/GeoTweetSearch/commit/420536e4a138fb22e0dd0e61ef9c83c23a9263c1