【実践】DBインデックス設計の基本|複合インデックスの列順とEXPLAINの読み方

「開発環境では一瞬で返るのに、本番でデータが増えたらクエリが数秒かかるようになった」——DB起因の性能問題は、たいていリリース後しばらくしてから顕在化します。そして原因の多くは、インデックスが張られていないか、張ってあるのに使われていないかのどちらかです。

この記事では、B-treeインデックスの基本から、複合インデックスの列順の決め方、EXPLAINの読み方、そして「張ったのに効かない」典型パターンまでを整理します。

目次

B-treeインデックスの基本イメージ

RDBMSのインデックスはほとんどがB-tree構造です。ソート済みの索引を木構造でたどるため、全行を走査する代わりに対数オーダーで目的の行に到達できます。書籍の索引と同じで、「値で探す」「範囲で探す」「ソート済みで取り出す」操作が得意です。

一方でインデックスはタダではありません。INSERT/UPDATE/DELETEのたびに索引の更新コストがかかり、ストレージも消費します。「とりあえず全列に張る」は書き込み性能を落とすだけなので、WHERE・JOIN・ORDER BYで実際に使われる列に絞って設計します。

複合インデックスは「列の順番」がすべて

複合インデックスは先頭列から順に使われます。電話帳が「姓→名」の順で並んでいるのと同じで、(last_name, first_name) の索引は姓だけの検索には使えますが、名だけの検索には使えません。

列順を決める実務上の指針は次の通りです。

  • 等値条件(=)で使う列を先頭に、範囲条件(>、BETWEEN)の列は後ろに置く
  • 範囲条件の列より後ろの列は、絞り込みには使われないことを意識する
  • ORDER BYの列を末尾に含めると、ソート処理自体を省略できることがある
-- status で等値、created_at で範囲+ソートなら
CREATE INDEX idx_orders_status_created
    ON orders (status, created_at);

SELECT * FROM orders
 WHERE status = 'pending'
   AND created_at >= '2026-06-01'
 ORDER BY created_at;

逆順の (created_at, status) にすると、範囲条件が先頭に来るためstatusでの絞り込みに索引を活かせません。この違いだけで実行時間が桁で変わることがあります。

EXPLAINで「想定通りか」を必ず確認する

インデックスは張って終わりではなく、実際に使われているかをEXPLAINで確認します。MySQLの場合、最低限見るべきは次の項目です。

  • type: ALL(フルスキャン)が出ていたら要注意。refrange なら索引を利用
  • key: 実際に選ばれたインデックス。想定と違う索引が選ばれることもある
  • rows: 読む見込みの行数。実データ量に対して極端に多ければ設計を見直す
  • Extra: Using filesortUsing temporary はソート・一時表のコストがかかっているサイン

本番相当のデータ量で確認するのが重要です。行数が少ないとオプティマイザがあえてフルスキャンを選ぶため、開発環境のEXPLAIN結果は当てにならないことがあります。

効かないインデックスの典型パターン

「張ってあるのに使われない」場合、だいたい次のどれかに当てはまります。

  • 索引列への関数適用: WHERE DATE(created_at) = '2026-07-01' は不可。created_at >= '2026-07-01' AND created_at < '2026-07-02' に書き換える
  • 暗黙の型変換: 文字列列に数値で比較(WHERE code = 123)すると索引が無効になる
  • 前方一致でないLIKE: LIKE '%keyword%' はB-treeでは効かない。全文検索の導入を検討
  • 絞り込み効果の低い列: 値が2〜3種類しかない列の単独インデックスは選ばれにくい
  • OR条件の組み合わせ: 索引が使えない形になりやすく、UNIONへの分解が有効な場合がある

まとめ

インデックス設計は「クエリありき」です。まず実際のWHERE・JOIN・ORDER BYを洗い出し、等値→範囲→ソートの原則で複合インデックスの列順を決め、本番相当データのEXPLAINで検証する。この流れを新機能のリリース前チェックに組み込んでおくと、「データが増えてから発覚する性能問題」をかなりの割合で未然に防げます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

クラウド・バックエンドエンジニア。AWSを中心に設計・構築から運用までを担当しています。主要言語は Java・JavaScript・Python。運用の現場で拾った知見を、再現できる手順に落として残すのがこのブログのテーマです。

目次