MySQLで、やたら重いクエリが発生したので、原因を究明するまでの話

今後の為に原因の追求方法をメモ

まずは、どのクエリが重いのか調査


SHOW PROCESSLIST


実行中のプロセスが表示される
処理時間がTimeに表示されたり、実行中のコマンドがInfoに表示されるので、重い処理がどれだか判る


ちなみに、KILLコマンドで、処理中のプロセスを殺すことも可能

EXPLAIN SELECT でどんな風に検索されているか確認


今回は、indexがきちんと使われているか、確認の為に使用

+----+-------------+------------+-------+---------------+------------+---------+------+------+-------------+
| id | select_type | table      | type  | possible_keys | key        | key_len | ref  | rows | Extra       |
+----+-------------+------------+-------+---------------+------------+---------+------+------+-------------+
|  1 | SIMPLE      | test_table | index | NULL          | test_index | 4       | NULL |   10 | Using where |
+----+-------------+------------+-------+---------------+------------+---------+------+------+-------------+
  • typeがindexならば、indexが使われている
  • typeがALLならば、フルテーブルスキャン(重い)


MySQL :: MySQL 4.1 リファレンスマニュアル :: 5.2.1 EXPLAIN 構文(SELECT に関する情報の取得)


indexが使われているが、やたら重いのでボトルネックを確認する

SHOW PROFILE でクエリの処理毎の実行時間を見る


やたら重い箇所があったらそこがボトルネックなはず

mysql> SET profiling = 1;
mysql> SELECT ...
mysql> SHOW PROFILE;

+--------------------------------+------------+
| Status                         | Duration   |
+--------------------------------+------------+
| starting                       |   0.000081 | 
| checking query cache for query |   0.000484 | 
| Opening tables                 |   0.000052 | 
| System lock                    |   0.000020 | 
| Table lock                     |   0.000092 | 
| init                           |   0.000283 | 
| optimizing                     |   0.000137 | 
| statistics                     |   0.000052 | 
| preparing                      |   0.000089 | 
| executing                      |   0.000002 | 
| Sorting result                 |   0.000010 | 
| Sending data                   | 115.337027 | 
| end                            |   0.000066 | 
| query end                      |   0.000015 | 
| freeing items                  |   0.000201 | 
| storing result in query cache  |   0.000022 | 
| logging slow query             |   0.000001 | 
| logging slow query             |   0.033929 | 
| cleaning up                    |   0.000034 | 
+--------------------------------+------------+


Sending dataは、読み込みと絞りこみに掛かってる時間

This is quite a misleading status. It should be called "reading and filtering data".


MySQL :: MySQL 5.1 リファレンスマニュアル (オンラインヘルプ) :: 8.5.5.33 SHOW PROFILES 構文


普通に、検索結果をwhereするのに時間が掛かってる様子
この辺でMySQLのファイルが上手くキャッシュメモリに乗っていないのではないか? という疑惑が


vmstatコマンド で、disk ioを調査


disk io 等を調査できるlinuxのコマンド
詳しくはこっち → linuxで空きメモリがどんくらいあるか確認する方法 - かせいさんとこ


$ vmstat -SM 10
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0    361      5      1    267    0    0     0     2  987   85  0  0 100  0  0

〜 検索開始 〜

 0  1    361      5      1    267    0    0   348     0 1024   90  1  2 55 43  0
 0  1    361      5      1    267    0    0   610     3 1128  192  0  3  0 97  0


SELECT文発行直後に、ディスクioが大量に発生して、さらにcpuのio待ち率が97%とかになってる!


調べてみたら、テーブル用のファイルがOSのディスクキャッシュより大きかったために、SELECT文を発行する度にHDDにアクセスしていたのが原因だった
今回は、テスト用の環境なので、データ量を減らす事で解決
ハマりました...

おまけ


MySQLのtable_cacheは、テーブルそのもののキャッシュではなく、テーブルに必要なファイルのハンドラをキャッシュする領域
テーブルそのもののキャッシュと勘違いしてました...