変更要約: 初版
5.1インデックス設計
検索を高速化するB木インデックス、等価検索に強いハッシュインデックス、低選択性の列に向くビットマップインデックスの使い分け、複数列を対象とする複合インデックスの列順と選択性の関係、そしてインデックスが更新性能に与えるトレードオフを学びます。
データベース設計者にとって「インデックスは多いほど良い」という単純な話ではありません。検索を速くする一方で、行の追加・更新・削除のたびにインデックス自体も維持されるため、参照と更新の両方の負荷を見積もったうえで、どの列に・どの種類のインデックスを・どの順序で張るかを判断する必要があります。この節ではインデックス構造の特性と、遅いクエリを改善するための設計判断を学びます。
5.1.1B木インデックスの仕組みと適性
- B木インデックス(B-treeインデックス)=キー値を大小関係を保ったまま木構造に整理し、根から葉へたどることで対象行を
O(log n)程度で見つけられる、最も汎用的なインデックス構造。多くのRDBMSで既定のインデックス種別になっている。 - 木構造で大小関係を保持しているため、等価検索(
=)だけでなく範囲検索(>・<・BETWEEN)や並べ替え(ORDER BY)にも強い。日付範囲での抽出や連番の前方一致検索など、業務クエリの大半はB木で十分に高速化できる。
5.1.2ハッシュインデックスとビットマップインデックス
- ハッシュインデックス=キー値をハッシュ関数で変換し、ハッシュ値をもとに格納位置へ直接アクセスする構造。等価検索(
=)はO(1)に近い速度で行えるが、ハッシュ値は大小関係を保たないため範囲検索や並べ替えには使えない。完全一致の検索が支配的で範囲検索がほぼ発生しない列(例:セッションIDでの一意検索)に向く。 - ビットマップインデックス=列が取りうる値ごとに、各行が該当するかを0/1のビット列で表現する構造。「性別」「会員ランク」のように取りうる値の種類が少ない(低選択性)列では、ビット列同士のAND/OR演算で複数条件を高速に絞り込める。一方、値の種類が多い(高選択性)列や更新頻度が高いテーブルでは、更新のたびにビット列を書き換えるコストが大きく不向き。
「B木=範囲検索/ソートに強い汎用型」「ハッシュ=等価検索専用で範囲検索不可」「ビットマップ=低選択性の列に強いが更新頻度が高いと不向き」の対比が最頻出です。「選択性」は列内の値の種類数(distinct値の割合)を指す用語として押さえましょう。
あるECサイトのDBAが、注文テーブル(orders)に対する次のクエリが遅いという報告を受けたとします:SELECT * FROM orders WHERE customer_id = 12345 AND order_date >= '2026-06-01' AND order_date < '2026-07-01'。実行計画を確認すると全表走査になっており、テーブルには数百万行あるため実用に耐えない速度でした。まず着目すべきはインデックスの有無で、customer_idにもorder_dateにもインデックスが張られていませんでした。次に検討するのは複合インデックスの列順です。この業務では「特定顧客の直近の注文」を調べるクエリが大半を占め、customer_idは数万件の顧客で分散する一方、order_dateは日単位でさらに絞り込む形で使われています。ここで複合インデックスを(order_date, customer_id)の順で作ると、まず日付範囲で絞り込んでから顧客IDで探すことになり、日付範囲がヒットする行数が多い月は絞り込み効果が薄くなります。逆に(customer_id, order_date)の順で作れば、選択性の高いcustomer_idで先に大きく絞り込み、残った少数の行に対してorder_dateの範囲条件を適用するため、インデックスの効きが良くなります。一般に複合インデックスは等価条件で使われ選択性の高い列を先頭に、範囲条件で使われる列を後方に置くのが定石です。ただし、この注文テーブルは書き込み(新規注文)も頻繁に発生するため、インデックスを増やすほどINSERT時にインデックス構造の更新コストが積み上がるトレードオフも忘れてはいけません。検討の結果、参照頻度の高いこの複合インデックス1本を追加するにとどめ、他の列への追加インデックスは書き込み性能への影響を測定してから判断する、という段階的な設計判断を下しました。
| 種類 | 得意な検索 | 弱点/注意点 |
|---|---|---|
| B木インデックス | 等価検索・範囲検索・並べ替え | 特殊な用途では専用構造より遅い場合がある |
| ハッシュインデックス | 等価検索のみ(高速) | 範囲検索・並べ替えに使えない |
| ビットマップインデックス | 低選択性の列の複合条件(AND/OR) | 更新頻度が高いと書き換えコスト大 |
| 複合インデックス | 先頭列で大きく絞り込み後続列で追加絞り込み | 列順を誤ると効果が薄い |
ひっかけ: 「インデックスを張れば張るほど検索は速くなるので全列に張るべきだ」は誤りです——インデックスはINSERT/UPDATE/DELETEのたびに維持コストが発生し、書き込みが多いテーブルでは逆効果になりえます。また「複合インデックスは列の並び順に関係なく同じ効果を発揮する」も誤り=選択性の高い列を先頭に置くかどうかで絞り込み効果が大きく変わります。
5.1.3この節のまとめ
- B木インデックスは等価/範囲検索/ソートに強い汎用型。ハッシュインデックスは等価検索専用で範囲検索不可
- ビットマップインデックスは低選択性の列の複合条件に強いが更新頻度が高いと不向き
- 複合インデックスは選択性の高い列を先頭に置くのが定石。インデックス追加は検索性能と更新コストのトレードオフ
進捗の記録にはログインが必要です。
理解度チェック
(軽い確認用)Q1. あるECサイトの注文テーブルで`SELECT * FROM orders WHERE customer_id = ? AND order_date BETWEEN ? AND ?`という検索が頻発する。customer_idは数万件に分散する高選択性の列、order_dateは日単位の範囲条件で使われる。このクエリを改善する複合インデックスの列順として最も適切なものはどれか。
Q2. 会員テーブルの「会員ランク」列(取りうる値は4種類のみ)に対して、ランク別の複合条件検索を高速化したいが、この列は月次バッチ以外ではほとんど更新されない。最も適したインデックス種別はどれか。
Q3. 書き込み(INSERT)が1秒間に数千件発生する注文明細テーブルに対し、参照系のクエリを高速化するためにインデックスを追加する設計を検討している。最も妥当な判断はどれか。

