こんにちは、かつコーチです。
前回まではCREATE TABLEやデータ型、正規化といったテーブル設計の話を扱ってきました。
テーブル設計をきちんと行っても、データ件数が増えてくると検索が遅くなる場面が出てきます。
そこで登場するのがインデックスです。
今回はインデックスがなぜ検索を速くするのか、その仕組みと基本の使い方を解説します。
インデックスとは?
本の索引と同じ仕組み
インデックスとは、テーブル内のデータを高速に検索するために作られる、いわば「データの目次」のような仕組みです。
分厚い辞書で単語を探すとき、最初のページから1文字ずつ読んでいく人はいません。
索引を見て、目的のページに一気にたどり着くはずです。
データベースのインデックスも同じ考え方で、「この値はどこにあるか」をあらかじめ整理しておくことで、目的のデータに素早くたどり着けるようにします。
インデックスがない場合の検索:フルスキャン
インデックスがない列を条件に検索すると、データベースはテーブルの先頭から1行ずつすべてのデータを確認していきます。
これをフルテーブルスキャンと呼びます。
SELECT * FROM members WHERE email = 'taro@example.com';
membersテーブルにemailのインデックスがない場合、このクエリはデータが100件でも100万件でも、全行を舐めるように調べることになります。
データ件数が少ないうちは体感できるほどの差は出ませんが、件数が増えるほど検索時間は線形に伸びていきます。
基本の使い方
手順1:インデックスを作成する
CREATE INDEXでインデックスを作成します。
CREATE INDEX idx_members_email ON members(email);
これでmembersテーブルのemail列にインデックスが作成されました。
命名規則に厳密なルールはありませんが、「idx_テーブル名_カラム名」のような一貫した命名にしておくと、後から見たときに何のインデックスか分かりやすくなります。
手順2:インデックスの効果を体感する
私が実際にローカル環境で10万件のダミーデータを投入して検証したところ、インデックスなしの検索では数百ミリ秒かかっていたWHERE email = ...のクエリが、インデックスを作成した後は数ミリ秒で返るようになりました。
体感としても明らかに違いが分かるレベルの改善でした。
-- インデックス作成前後で同じクエリを実行して比較する
SELECT * FROM members WHERE email = 'taro@example.com';
「本当に効果があるのか」を疑うよりも、まずは自分の手元で試してみるのが理解への一番の近道です。
手順3:主キーと外部キーには自動でインデックスが付く
以前の記事で解説したPRIMARY KEYやFOREIGN KEYには、MySQL(InnoDB)が自動的にインデックスを作成してくれます。
そのため、主キーでの検索や外部キーを使ったJOINは、明示的にインデックスを作らなくてもすでに高速化されています。
インデックスを新たに検討すべきなのは、WHERE句やORDER BY句、JOINの結合条件で頻繁に使われる、主キー・外部キー以外の列です。
つまずきやすいポイント:インデックスを付けすぎる
更新が遅くなるという見落とし
インデックスは検索を速くする一方で、タダで手に入るものではありません。
❌ Before:とにかく全部の列にインデックスを付ける
CREATE INDEX idx_members_name ON members(name);
CREATE INDEX idx_members_email ON members(email);
CREATE INDEX idx_members_phone ON members(phone);
CREATE INDEX idx_members_address ON members(address);
CREATE INDEX idx_members_created_at ON members(created_at);
私が過去に見た案件で、「検索が速くなるならとにかく付けておこう」という発想で全カラムにインデックスを付けたテーブルがありました。
その結果、INSERTやUPDATEのたびに、すべてのインデックスを同時に更新する必要が生じ、書き込み処理が想定より大幅に遅くなるという問題が起きました。
インデックスは検索の目次であると同時に、データ更新のたびにメンテナンスが必要な「もう1つのテーブル」のようなものです。
✅ After:実際によく使う検索条件だけに絞る
-- WHERE句・ORDER BY句で頻繁に使う列に絞ってインデックスを作成
CREATE INDEX idx_members_email ON members(email);
インデックスは「よく検索条件に使う列」「よく並び替えに使う列」に絞って作成するのが基本方針です。
迷ったときは、実際のアプリでどんなクエリが頻繁に発行されているかをログで確認してから判断するとよいでしょう。
応用:複合インデックスとカーディナリティ
複数列をまとめたインデックス
2つ以上の列をセットで検索条件にする場合は、複合インデックスが有効です。
CREATE INDEX idx_orders_member_status ON orders(member_id, status);
このインデックスはmember_idとstatusの両方を条件にした検索で威力を発揮します。
SELECT * FROM orders WHERE member_id = 10 AND status = '発送済み';
ただし複合インデックスは、先頭の列(この例ではmember_id)から順番に効いていく性質があるため、列の並び順を業務でよく使う検索パターンに合わせて設計する必要があります。
カーディナリティを意識する
インデックスの効果は、列の値のばらつき(カーディナリティ)に大きく左右されます。
「性別」のように値の種類が数個しかない列にインデックスを付けても、絞り込める件数が少なく、あまり効果は出ません。
一方「メールアドレス」のように値がほぼ一意になる列は、カーディナリティが高く、インデックスの効果を最大限発揮できます。
インデックスを検討する際は、「その列の値はどれくらいばらついているか」を意識しておくと、効果的な設計ができるようになります。
まとめ
この記事のポイント
- インデックスは検索を高速化する「データの目次」のような仕組み
- インデックスがないと、全行を確認するフルテーブルスキャンが発生する
CREATE INDEXで作成でき、主キー・外部キーには自動で付く- インデックスを付けすぎると更新処理が遅くなるため、よく使う検索条件に絞る
- 複合インデックスは列の順番、単一列インデックスはカーディナリティを意識する
次に読むべき記事
インデックスが実際に使われているかどうかは、感覚だけでなくデータベースに直接確認する方法があります。
次回は実行計画を確認するEXPLAINの読み方を解説します。
→ 次の記事:EXPLAINで実行計画を読む基本
タグ: SQL, 中級者向け, パフォーマンス