256bitの殺人メニュー このページをアンテナに追加 RSSフィード Twitter

2015-12-18

[] DEXでもうMongoDB職人は要らなくなるの巻

※このエントリは個人の見解であり、所属する組織の公式見解ではありません

このエントリは、MongoDB Advent Calendar 2015 18日目のエントリです。

どうもどうも乙カレー様です。桑野です。

MongoDB on AWS的ななにかを書こうとしたのですが、その前にこれ紹介したことなかったなーと思いDEXの紹介しようと思います。

DEXとは

あのMongoDBならこの人達のMongolabさんの作った、MongoDBのSlowlogなどから適切なINDEX設定をRecommendしてくれるプロダクトになります。

神様仏様Mongolab様。

インストール

pipで簡単。

$ pip install dex

コマンドライン手順

基本的には、MongoDBURLと、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]

投稿したコメントは管理者が承認するまで公開されません。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/akuwano/20151218/1450451791