3秒でわかる
テーブルの特定の列に作る索引。全行を上から調べる代わりに目的の行へ直接たどり着けるので、検索の待ち時間が桁で変わります。
30秒図解
もう少し詳しく
どういうものか
インデックスは、テーブルの列に対して別途つくる並べ替え済みのデータ構造です。多くの RDBMS では B+ 木という木構造が使われ、値を手がかりに数回のたどり直しで目的の行の位置が分かります。
インデックスが無い列で検索すると、データベースは先頭から全行を読んで条件に合うか調べます。これがフルスキャンです。100 万行なら 100 万回の照合になりますが、B+ 木なら数十回の比較で済みます。
なぜ必要か
行数が増えたときの伸び方が違います。フルスキャンは行数に比例して遅くなり、10 倍のデータなら 10 倍の時間がかかります。木構造の探索は行数が 10 倍になっても数段深くなる程度です。
そのため、テスト環境の数百件では差が出ず、本番でデータが増えてから急に遅くなる、という形で問題が表面化します。
具体例
-- 検索条件によく使う列に索引を作る
CREATE INDEX idx_orders_customer ON orders (customer_id);
-- 使われているかを実行計画で確認する
EXPLAIN SELECT * FROM orders WHERE customer_id = 42;複数列をまとめた複合インデックスもあります。
CREATE INDEX idx_orders_customer_date ON orders (customer_id, ordered_at);この索引は customer_id だけの検索にも効きますが、ordered_at だけの検索には効きません。左から順に使う決まりがあるためです。
つまずきやすいところ
列に関数や計算を掛けると索引が使われなくなります。
-- 索引が効かない書き方
SELECT * FROM users WHERE YEAR(created_at) = 2024;
-- 範囲で書けば効く
SELECT * FROM users WHERE created_at >= '2024-01-01'
AND created_at < '2025-01-01';前方一致以外の LIKE '%山田%' も同じ理由で効きません。
作りすぎも問題です。インデックスは行の追加や更新のたびに書き換えが必要なので、書き込みが遅くなり容量も増えます。検索されない列に片っ端から作るのは逆効果です。
さらに、対象が全体の半分を占めるような列は、索引をたどるより全部読んだほうが速いと判断され、作っても使われません。性別のように種類が少ない列が典型です。
覚え方
「読むのが速くなり、書くのが遅くなる」。この交換条件が判断の軸です。