ExplainでSQLのボトルネックを調査する
→select文の前に「explain」をつけて実行してみる
explain select * from tableName;
実行結果の読み方
select_type
サブクエリの場合
| 値 | 説明 |
|---|---|
| PRIMARY | 外部のクエリ |
| SUBQUERY | 相関関係のないサブクエリ |
| DEPENDENT SUBQUERY | 相関関係のあるサブクエリ |
| UNCACHEABLE SUBQUERY | 実行のたびに結果が変わるかもしれないサブクエリ |
| DERIVERD | from句で使われるサブクエリ |
外部クエリ→サブクエリの順序で実行されるが、DERIVERDのときだけはサブクエリ→外部クエリと実行順所が逆になる。
UNIONの場合
| 値 | 説明 |
|---|---|
| PRIMARY | unionのときに最初にフェッチされるテーブル |
| UNION | 2番目以降にフェッチされるテーブル |
| UNION RESULT | UNIONの実行結果 |
| DEPENDENT UNION | DEPENDENT SUBQUERYがUNIONになっている場合 |
| UNCACHEABLE UNION | UNCACHEABLE SUBQUERYがUNIONになっている場合 |
UNOINは先頭から順に処理されていくので、処理順序がわかりやすい・・・はず。
type
対象のテーブルに対してのアクセス方法。
致命的なものは見れば一発でわかる・・・らしい。
| 値 | 説明 |
|---|---|
| ALL | フルスキャンなので一番重い。改善必須 |
| index | フルインデックススキャン。インデックスをALL検索するのでこれまた重い。改善必須 |
| ref | インデックスを使った検索 |
| range | インデックスを使った範囲検索。 |
| eq_ref | joinのときにPKやユニークキーを使う場合。joinのときはこの値になれば最速。 |
| const | join以外でPKやユニークキーを使う場合。最速。 |
key
テーブルにアクセスするために使ったindexを示す。
ちなみに、possible_keysは「使える候補」で、実際につかったかどうかはkeyの値を見る。
key_len
選択されたキーの長さ。
インデックスのキー長が短いほうが高速になるので、迷ったら長さの短いほうにインデックスをつけるといい。
rows
selectの取得件数の見積もり。
ざっくりとしたものなので、where句次第ではもっと少ない件数が返ってくることもある。
extra
ほかに使用している条件などがあれば、ここに出力される。
チューニング方法の例
type=null かつ key=null の場合
→インデックスが張られてない&テーブルのフルスキャンが行われている。
where条件で使うカラムにインデックスを張る。
もしくは、インデックスが張られたカラムをwhere条件の中に追加する。
extra=Using filesort もしくは extra=temporary
→order byに指定しているカラムにインデックスを張る。
Javaだと、Listで取得してCollection.sort()もできるので、Java側にソート処理を入れるものアリかもしれない。
「extra=temporary」はjoinしているときに出現するので、joinしているテーブルのどこかにインデックスを張ればよい。
インデックスを張ってあるはずなのにtype=all
→複合インデックスが使われてない。
複合インデックスが張ってあるカラムを、すべてwhere条件に使用する。
インデックスと同じ順序でwhere条件を組み立てること!
possible_keysに出力があるのにkey=nullで使われてない
→MySQLの仕様が原因かもしれないので、SQLそのものを作り変える。
例)インデックスが張ってあるカラムをwhere条件に使用しているのに、テーブル検索が実施されている
MySQLが「インデックス検索よりもテーブル検索が早い」と判断してしまうことがあるようです。
SQLのつくりを変えるなどの、大きな変更が必要になる。
(仕様や設計を見直すことも視野に入れた方がよい)
その他やってみるといいかもしれない方法
joinのときにインデックスを使うような記述を追加する
SELECT * FROM tableA aa LEFT JOIN tableB bb USE INDEX(index名)
SELECT * FROM tableA aa LEFT JOIN tableB bb FORCE INDEX(index名) /* USEが効かない場合はこちら */