MySQL | SQLite経験者向け、30日で習得するMySQL:パフォーマンスと設計 - Day16 実行計画

SQL MySQL
スポンサーリンク

Day16 前半のゴール

「EXPLAIN を“怖い表”じゃなくて“SQLの行動ログ”として読めるようになる」

ここからいよいよ「パフォーマンスと設計」の世界に入ります。
Day16 の主役は EXPLAIN、つまり「実行計画」です。

一言で言うと、実行計画はこうです。

その SQL を実行するときに、MySQL が「どうやってデータを取りに行くつもりか」を教えてくれるもの

Day16 前半のゴールは、

EXPLAIN を実行して結果の表を出せる
その表の“どこを最初に見ればいいか”が分かる
「これは速そう/これは危なそう」をざっくり判別できる

ここまで行けば、後半でインデックスとの関係を深掘りするときに、かなり楽になります。


実行計画とは何か

「SQL の“作戦会議の結果”をのぞき見る」

SQL を投げるとき、MySQL の中ではこんなことが起きています。

どのテーブルから先に読むか
どのインデックスを使うか
何行くらい読みに行くつもりか

これを決めるのが「オプティマイザ(最適化エンジン)」です。
オプティマイザが決めた「作戦」が、実行計画です。

EXPLAIN は、その作戦を人間が読める形で見せてくれるコマンドです。

EXPLAIN
SELECT *
FROM users
WHERE email = 'foo@example.com';
Python

こう書くと、「この SELECT をどう実行するつもりか」が表形式で返ってきます。

実行計画は、「結果」ではなく「やり方」です。
ここを一度、はっきり分けておきましょう。

結果
→ 実際に返ってくる行データ

実行計画
→ その行データを取るために、MySQL がどう動くか

あなたがパフォーマンスを考えるときに見るべきなのは、後者です。


まずは超シンプルな例で EXPLAIN を叩いてみる

「1テーブル・WHERE 1条件の世界で“形”に慣れる」

次のようなテーブルを用意したとします。

CREATE TABLE users (
  id    INT AUTO_INCREMENT PRIMARY KEY,
  name  VARCHAR(50) NOT NULL,
  email VARCHAR(100) NOT NULL,
  INDEX idx_email (email)
) ENGINE=InnoDB;
Python

そして、こんなクエリを考えます。

SELECT *
FROM users
WHERE email = 'foo@example.com';
Python

これに対して EXPLAIN を実行します。

EXPLAIN
SELECT *
FROM users
WHERE email = 'foo@example.com';
Python

すると、だいたいこんな感じの表が返ってきます(簡略化しています)。

idselect_typetabletypepossible_keyskeyrowsExtra
1SIMPLEusersrefidx_emailidx_email1NULL

最初は「カラム多すぎて無理…」ってなると思います。
なので、前半では“最低限ここだけ見ればいい”というポイントに絞ります。


EXPLAIN で最初に見るべきカラム

「table / type / key / rows の4つに絞る」

いきなり全部を理解しようとしなくて大丈夫です。
まずは、この4つだけに絞りましょう。

table
どのテーブルについての行か

type
どんなアクセス方法で行を取りに行くか(ここが超重要)

key
実際にどのインデックスが使われたか

rows
何行くらい読むつもりか(見積もり)

さっきの結果を、この4つに絞って見直します。

tabletypekeyrows
usersrefidx_email1

これだけなら、だいぶ読みやすくなったはずです。


table カラム

「どのテーブルが“何回”出てくるかを見る」

table カラムは、その行が「どのテーブルに対するアクセスか」を表します。

1テーブルだけのクエリなら、1行だけです。
JOIN が増えると、テーブルの数だけ行が増えます。

例えば、次のようなクエリを EXPLAIN すると、

SELECT *
FROM orders
JOIN users ON orders.user_id = users.id;
Python

結果には ordersusers の2行が出てきます。

前半の段階では、

「このクエリは、何テーブルにアクセスしているのか」
「どのテーブルから先に読んでいるのか(id の順番)」

くらいを意識しておけば十分です。


type カラム(超重要)

「“どれくらい雑に読んでいるか”の指標」

type は、実行計画の中でも特に重要なカラムです。
一言で言うと、

どれくらい効率よく行を取りに行っているか

のざっくりしたランクです。

全部を覚える必要はありませんが、
前半で押さえておきたいのはこの3つです。

ALL
テーブル全体を端から端まで読む(フルスキャン)。基本的に遅い。

ref / eq_ref
インデックスを使って、条件に合う行だけを取る。だいたい「良い」状態。

const
1行だけをピンポイントで取る。かなり「良い」状態。

さっきの例では、type = ref でした。

tabletypekeyrows
usersrefidx_email1

これは、

インデックス idx_email を使って、条件に合う行だけを取っている

という意味で、「良い」状態です。

逆に、もしこうなっていたら要注意です。

tabletypekeyrows
usersALLNULL10000

type = ALL は「テーブル全体をなめている」状態です。
key = NULL は「インデックスを使っていない」ことを意味します。

rows が 10000 なら、1万行全部を読んでから WHERE で絞り込んでいる、ということです。
テーブルが大きくなると、ここがボトルネックになります。

Day16 前半では、

type が ALL なら「ちょっと危ないかも」
type が ref / const なら「とりあえず良さそう」

くらいの感覚を持てればOKです。


key カラム

「“どのインデックスを実際に使ったか”を見る」

key カラムは、「実際に使われたインデックスの名前」です。

さっきの例では、

tabletypekeyrows
usersrefidx_email1

となっていて、idx_email が使われています。

もし、テーブルにインデックスを作っているのに、
key = NULL になっていたら、

インデックスはあるけど、このクエリでは使われていない

ということになります。

インデックスを作ったのに遅いまま、というときは、
まずここを疑います。

「インデックスがない」のか
「インデックスはあるけど、使われていない」のか

この2つは、対策が全然違うので、
EXPLAIN で切り分けられるようになると一気に楽になります。


rows カラム

「“どれくらい読みに行くつもりか”のざっくり見積もり」

rows は、「このテーブルから何行くらい読むつもりか」の見積もりです。
実際の行数ではなく、統計情報からの予測ですが、感覚をつかむには十分です。

例えば、

tabletypekeyrows
usersrefidx_email1

なら、「1行くらい読めば済みそう」と判断している、ということです。

一方、

tabletypekeyrows
usersALLNULL100000

なら、「10万行くらい読むことになりそう」と見積もっているわけです。

JOIN が絡むと、複数テーブルの rows が掛け算的に効いてくるので、
rows が大きいテーブルがどれか、というのは意識しておくとよいです。

Day16 前半では、

rows がやたら大きいのに type が ALL だと「かなり危ない」
rows が小さくて type も ref / const なら「だいぶ安心」

くらいのざっくり感覚で十分です。


まとめて読む練習:良いパターンと悪いパターン

「“EXPLAIN を見て一言コメントできる”ところまで行く」

ここまでの話を、2つのパターンでまとめてみます。

良さそうなパターン(インデックスが効いている)

tabletypekeyrows
usersrefidx_email1

コメント例:

users テーブルに対して、idx_email インデックスを使って、条件に合う1行だけを取っている。type も ref で rows も1なので、かなり効率よく取れていそう。

危なそうなパターン(フルスキャン)

tabletypekeyrows
usersALLNULL100000

コメント例:

users テーブルをインデックスなしで全件スキャンしていて、10万行くらい読むつもりになっている。WHERE 条件に合う行が少ないなら、インデックスを貼るか、クエリを書き直す余地がありそう。

Day16 前半の目標は、「このくらいのコメントを自分の言葉で言える」ことです。
全部のカラムを完璧に理解する必要はありません。


Day16 前半のまとめ

実行計画は「SQL の結果」ではなく「その結果を取るために MySQL がどう動くつもりか」という“作戦書”であり、EXPLAIN SELECT ... と書くことで、その作戦を表形式で確認できる。
最初からすべてのカラムを理解する必要はなく、まずは table(どのテーブルか)、type(アクセス方法のざっくりランク)、key(実際に使われたインデックス)、rows(読みに行くつもりの行数の見積もり)の4つに絞って、「type が ALL で key が NULL かつ rows が大きいときは“フルスキャンで危ない”、type が ref / const で key にインデックス名が入り rows が小さいときは“インデックスが効いていて良さそう”」という感覚を持てれば、実務で「このクエリ、なんか遅いな」と感じたときに、EXPLAIN を叩いて一言コメントできるレベルには立てる。

後半では、
JOIN が絡んだときの EXPLAIN の読み方、
possible_keyskey の違い、
Extra カラムに出てくる「Using index」「Using filesort」などの意味を、
インデックス設計と結びつけながらもう一段深く見ていきます。

タイトルとURLをコピーしました