34
MongoDBチューニング ~ スロークエリ撲滅までの道筋~ id:matsuou1

20110514 mongo dbチューニング

Embed Size (px)

DESCRIPTION

第3回MongoDB勉強会の発表資料です。

Citation preview

Page 1: 20110514 mongo dbチューニング

MongoDBチューニング ~ スロークエリ撲滅までの道筋~

id:matsuou1

Page 2: 20110514 mongo dbチューニング

アジェンダ !   自己紹介

!   目標

!   オプティマイザの概要

!   実行計画について

!   スロークエリの特定

!   クエリチューニングTips

!   まとめ

Page 3: 20110514 mongo dbチューニング

自己紹介

!   ID:matsuou1

!   名前:松尾 裕一(まつお ゆういち)

!   職業:音楽配信サービスの会社のエンジニア

!   システムアーキテクト? !   プロジェクトマネージャー? !   元はOracleやPostgreSQLでチューニング等を。

!   興味があること:MongoDB、Hadoop、R、統計解析、機械学習、データマイニング

Page 4: 20110514 mongo dbチューニング

目標 1.  オプティマイザの概要を理解する。

2.  実行計画を取得し、読めるようになる。

3.  遅いクエリを特定できる。

4.  簡単なクエリのチューニングができる。

Page 5: 20110514 mongo dbチューニング

1.オプティマイザの概要

Page 6: 20110514 mongo dbチューニング

オプティマイザとは? !   オプティマイザとは、「クエリの最適化を行う機能」のこと。オプティマイザが実行計画を立てる。

!   RDBMSでは、大別してルールベースとコストベースのオプティマイザがあり、コストベースが主流。

!   コストベースオプティマイザは、事前に取得された統計情報を使用し、最適な実行計画を決定する。

Page 7: 20110514 mongo dbチューニング

従来のオプティマイザの問題点 !   コンスタントに悪い実行計画を選択する可能性がある。

!   本番環境と検証環境では統計情報が異なるため、実行計画が異なる可能性がある。

Page 8: 20110514 mongo dbチューニング

MongoDBのオプティマイザ !   コストベースは使用しない。

!   統計情報は使用しない。(割とCPUコストも高い)

!   新しいクエリを実行する場合は、複数の実行計画を並行に実施し、一つが終わったら他のクエリを停止させる。その際、早い実行計画を学習する。 !   JOINがないからこそ、可能なやり方。

!   オーバーヘッドがあるが、最悪のケースは回避可能

Page 9: 20110514 mongo dbチューニング

実行計画の有効期限 •  一度評価された実行計画は、以下のイベントが発生するまで、使用される。 •  100回書き込まれた場合(insert、update、delete) •  インデックスが追加、削除された場合 •  最初の評価時と比較し、nscannedの値が10倍を超えた場合

•  再評価された後も、同じ実行計画が使われる可能性ももちろんある。

Page 10: 20110514 mongo dbチューニング

オプティマイザ詳細 db/queryoptimizer.cpp がオプティマイザのソースなので、これ以上の詳細は、ソースコードリーディング分科会の方々が明らかにしてくれるはず!! と期待しています。

Page 11: 20110514 mongo dbチューニング

2.実行計画

Page 12: 20110514 mongo dbチューニング

実行計画の取得方法 !   実行計画の取得には、explainコマンドを使用する。

> db.logs.find({uid:"a10Sty5jqqiyxxxx2"}).explain(); {

"cursor" : "BasicCursor", "nscanned" : 2669511, "nscannedObjects" : 2669511, "n" : 3, "millis" : 6100, "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : { }

}

Page 13: 20110514 mongo dbチューニング

実行計画の詳細 項目 例 説明

cursor BasicCursor カーソルタイプ (BasicCursor or BtreeCursor)

nscanned 14 スキャンされたインデックスまたはオブジェクトの数

nscannedObjects 14 スキャンされたオブジェクトの数

n 14 処理結果数

millis 9 処理にかかった時間(ms)

nYields 0

nChunkSkips 0 Chunkマイグレーション中にスキップしたドキュメント数

isMultiKey false

indexOnly false

indexBounds

scanAndOrder

allPlans

Page 14: 20110514 mongo dbチューニング

実行計画例(インデックス未使用) > db.logs.find({uid:"a10Sty5jqqiyxxxx2"}).explain(); {

"cursor" : "BasicCursor", "nscanned" : 2669511, "nscannedObjects" : 2669511, "n" : 3, "millis" : 6100, "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : { }

}

・インデックスが使えていない ・クエリの実行に6100ms ・3件の結果を取得するのに266万オブジェクトスキャン

Page 15: 20110514 mongo dbチューニング

実行計画例(インデックス使用) > db.logs.find({uid:"b3iVxMGIEc3xxxxY"}).explain(); { "cursor" : "BtreeCursor uid_1", "nscanned" : 3, "nscannedObjects" : 3, "n" : 3, "millis" : 6, "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : { "uid" : [ [ "b3iVxMGIEc3xxxxY", "b3iVxMGIEc3xxxxY" ] ] } }

・”uid_1”のインデックスを使用 ・クエリの実行に6ms ・3件の結果を取得するのに3オブジェクトのスキャン

Page 16: 20110514 mongo dbチューニング

3.スロークエリの特定

Page 17: 20110514 mongo dbチューニング

スロークエリを特定するには? !   大きく分けて、以下の3つの方法がある。

! slowmsオプションをつけて起動する。

!   プロファイルレベルを設定する。 ! db.setProfilingLevel(level,slowms)

!   動作中のクエリを確認する。

Page 18: 20110514 mongo dbチューニング

slowmsオプション !   mongod起動時にslowmsオプションを指定することで、指定したms以上の遅いクエリをファイルに出力することができる。

Sat May 14 02:25:53 [conn16] query logdb.$cmd ntoreturn:1 command: { count: "logs", query: { path: "/index.html" }, fields: {} } reslen:64 3116ms

mongod --logpath=/var/log/mongodb/mongodb.log --logappend –slowms 500

mongod起動

ログファイル

Page 19: 20110514 mongo dbチューニング

setProfilingLevel プロファイリングを有効にするには、setProfilingLevel

(level,slowms)を設定します。

> db.setProfilingLevel(1,500); { "was" : 1, "slowms" : 100, "ok" : 1 }

レベル 対象クエリ

0 対象なし

1 指定時間より遅いクエリのみ(デフォルト100ms)

2 全てのクエリ

Page 20: 20110514 mongo dbチューニング

setProfilingLevel 結果はsystem.profileコレクションにて確認ができます。

> db.system.profile.find(); { "ts" : ISODate("2011-05-01T03:40:04.462Z"), "info" : "query logdb.logs reslen:192 nscanned:2669511 \nquery: { uid: \"b2kwyYCXn3b5Scpr\" } nreturned:1 6686ms", "millis" : 6686 }

項目 説明

ts タイムスタンプ

millis 実行にかかった時間

info

query

query query詳細

nscanned スキャンしたオブジェクト数

reslen クエリの結果のバイト数

nreturned クエリの結果のオブジェクト数

Page 21: 20110514 mongo dbチューニング

動作中のクエリの参照

項目 説明

opid オペレーションID(KillOpで指定する)

op 操作の種類(query、update等)

ns 実行されているネームスペース(データベース+コレクション)

query クエリの中身

lockType ロックタイプ

waitingForLock ロック待ち

client クライアント情報

desc コネクションタイプ

db.currentOpで現在動作中のクエリの参照が可能

Page 22: 20110514 mongo dbチューニング

動作中のクエリの参照 > db.currentOp(); {

"inprog" : [ { "opid" : 272, "active" : true, "lockType" : "read", "waitingForLock" : false, "secs_running" : 20, "op" : "query", "ns" : "logdb.logs", "query" : { "count" : "logs", "query" : { "month" : "2010/02" }, "fields" : { } }, "client" : "127.0.0.1:59394", "desc" : "conn" }]}

Page 23: 20110514 mongo dbチューニング

スロークエリ特定方法まとめ 方法 メリット デメリット

slowms ・ファイルに出力されるため、後から確認が可能。

・起動時に指定する必要がある。 ・適切な指定を行わないと、ログファイルサイズが大きくなる&パフォーマンスに影響がある。

db.setProfilingLevel ・collectionに出力されるため、扱いが簡単。 ・簡単にレベルや閾値を変更可能

・system.profileは、capped collectionなので、古い情報は削除される。 ・適切な指定を行わないと、パフォーマンスに影響がある。

db.currentOp ・クエリの実行完了まで待つ必要がない。 ・現在実行されているクエリの情報を取得できる。

・実行完了後の特定は不可。

※ これらはスロークエリの特定には有用だが、少しだけ遅くて、実行回数が多いクエリの特定には向かない。 (MongoDBにはその手の問題を特定できる術がまだない?)

Page 24: 20110514 mongo dbチューニング

4.チューニングTips

Page 25: 20110514 mongo dbチューニング

複合インデックス !   複合インデックスのキーの順序は重要です。

• db.c.find( {x:10,y:20} ), using index {x:1,y:1} • db.c.find( {x:10,y:20} ), using index {x:1,y:-1} • db.c.find( {x:{$in:[10,20]},y:20} ), using index {x:1,y:1} • db.c.find().sort( {x:1,y:1} ), using index {x:1,y:1} • db.c.find().sort( {x:-1,y:1} ), using index {x:1,y:-1} • db.c.find( {x:10} ).sort( {y:1} ), using index {x: 1,y:1}

Page 26: 20110514 mongo dbチューニング

hint !   hintを使うことで、特定のインデックスを使わせることができる。

!   複数のインデックスが張られているコレクションに対し、複数のフィールドを条件にクエリを実行する場合に使用する。

Page 27: 20110514 mongo dbチューニング

hint > db.logs.find({uid:"f2RNwtj34m3xxxxB" , path : "/index.html"}).hint({uid:1}).explain(); {

"cursor" : "BtreeCursor uid_1", "nscanned" : 95, "nscannedObjects" : 95, "n" : 35, "millis" : 3, "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : { "uid" : [ [ "f2RNwtj34m3xxxxB", "f2RNwtj34m3xxxxB" ] ] }

}

Page 28: 20110514 mongo dbチューニング

hint > db.logs.find({uid:"f2RNwtj34m3xxxxB" , path : "/index.html"}).hint({uid:1 , path:1}).explain(); {

"cursor" : "BtreeCursor uid_1_path_1", "nscanned" : 35, "nscannedObjects" : 35, "n" : 35, "millis" : 0, "nYields" : 0, "nChunkSkips" : 0, "isMultiKey" : false, "indexOnly" : false, "indexBounds" : { "uid" : [ [ "f2RNwtj34m3xxxxB", "f2RNwtj34m3xxxxB" ] ],

(略) }

}

Page 29: 20110514 mongo dbチューニング

mongostat mongostatで統計情報を確認することができます。

[matsuou1@testsvr ̃]$ mongostat connected to: 127.0.0.1 insert query update delete getmore command flushes mapped vsize res faults locked % idx miss % qr¦qw ar¦aw netIn netOut conn repl time 0 0 0 0 0 1 0 2.16g 2.39g 397m 0 0 0 0¦0 0¦0 62b 1k 2 M 03:16:44 0 0 0 0 0 1 0 2.16g 2.39g 397m 0 0 0 0¦0 0¦0 62b 1k 2 M 03:16:45 0 0 0 0 0 1 0 2.16g 2.39g 397m 0 0 0 0¦0 0¦0 62b 1k 2 M 03:16:46 0 0 0 0 0 1 0 2.16g 2.39g 397m 0 0 0 0¦0 0¦0 62b 1k 2 M 03:16:47 0 1 0 0 0 1 0 2.16g 2.39g 396m 203 0 0 0¦0 1¦0 62b 1k 2 M 03:16:48 0 0 0 0 0 1 0 2.16g 2.39g 396m 323 0 0 0¦0 1¦0 62b 1k 2 M 03:16:49 0 0 0 0 0 1 0 2.16g 2.39g 397m 252 0 0 0¦1 1¦0 62b 1k 2 M 03:16:50 0 0 0 0 0 1 0 2.16g 2.39g 397m 232 0 0 0¦0 1¦0 62b 1k 2 M 03:16:51 0 0 0 0 0 1 0 2.16g 2.39g 399m 320 0 0 0¦0 1¦0 62b 1k 2 M 03:16:52 0 0 0 0 0 1 0 2.16g 2.46g 405m 261 0 0 0¦0 1¦0 62b 1k 2 M 03:16:53

Page 30: 20110514 mongo dbチューニング

mongostat詳細 項目 説明

inserts insert数/秒

query query数/秒

update update数/秒

delete delete数/秒

getmore getmore数/秒

command command数/秒

flushes fsync flush数/秒

mapped Mapされだデータ量(MB)

visze 仮想プロセスのサイズ(MB)

res プロセスのサイズ(MB)

faults ページフォルト回数/秒

locked 書き込みロック待ちのパーセンテージ

Idx miss インデックスが使用されなかったパーセンテージ(サンプル値)

qr|qw 待ちクライアント数(read | write)

ar|aw アクティブクライアント(read | write)

netIn Inネットワークトラフィック(bits)

netOut Outネットワークトラフィック(bits)

conn コネクション数

Page 31: 20110514 mongo dbチューニング

collectionのインデックスの確認 !   getIndexKeys()で、インデックスを持った全てのフィールドを取得できます。

> db.logs.getIndexKeys(); [ { "_id" : 1 }, { "uid" : 1 }, { "uid" : 1, "path" : 1 } ]

Page 32: 20110514 mongo dbチューニング

動作中のクエリの停止 !   db.currentOpでopidを特定し、db.killOp(opid) で処理中のクエリを停止できる。

> db.currentOp(); {

"inprog" : [ { "opid" : 308, "active" : true, "lockType" : "read", "waitingForLock" : false, "secs_running" : 11, "op" : "query", "ns" : "logdb.logs",

(略) } ]

} > db.killOp(308); { "info" : "attempting to kill op" }

Page 33: 20110514 mongo dbチューニング

まとめ !   基本はインデックススキャンさせる

!   クエリを書いたら、実行計画を確認する習慣をつける !   explain

!   スロークエリの特定 !   slowmsオプション

!   setProfilingLevel

!   db.currentOP

!   チューニングTips !   複合インデックス

!   Hint

!   mongostat

Page 34: 20110514 mongo dbチューニング

ご清聴ありがとうございました。