2 億 5000 万以上のドキュメントをホストする MongoDB シャード クラスターがあります。
ドキュメントの構造は次のとおりです。
{
"app_id": "whatever",
"created": ISODate("2018-05-06T12:13:45.000Z"),
"latest_transaction": ISODate("2019-03-06T11:11:40.000Z"),
"anotherField1": "Str", "anotherField2": "Str", ...otherfields
}
{
"app_id": "whatever",
"created": ISODate("2018-04-06T12:13:45.000Z"),
"latest_transaction": ISODate("2019-03-06T11:11:40.000Z"),
"uninstalled": ISODate("2019-03-07T11:11:40.000Z"),
"anotherField1": "Str", "anotherField2": "Str", ...otherfields
}
したがって、基本的に一部のドキュメントにはフィールドuninstalledがあり、一部のドキュメントにはありません。
以下は、コレクションに対するクエリです (これは pymongo の説明です。datetime.datetime s については申し訳ありません)。
{
'$and': [
{'app_id': {'$eq': 'whatever'}},
{'created': {'$lt': datetime.datetime(2019, 3, 7, 0, 0)}},
{'latest_transaction': {'$gt': datetime.datetime(2019, 2, 5, 0, 0)}},
{'$nor': [{'uninstalled': {'$lt': datetime.datetime(2019, 3, 7, 0, 0)}}]}
]
}
コレクションにある 2 つの関連するインデックスを次に示します。
Index1: {"created": 1, "latest_transaction": -1, "uninstalled": -1, "app_id": 1}
Index2: {'app_id': 1, 'anotherField1': 1, 'anotherField2': 1}
問題は、MongoDb クエリ プランナーが、まったく同じ目的 でコレクションにあるIndex1を選択しないように見えることです。
私の最初の印象は、クエリは、インデックスを構造化した方法でカバーされたインデックスを使用するというものでした [したがって、非常に高速です]。 150 万のドキュメントの結果セットの場合は 6 分 [つまり、一致する app_id には約 150 万のドキュメントがあります]。
「Index1」を使用して拒否された計画を示すクエリの Explain の出力を次に示します。
{
'inputStage': {
'inputStage': {
'direction': 'forward',
'indexBounds': {
'app_id': ['["whatever", "whatever"]'],
'created': ['(true, new Date(1551916800000))'],
'latest_transaction': ['[new Date(9223372036854775807), new Date(1549324800000))'],
'uninstalled': ['[MaxKey, new Date(1551916800000)]', '[true, MinKey]']
},
'indexName': 'created_1_latest_transaction_-1_uninstalled_-1_app_id_1',
'indexVersion': 2,
'isMultiKey': False,
'isPartial': False,
'isSparse': False,
'isUnique': False,
'keyPattern': {
'app_id': 1.0,
'created': 1.0,
'latest_transaction': -1.0,
'uninstalled': -1.0
},
'multiKeyPaths': {'app_id': [], 'created': [], 'latest_transaction': [], 'uninstalled': []},
'stage': 'IXSCAN'},
'stage': 'FETCH'},
'stage': 'SHARDING_FILTER'
}
以下は、無関係でカバーされていない Index2を使用した勝者の計画です。
{'inputStage': {
'inputStage': {'direction': 'forward',
'indexBounds': {
'app_id': ['["whatever", "whatever"]'],
'anotherField1': ['[MinKey, MaxKey]'],
'anotherField2': ['[MinKey, MaxKey]']},
'indexName': 'app_id_1_anotherField2_1_anotherField1_1',
'indexVersion': 2,
'isMultiKey': False,
'isPartial': False,
'isSparse': False,
'isUnique': False,
'keyPattern': {'app_id': 1, 'anotherField1': 1, 'anotherField2': 1},
'multiKeyPaths': {'app_id': [], 'anotherField1': [], 'anotherField2': []},
'stage': 'IXSCAN'},
'stage': 'FETCH'},
'stage': 'SHARDING_FILTER'
}
- mongodb がインデックスを正しく使用しない理由について何か考えはありますか?
- アンインストール済みが一部のドキュメントに存在しない可能性があるためですか?
- 複合日付クエリを実行するときのインデックスの方向に関するいくつかの説明も大歓迎です。おそらくその理由はインデックスの方向ですか?
(1, -1, -1, 1)
ありがとう!:)
------------編集--------------
説明の完全な結果は少し長いので、ここに貼り付けました。これは、queryPlanner のインデックス (Index2) の選択を説明しています。
また、shard_key については、ここでクエリされているものとはまったく異なります。そのため、このクエリ専用の別の特定のインデックスを定義しています。(シャード キーは、(app_id、android_id、some_other_field_not_in_query) の複合インデックスです。