2015-12-18
■[MongoDB] DEXでもうMongoDB職人は要らなくなるの巻
※このエントリは個人の見解であり、所属する組織の公式見解ではありません
このエントリは、MongoDB Advent Calendar 2015 18日目のエントリです。
どうもどうも乙カレー様です。桑野です。
MongoDB on AWS的ななにかを書こうとしたのですが、その前にこれ紹介したことなかったなーと思いDEXの紹介しようと思います。
DEXとは
あのMongoDBならこの人達のMongolabさんの作った、MongoDBのSlowlogなどから適切なINDEX設定をRecommendしてくれるプロダクトになります。
神様仏様Mongolab様。
インストール
pipで簡単。
$ pip install dex
コマンドライン手順
基本的には、MongoDBのURLと、Logのパスを指定していきましょう。
$ dex -f /var/log/mongodb/mongodb.log mongodb://localhost { 'runStats': { 'linesRecommended': 0, 'linesProcessed': 1, 'linesPassed': 149 }, 'results': [] }
ほいできました。
データベース例
データベースはこの前 作ったデータベースにデータを足して作ってみましょう。
適当に1000万レコードほど追加しました。
$ mongo d1 (snip) > db.t1.find().count() 10110005
そこでもう一回DEXを実行してみますが、まだクエリを実行していないので当然何も出ません。
$ dex -f /var/log/mongodb/mongodb.log mongodb://localhost { 'runStats': { 'linesRecommended': 0, 'linesProcessed': 1, 'linesPassed': 149 }, 'results': [] }
では、Indexのきいていないクエリを打ってみましょう
どれも10秒以上かかります。重い。
$ mongo d1 # クエリ1つ目 > db.t1.find( { "nosql" : "mongodb"} ) { "_id" : ObjectId("567365cf760961552b000001"), "created_at" : ISODate("2015-12-16T13:04:10Z"), "name" : "kuwa_tw", "nosql" : "mongodb", "price" : 10 } # クエリ2つ目 > db.t1.find( { "name" : "saeoshi"} ) { "_id" : ObjectId("567365cf760961552b000005"), "created_at" : ISODate("2015-12-16T13:04:38Z"), "name" : "saeoshi", "nosql" : "voldemote", "price" : 2000 } # クエリ3つ目 > db.t1.find( { "price" : { $gt: 9000 } } ) (いっぱい出たのでsnip) > # クエリ4つ目:複合条件 > db.t1.find( { "name" : "kakerukaeru" , "price" : { $gt: 10 } } ) >
そうしたらそこでもう一回DEXを実行してみましょう。
そうすると、重かったクエリをピックアップして、どのようなインデックスをはればいいのかRecommendしてくれます。これは素晴らし。
$ dex -f /var/log/mongodb/mongodb.log mongodb://localhost { 'runStats': { 'linesRecommended': 7, 'linesProcessed': 7, 'linesPassed': 201 }, 'results': [ { 'queryMask': '{"$query":{"name":"<val>","price":{"$gt":"<val>"}}}', 'namespace': 'd1.t1', 'recommendation': { 'index': '{"name": 1, "price": 1}', 'namespace': 'd1.t1', 'shellCommand': 'db["t1"].ensureIndex({"name": 1, "price": 1}, {"background": true})' }, 'details': { 'count': 3, 'totalTimeMillis': 52289, 'avgTimeMillis': 17429 } }, { 'queryMask': '{"$query":{"name":"<val>"}}', 'namespace': 'd1.t1', 'recommendation': { 'index': '{"name": 1}', 'namespace': 'd1.t1', 'shellCommand': 'db["t1"].ensureIndex({"name": 1}, {"background": true})' }, 'details': { 'count': 2, 'totalTimeMillis': 35758, 'avgTimeMillis': 17879 } }, { 'queryMask': '{"$query":{"nosql":"<val>"}}', 'namespace': 'd1.t1', 'recommendation': { 'index': '{"nosql": 1}', 'namespace': 'd1.t1', 'shellCommand': 'db["t1"].ensureIndex({"nosql": 1}, {"background": true})' }, 'details': { 'count': 1, 'totalTimeMillis': 17983, 'avgTimeMillis': 17983 } }, { 'queryMask': '{"$query":{"price":{"$gt":"<val>"}}}', 'namespace': 'd1.t1', 'recommendation': { 'index': '{"price": 1}', 'namespace': 'd1.t1', 'shellCommand': 'db["t1"].ensureIndex({"price": 1}, {"background": true})' }, 'details': { 'count': 1, 'totalTimeMillis': 17488, 'avgTimeMillis': 17488 } } ] }
ついでにexplainもしてみましょう。
> db.t1.find( { "name" : "kakerukaeru" , "price" : { $gt: 10 } } ).explain() { "cursor" : "BasicCursor", "isMultiKey" : false, "n" : 0, "nscannedObjects" : 10110005, "nscanned" : 10110005, "nscannedObjectsAllPlans" : 10110005, "nscannedAllPlans" : 10110005, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 18, "nChunkSkips" : 0, "millis" : 17893, "indexBounds" : { }, "server" : "ip-172-31-20-34:27017" }
当たり前ながらIndexが無いので当然BasicCursorです。
ではRecommendにしたがってIndexを貼ってみましょう
> db["t1"].ensureIndex({"name": 1, "price": 1}, {"background": true}) > db["t1"].getIndexes() [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "d1.t1", "name" : "_id_" }, { "v" : 1, "key" : { "name" : 1, "price" : 1 }, "ns" : "d1.t1", "name" : "name_1_price_1", "background" : true } ]
はいIndexれましたね。
ではもう一回explainしてみましょ。
爆速!!!
> db.t1.find( { "name" : "kakerukaeru" , "price" : { $gt: 10 } } ).explain() { "cursor" : "BtreeCursor name_1_price_1", "isMultiKey" : false, "n" : 0, "nscannedObjects" : 0, "nscanned" : 0, "nscannedObjectsAllPlans" : 0, "nscannedAllPlans" : 0, "scanAndOrder" : false, "indexOnly" : false, "nYields" : 0, "nChunkSkips" : 0, "millis" : 0, "indexBounds" : { "name" : [ [ "kakerukaeru", "kakerukaeru" ] ], "price" : [ [ 10, 1.7976931348623157e+308 ] ] }, "server" : "ip-172-31-20-34:27017" }
BtreeCursorになってIndex[name_1_price_1]が使われているのがわかると思います。
というわけで、Indexはメモリを食うので一概に貼りまくったらええというわけではないんですが、定期的にDEXを流すことでIndexが使われていないクエリが流れていないかを確認することができます。
簡単に使えるので使っていくと幸せになりますよ。
MongoDB使ってる時点で幸せなのかよくわかりませんがね。。。(怪談的な終わり方)
[asin:1617291609:detail]
[asin:B004I8W4XQ:detail]
- 5 https://t.co/tQQpvJpxqS
- 3 http://htn.to/Kd7RSZ
- 3 https://t.co/cnqQCJVf5u
- 2 http://htn.to/G5UsEuqM
- 2 http://htn.to/fGutcvmU
- 1 http://m.facebook.com
- 1 http://qiita.com/eigo_s
- 1 http://www.google.co.in/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CDEQFjAD&url=http://goo.gl/nbYB7C&ei=2yR0VpqkGbXcWsh2&usg=AFQjCNH_qci9PzcSATB7kwQ871COqWhhOQ
- 1 http://www.google.co.in/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&ved=0CFAQFjAD&url=http://goo.gl/L2T4hJ&ei=6SR0VrqSLbqgjAaCvAE&usg=AFQjCNHwsxL8pz5kp5a6-rWdCjuPQFPjzw
- 1 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=3&ved=0CFQQFjAA&url=http://d.hatena.ne.jp/akuwano/20151218/1450451791&ei=QyV0VqaDI5eItAG3oQE&usg=AFQjCNGDIhMShxrIKSKh4NUlT90Jili6kA