コンテンツにスキップ

Index の設計と検証

注文一覧を customer_id で絞り、created_at の新しい順に 20 件返すとする。 まず実際の query、行数、データ分布と実行計画を確認し、 (customer_id, created_at DESC) の B-tree index を候補にする。 列ごとに機械的に index を作るのではなく、絞り込みと並び順の組合せから考える。

Why — 調べる範囲と sort を減らす

Section titled “Why — 調べる範囲と sort を減らす”

B-tree は順序を持つ。先頭列の等価条件で範囲を狭め、続く列の順序を利用できる場合がある。 この query では必要な 20 件を早く取得できることを期待するが、採用される plan は統計や分布に依存する。 詳しくは PostgreSQL 18: Multicolumn Indexes

注文一覧のリクエストを図で開く

図はアプリと DB のやり取りを示す概念図で、PostgreSQL 内部処理や実測 latency を示すものではない。 図の操作 UI は英語。

index は追加の容量を使い、データ変更時に保守が必要になる。 読み取りの短縮と、書き込み・運用への影響を両方検証する。 公式 Introductionを参照。

Exception — Seq Scan は必ずしも悪くない

Section titled “Exception — Seq Scan は必ずしも悪くない”

大部分の行を読む場合や小さいテーブルでは、全体を順に読む方が有利な場合がある。 「index があれば必ず速い」「複合 index の後続列だけでは絶対に使えない」とは言わない。 PostgreSQL 18 には条件次第で skip scan を利用する場合もある。まず実行計画で確かめる。

リポジトリのターミナルで実行する。Docker Desktop を起動しておく。

ターミナルウィンドウ
npm run db:up
npm run db:lab

labs/database-index.sql は専用の index_lab schema に 10 万件の合成データを作り、 同じ SELECT を index 追加前後に実行する。再実行するとこの schema だけを作り直す。

記録するもの:scan の種類、推定行数と実測行数、sort の有無、Buffers、実行時間。 EXPLAIN ANALYZE は SQL を実際に実行する。今回は学習用 DB の SELECT を対象にしている。 時間の一回比較だけで結論を出さず、cache が温まる影響も考える。 Using EXPLAINで出力の意味を確認する。

Production — 利用者の遅さから観測する

Section titled “Production — 利用者の遅さから観測する”

一覧 API の p99 とエラー率を起点に、query の時間、待機、接続待ち、DB とアプリのメトリクスを関連付ける。 index 作成時の lock や I/O、書き込み負荷も検討対象。学習用の CREATE INDEX を本番へそのまま流用しない。

Troubleshooting — 次に何を確かめるか

Section titled “Troubleshooting — 次に何を確かめるか”

遅い query の実体とパラメータを特定し、plan、推定と実測の差、待機イベントを調べる。 Buffer の read をそのまま物理 disk read と断定しない。OS cache や storage の観測まで接続して考える。

資料を閉じて説明する:この index の列順はなぜこうしたか。データの大半が一顧客の注文なら何を確認し直すか。 学習した内容を面接形式で確かめるときは、テストシナリオ一覧から選ぶ。

一次資料の確認日:2026-09-07。実験対象:PostgreSQL 18。