MySQL のチューニング (ボトルネックの検出)

こんにちは! onk です。

SAPさんが各社とも「ソーシャルアプリは負荷対策が大事」って言っていますね。 弊社でも mixi アプリ(PC),mixi アプリモバイルをリリースしたときはお祭り状態だったので, ふりかえりも兼ねて MySQL のボトルネックを調べる方法を書いてみました。 (幸い,モバゲーオープンゲームのリリース時はこれらの経験が役に立ったので何ともなかったです)

といっても 9 割方

辺りなんですけどねー。

基本は下から

まず,ボトルネックを調べるときは下の層から上がっていくのが基本です。たぶん。

なので ssh でサーバに入って (LoadAverage 300 ぐらいまでならなんとか入れますね) 以下のコマンドをよく叩きます。

top

現在稼動しているプロセスの一覧と,システム情報の概要が表示されるプログラム。

見るのは主に

と,これらが異常なプロセス。

たとえば swap だと,正常なときは

Swap:  7879872k total,      764k used,  7879108k free,  3589852k cached

ですが,異常に減っているときは

Swap:  7879872k total,  2529080k used,  5350792k free,  1574320k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
26945 mysql     20   0 7681m 6.0g 5080 S   37 77.2  17214:10 /usr/local/mysql/libexec/mysqld

のようになります。明らかに mysql がメモリ食い潰してますね。 freeps でも確認出来ますが,どのプロセスが悪いのか見渡しやすいので,僕は top が好きです。

vmstat

これもシステム情報を表示するプログラムです。

あたりを確認します。

たとえば CPU IO wait が異常なときは

procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 0 11  20092 281480     12 7671592    0    0 27820    42 1967 2557  3  3 41 52
 0 12  20092 284492     12 7668020    0    0 26512     1 1812 2222  2  3 47 48
 0 13  20092 281188     12 7671080    0    0 33144     0 1890 2258  2  2 40 56
 0 13  20092 277208     12 7674300    0    0 24612     0 1722 2000  2  1 38 59
 0 24  20092 283364     12 7665688    0    0 24304     0 1871 2287  2  2 30 66
 2 21  20092 284912     12 7661960    0    0 31688    30 1714 2130  3  3 35 60

となります。

大抵の場合,以上の 2 コマンドで原因となっているプロセスを特定できるので,新機能のリリース時は 2 画面開いて

として見張っています。

ありがちな例

CPU Usage が高い

大概,インデックスの張り忘れです。インデックスさえ張っておけば一瞬で終わるはずの WHERE 句や ORDER 句を,必死に CPU が計算しています。

最初はデータ量が少なくて気づかないかもしれないけど,次第に CPU 使用率が 100% に限りなく近づいて……。

varchar カラムに対して int を検索条件にする場合は INDEX が使われないので注意してください。 MySQL は文字列の型に対して where hoge = 1 と絞り込むとき,'01' や '1.00' の行もマッチします。つまり INDEX は使えず,全件走査となります。

ソーシャルアプリでは opensocial_owner_id が文字列なのでコレやりがちなんですよね。さすがにすぐ気づくとは思いますが(笑)

CPU IO Wait が高い

データ量に対して,メモリの割り当てが少ない場合とか。

show table status;

したときの Data_length + Index_lengthkey_buffer_sizeinnodb_buffer_pool_size を見直してみてください。 せめて Index だけでもメモリに載せてあげたいので。

ディスクの読み書きはメモリの読み書きの 10 万倍遅いです。ディスクアクセスは 1 回につき 5ms 程度かかるので,秒間 200 回しかアクセスできません。

1 回のクエリで 4 回ディスクへのアクセスが発生し,1 ページ表示するのに 5 回クエリを投げるとすると秒間 10 PV しか耐えられないことになります。 1 ユーザあたり 150 PV/day,ピーク時に平均の 2 倍になる場合で 2,500 UU/day が限界な感じですね。これが全てメモリに載っていれば 5 億人までいけます。

実行中のクエリを確認

MySQL の設定ではなくクエリが原因だと判断したら,実行中のクエリを確認します。

の 3 パターンかな。

innotop

InnoDB を使ってるときはぜひ。 innotop - Project Hosting on Google Code

僕が注意しているのは

ですね。

トランザクションが重い場合,Query List には「COMMIT;」しか表示されなかったりするので注意してください。

show processlist

innotopmytop で実行中のクエリのリストが見えるんですが,これらを入れていない場合は show processlist; で確認します。

クエリを全て Info カラムに出力したいときは

show full processlist;

mprofile

Kazuho@Cybozu Labs: MySQL のボトルネックを統計的に監視・解析する方法

show processlist を 1,000 回実行し,どのクエリが何回表示されたかの統計を取ってくれます。何から潰すべきなのかの判断に迷ったらとりあえず統計取りましょう。

奥さんも書いてますが,定期的に実行してログを残しておくと,ユーザの動向と DB の使われ方の変遷が見えてきてサーバ増強計画を作りやすいですね。

……思ってたより長くなったので次回 EXPLAIN 編に続きます。乞うご期待!