発展関連トピック

インデックスのコスト(更新オーバーヘッド)

定義

インデックスの更新コストとは、INSERT/UPDATE/DELETE時にインデックス側の再構成が必要になることで生じるオーバーヘッドであり、貼りすぎると全体性能を悪化させる。

インデックスはタダではない

インデックスは検索を速くする代わりに、以下のコストを裏で払っています。

  • ストレージ: テーブル本体とは別に、インデックスのB-tree構造をディスクに保持する。大規模テーブルでは数百MB〜数GBに達することも
  • 書き込みオーバーヘッド: INSERT / UPDATE / DELETE のたびに、テーブル本体だけでなくインデックス側にも書き込みが発生する
  • 統計情報の維持: オプティマイザが判断を下すための統計情報も、インデックスの数だけ管理対象が増える
  • プランニング時間: オプティマイザの検討候補が増えるほど、実行計画を立てる時間も伸びる

書き込みコストの正体は「n+1 ページ書き込み」

インデックスを貼れば貼るほど INSERT が遅くなる、と言われる理由はシンプルです。

1 件の INSERT で、テーブル本体のページ 1 枚に加えて、貼っているインデックスの葉ノードページを 1 枚ずつ更新する必要があります。インデックスが n 個あれば n + 1 ページの書き込みです。

INSERT INTO users (email, name, status, ...)
VALUES (...);

-- 発生する書き込み(概念)
-- 1. users テーブル本体のページ ×1
-- 2. idx_email の葉ノード         ×1
-- 3. idx_status の葉ノード        ×1
-- 4. idx_created_at の葉ノード    ×1
--   (インデックスが n 個あるだけ増える)

インデックスの葉ノード分裂や、B-tree の再バランスが発生すればさらに書き込みは増えます。 UPDATE の場合は「更新されたカラムに関連するインデックスだけ」が対象ですが、主キーや複合キーの更新は複数インデックスに波及します。

物理コストは「ページI/O 回数」で決まる

インデックスが「速い」「遅い」と言うときの実体は、ページI/Oの回数と種類です。目安は以下。

  • シーケンシャルページ読み取り: 1枚あたり数十マイクロ秒〜。連続領域は先読みが効くので実質もっと速い
  • ランダムページ読み取り: 1枚あたり数ミリ秒〜。シーケンシャルの数十〜数百倍遅い
  • ページ書き込み: WAL/redo log や fsync も絡み、読み取りより更に重い

だから「更新頻度が高いテーブルにインデックスを大量に貼る」ことは、実質的に 書き込み時のランダムページ書き込み回数を掛け算で増やす 選択と同じです。

どこまで貼っていいか(目安)

魔法の数字はないですが、実務でよく使う指針:

  • 主キー / ユニーク制約: 迷わず貼る(強制的に必要)
  • 頻出のWHERE条件: SELECT ... WHERE X = ? を高頻度で叩くなら X にインデックス
  • 頻出のJOIN条件: 外部キー側にインデックス。特に大きなテーブル同士の JOIN
  • ソート/GROUP BY: 頻出のソート順にインデックス

大まかな上限感として、1テーブルに 5〜7 個を超えたら要確認、10個以上ならほぼ間違いなく貼りすぎです。

「貼りすぎ」を防ぐ実務チェックリスト

  1. 本当に使われているかEXPLAIN でインデックスが選ばれているか確認。使われていないインデックスは削除候補
  2. 重複していないか(A)(A, B) がある場合、前者は後者で代替できることが多いので削除できる
  3. 書き込み比率を見る:INSERT/UPDATE の頻度が高いテーブルでは、インデックス数を積極的に絞る
  4. 使用回数の統計ビューを見る:多くのRDBMSに「そのインデックスが何回使われたか」を記録する統計ビューがある。ゼロ回のものは棚卸し対象
  5. ログ系・時系列テーブルは特に絞る:ほぼ書き込みしかないテーブルにインデックスを多く貼るのは典型的なアンチパターン

まとめ

インデックス設計は「速くする」だけの作業ではなく トレードオフを設計する 作業です。

検索頻度、書き込み頻度、データサイズ、ビジネス上の許容レイテンシ──これらを見て、本当に必要なものだけ を貼るのが正解に近づく方法です。 「とりあえず全部のカラムにインデックス」は、書き込みコストとストレージを爆発させる最短ルートなので避けましょう。

よくある疑問

Q.インデックスはどれくらいまで貼っていい?
A.「よく使うクエリのパターン数」+ 主キー・ユニーク制約分が目安。1テーブルに10個以上あるならほとんどのケースで貼りすぎです。使われていないインデックスは定期的に棚卸ししましょう。
Q.使われていないインデックスを見つけるには?
A.多くのRDBMSがインデックスの使用回数を記録する統計ビューを提供しており、それを使うと「一度も使われていないインデックス」を洗い出せます。棚卸しの起点として有効です。
Q.書き込み負荷が高いテーブルで気をつけることは?
A.読み取り優位のテーブルよりインデックスを絞る。特にログ・時系列・大量INSERTが想定されるテーブルでは、必要最小限に留めるのが定石。

関連トピック

もっと学びたい方へ(おすすめ書籍)

本セクションはAmazonアソシエイトのリンクを含みます。購入いただくと運営者に紹介料が入る場合があります。

オンライン個別指導

もっと深くDBを学びたい方へ。

たいてっくが、SQL・データベース設計・パフォーマンスチューニング・ IPAデータベーススペシャリスト対策まで、1対1で学習をサポートします。まずは無料相談から。

無料相談を予約する →