👀 MongoDB 3.x インデックス生成前後のexplain()結果を読む
はじめに
MongoDB 3.0 から explain() の出力結果が変わり、読み解くのに時間がかかってしまいました。 今回はインデックスの生成前後で explain() の結果がどう変わるかを確認してみます。
環境
- Mac OS X 10.10.3
- MongoDB 3.0.2
- MongoDB storage engine: mmapv1 (default)
サンプルデータの準備
DBを用意
$ mongo use sample_db switched to db sample_db
サンプルデータ追加
とりあえず10万件のドキュメントを生成しておきます。
> for (var i=0; i < 100000; i++) { ... db.items.insert({ name: 'item_' + i, price: 100 + i }) ... } > db.items.count() 100000
インデックスが無い状態
itemsコレクションから119円の商品を探すクエリを実行します。
> db.items.find({price: 119}).explain("executionStats") { "queryPlanner": { "plannerVersion": 1, "namespace": "sample_db.items", "indexFilterSet": false, "parsedQuery": { "price": { "$eq": 119 } }, "winningPlan": { "stage": "COLLSCAN", "filter": { "price": { "$eq": 119 } }, "direction": "forward" }, "rejectedPlans": [] }, "executionStats": { "executionSuccess": true, "nReturned": 1, "executionTimeMillis": 59, "totalKeysExamined": 0, "totalDocsExamined": 100000, "executionStages": { "stage": "COLLSCAN", "filter": { "price": { "$eq": 119 } }, "nReturned": 1, "executionTimeMillisEstimate": 20, "works": 100002, "advanced": 1, "needTime": 100000, "needFetch": 0, "saveState": 781, "restoreState": 781, "isEOF": 1, "invalidates": 0, "direction": "forward", "docsExamined": 100000 } }, "serverInfo": { "host": "MacBook-Pro.local", "port": 27017, "version": "3.0.2", "gitVersion": "nogitversion" }, "ok": 1 }
まずは queryPlannner.winningPlan
に着目します。"stage": "COLLSCAN"
となっています。
これは MongoDB 2.x の時の BasicCursor
に相当します。
インデックスを使わずに全走査してることがわかります。
※ COLLSCAN => COLLECTION SCAN
実際に走査対象となったドキュメント数やクエリにかかった時間は
queryPlannner.executionStats
を確認します。
key | description | value |
---|---|---|
"nReturned": 1 | 見つかったドキュメント数 | 1 |
"executionTimeMillis": 59 | 実行時間 | 59 msec |
"totalKeysExamined": 0 | 検索したインデックス数 | 0 |
"totalDocsExamined": 100000 | 検索したドキュメント数 | 100000 |
totalDocsExamined
からも全ドキュメントが検索対象だったことがわかりますね
※ インデックス生成してないので当たり前ですが
インデックス生成後
それではインデックスを生成してみます。
> db.items.createIndex({ price: 1 }) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } > db.items.getIndexes() [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "sample_db.items" }, { "v" : 1, "key" : { "price" : 1 }, "name" : "price_1", "ns" : "sample_db.items" } ]
price_1
という名前の priceフィールド昇順 のインデックスができました。
追記
db.collection.ensureIndex() は MongoDB 3.0.0 で deprecated と なりましたので db.collection.createIndex() に書き直しました。 参考: db.collection.ensureIndex()
再度、items コレクションから119円の商品を探すクエリを実行します。
> db.items.find({price: 119}).explain("executionStats") { "queryPlanner": { "plannerVersion": 1, "namespace": "sample_db.items", "indexFilterSet": false, "parsedQuery": { "price": { "$eq": 119 } }, "winningPlan": { "stage": "FETCH", "inputStage": { "stage": "IXSCAN", "keyPattern": { "price": 1 }, "indexName": "price_1", "isMultiKey": false, "direction": "forward", "indexBounds": { "price": [ "[119.0, 119.0]" ] } } }, "rejectedPlans": [] }, "executionStats": { "executionSuccess": true, "nReturned": 1, "executionTimeMillis": 13, "totalKeysExamined": 1, "totalDocsExamined": 1, "executionStages": { "stage": "FETCH", "nReturned": 1, "executionTimeMillisEstimate": 0, "works": 2, "advanced": 1, "needTime": 0, "needFetch": 0, "saveState": 0, "restoreState": 0, "isEOF": 1, "invalidates": 0, "docsExamined": 1, "alreadyHasObj": 0, "inputStage": { "stage": "IXSCAN", "nReturned": 1, "executionTimeMillisEstimate": 0, "works": 2, "advanced": 1, "needTime": 0, "needFetch": 0, "saveState": 0, "restoreState": 0, "isEOF": 1, "invalidates": 0, "keyPattern": { "price": 1 }, "indexName": "price_1", "isMultiKey": false, "direction": "forward", "indexBounds": { "price": [ "[119.0, 119.0]" ] }, "keysExamined": 1, "dupsTested": 0, "dupsDropped": 0, "seenInvalidated": 0, "matchTested": 0 } } }, "serverInfo": { "host": "MacBook-Pro.local", "port": 27017, "version": "3.0.2", "gitVersion": "nogitversion" }, "ok": 1 }
queryPlannner.winningPlan
を確認します。 "stage": "IXSCAN"
に変わりました。
これは MongoDB 2.x の時の BtreeCursor
に相当します。
INDEXを使用していることがわかります :)
※ IXSCAN => INDEX SCAN
ここでまた、 queryPlannner.executionStats
を確認します。
key | description | value |
---|---|---|
"nReturned": 1 | 見つかったドキュメント数 | 1 |
"executionTimeMillis": 13 | 実行時間 | 13 msec |
"totalKeysExamined": 1 | 検索したインデックス数 | 1 |
"totalDocsExamined": 1 | 検索したドキュメント数 | 1 |
おわりに
最低限見ておきたい項目について、インデックスの生成前後での差分を見てみました。 実際の運用においては1コレクションに1インデックスという事は少なく、複数のインデックスや 複合キーインデックスが生成されていると思います。
そうすると今回見ていった項目以外にも rejectedPlans
で採用されなかったインデックスを確認したり
ソート指定した場合のインデックスの使われ方を確認したりといった事が必要となるでしょう。
それぞれの項目の説明についてはまた別途まとめましょうか。