インデックスの正体
SHOW INDEX でインデックスの構造を直接確認し、EXPLAIN を使って「全件スキャン」と「インデックスによる高速検索」の違いを実感します。
- 索引(インデックス)とは物理的にどのようなものなのか
- DB が「全件スキャンした」のか「インデックスで検索した」のかはどこでわかるか
- インデックスはいつ作られ、どのようなトレードオフを伴うのか
インデックスの構造 1: 名前と対象列の定義
2 行の結果が返ってきます。Key_name がインデックス名、Column_name が対象の列名を表しています。 orders テーブルには「PRIMARY(主キー id のインデックス)」と「customer_id のインデックス」がすでに存在しています。 主キーを設定するとインデックスは自動的に生成されます。また customer_id のインデックスは、第 4 章で外部キー制約を定義した際に MySQL が自動生成したものです。 テーブル一覧パネルで ▪ マークが付いている列が該当し、インデックスとは「テーブル本体とは別に存在する、名前と対象列を持つ独立したデータ構造」であることが確認できます。
インデックスの構造 2: ストレージ上のデータサイズ
data_length はテーブル本体の行データの総容量、index_length はインデックス全体の容量(バイト単位)を示しています。 インデックスは論理的な概念にとどまらず、ディスク上に独立したバイナリデータとして物理的に格納されています。 この章の後半で新しいインデックスを作成した際、この数値が実際に増加する様子を確認してみましょう。
インデックスの構造 3: 実行計画で見る探索の足跡
この章から、実行ラボに「推定プラン」「実測プラン」というタブが追加されます。 これはデータベースが「目的の行をどのように探索したか」を記録した実行計画であり、第二部(性能編)で徹底的に読み解いていく最重要ツールです。 現時点では、実測プランの最下行に注目してください。そこが Table scan(全件走査)になっているか、 それとも Index lookup(インデックスによるピンポイント探索)になっているかを確認します。
同じ SELECT 文であっても、データベースの探索アプローチは全く異なります。status 列にはインデックスが存在しないため、全 50 行を先頭から最後まで愚直に走査(Table scan)し、一致する行を拾い出しています。 一方、id や customer_id にはインデックスが存在するため、該当する行だけを直接読み取っています。 計器パネルに表示される「読んだ行数」の差が、探索効率の違いを如実に物語っています。
インデックスとは「本の巻末索引」のようなもの
テーブルのレコードは基本的に主キー(id)の順にストレージへ格納されています。そのため id での検索は瞬時に行えます。 しかし、データは status 順や ordered_at 順に並んでいるわけではないため、インデックスがなければ全件を端からチェックするしかありません。 インデックスとは、「ordered_at の値 → その値を持つ行の主キー」のペアをあらかじめ ordered_at 順に整列して保持しておく別の構造であり、 書籍の「巻末索引」とまったく同じ役割を果たしています。索引を引けば、目的のデータがどこにあるかが即座に特定できます。
ここでは概念的な理解にとどめ、インデックスのより深い内部構造(ページ、ノード、B-tree)については第二部 1 章で詳しく解き明かします。 50 件程度の少量データでは探索速度に体感上の差は現れませんが、第二部 1 章では 3,200 万行の巨大データを用いて、4 秒かかる全件走査がインデックスによって 0.01 秒未満に短縮される圧倒的な差を体感します。
インデックスを作成してみる
構文は CREATE INDEX インデックス名 ON テーブル名 (対象列) です。 インデックスを作成すると、実測プランの探索方法が Table scan から Index lookup へと切り替わり、ディスク上のストレージ容量(index_length)が増加していることが確認できます。
インデックスの代償(トレードオフ)
インデックスは検索を劇的に高速化しますが、タダではありません。 テーブルに新しい行を追加・更新するたびに、データベースはすべてのインデックス構造も最新の状態に同期更新しなければなりません。 もし 5 本のインデックスが張られていれば、1 行の INSERT ごとに本体+5 本の索引という計 6 箇所への書き込みが発生し、ディスク容量やメモリ(バッファプール)も消費します。 そのため、むやみに全列へインデックスを張るのではなく、「頻繁に WHERE 句や JOIN 結合、ORDER BY に利用される列」を厳選して設計する必要があります。
インデックスはテーブル本体とは別にディスク上に構築される検索用の索引です。実行計画を確認することで、全件走査なのかインデックス検索なのかを正確に判別できます。
確認
あるクエリが全部読んだのか索引で引いたのかは、どこを見ればわかるか。
索引を作ると増えるものは何か。それはどこで確かめられるか。
開発環境で速かったクエリが本番で遅い、を動作確認で見つけられないのはなぜか。
次の章へ
ここまでの SQL はすべてブラウザ上のラボから直接発行してきました。 しかし実務の本番システムでは、バックエンドのアプリケーションコード(Node.js, Python, Go, Java 等)や ORM を介して SQL が発行されます。 次の章では、アプリケーションからデータベースを操作する際に直面する「SQL インジェクション」「ORM の隠蔽」「N+1 問題」という 3 大トラブルを取り上げ、第一部の総仕上げを行います。