ALGORITHM VISUALIZER

コストベース最適化(Cost-Based Optimization)

データベースが複数の実行プランのコストを見積もり、最も効率的なプランを自動選択する仕組み

シナリオ選択
テーブル設定
orders50,000 rows
users10,000 rows
条件設定
orders.status
選択率 (selectivity)20.0%
インデックス
PLAN COMPARISONREADY
📊
パラメータを設定して「最適化実行」を押してください
RUN OPTIMIZER TO COMPARE PLANS
解説

📌
コストベース最適化とは

コストベース最適化(Cost-Based Optimization, CBO)は、PostgreSQL・MySQL・Oracleなど主要なRDBMSがデフォルトで採用しているクエリ最適化手法です。 SQLクエリを受け取ると、データベースはそれを実行する方法(実行プラン)を複数生成し、テーブル統計情報(行数・カーディナリティ・データ分布)を元に各プランのコストを数値化します。 そして最もコストが低い(=最も効率が良いと推定される)プランを自動的に選択して実行します。

開発者は EXPLAIN ANALYZE コマンドで、オプティマイザがどのプランを選んだか、実際のコストはいくつだったかを確認できます。 スロークエリの原因調査やインデックス設計の判断に不可欠なツールです。

コストの計算にはディスクI/Oコスト(ページ読み取り回数)とCPUコスト(行処理・比較演算)が含まれます。 PostgreSQLの場合、シーケンシャルページ読み取りのコストは seq_page_cost = 1.0、ランダムページ読み取りは random_page_cost = 4.0 がデフォルトです。

🔍
このツールの値は何を表している?

このビジュアライザは、PostgreSQLのコストモデルに準拠した計算式を使って、各実行プランの推定コストを算出しています。 コストの単位は「ディスクページ1回のシーケンシャル読み取りを 1.0 とした相対値」です。絶対的な実行時間ではなく、プラン同士の相対比較に使います。

テーブルの行数とコストの関係
行数が増えるとページ数も増え、seq_scanのコストはほぼ線形に増加します。一方、index_scanのコストは選択率が低い(=絞り込みが効く)ほど有利になります。行数10万のテーブルで選択率1%なら、index_scanのコストはseq_scanの数十分の1になることもあります。
seq_scan vs index_scan の判断基準
一般的に選択率が5〜15%を超えるとseq_scanの方が有利になります。 これはindex_scanがランダムI/O(random_page_cost=4.0)を伴うのに対し、seq_scanはシーケンシャルI/O(seq_page_cost=1.0)で済むためです。 スライダーで選択率を変えて、切り替わるポイントを確認してみてください。
コストベース最適化の全体イメージ
SELECT * FROM users WHERE age > 25オプティマイザPlan A: Seq Scancost = 1500Plan B: Index Scan (age_idx)cost = 45最小コストを選択!

⚙️
仕組み

コストベース最適化は、以下の4つのステップで実行されます。SQLを受け取ってから実行プランが決定するまで、通常は数ミリ秒以内に完了します。

Step 1: 統計情報の収集

レストランを選ぶとき、まず口コミサイトで評価や距離を調べますよね? データベースも同じで、テーブルの行数・値の分布・インデックスの有無などの「口コミ情報」を事前に収集しています。PostgreSQLでは ANALYZE コマンドがこの収集を行います。

ANALYZEテーブル統計収集pg_statistics行数, カーディナリティデータ分布, NULL率ヒストグラム値の分布を把握
Step 2: プラン列挙

目的地へのルートを複数調べるイメージです。「高速道路を使う」「下道を行く」「電車で行く」のように、同じクエリでも実行方法は何通りもあります。

SQL QuerySELECT ...Plan A: seq_scanPlan B: idx_scanPlan C: bmp_scanJOINの場合結合順序 x 結合方式
Step 3: コスト計算

各ルートの所要時間を見積もるイメージです。高速道路は速いけど料金がかかる(ランダムI/Oは速いがコスト4.0)、下道は遅いが無料(シーケンシャルI/Oはコスト1.0)。

Step 4: 最適選択

見積もりが最も安いルートを選びます。これがEXPLAINで表示されるプランです。

I/Oコストpages x page_cost+ CPUコスト全プラン比較min(total_cost)OPTIMAL最小コストのプラン

特徴

  • 📊統計情報ベースの意思決定 — テーブルの行数・カーディナリティ・データ分布などの実測値に基づいてコストを計算する。「このテーブルは小さいからseq_scanで十分」「このカラムはユニーク値が多いからインデックスが効く」といった判断を自動で行う。
  • 🔄データ量に応じた適応的な選択 — 同じSQLでも、テーブルの行数が100行のときと100万行のときでは最適なプランが異なる。CBOはデータの変化に合わせて最適なプランを動的に切り替える。
  • 🎯結合順序の最適化 — 3テーブル以上のJOINでは、結合の順番によってコストが劇的に変わる。CBOは結合順序の組み合わせも探索し、最も安い順序を選択する。
  • インデックスの活用判断 — インデックスがあっても常に使うとは限らない。選択率が高い(=多くの行がマッチする)場合はseq_scanの方がランダムI/Oを避けられて高速。この判断を定量的に行う。
  • 🧮EXPLAIN で可視化可能 — 開発者は EXPLAIN / EXPLAIN ANALYZE で選択されたプランとそのコストを確認できる。スロークエリの原因特定やインデックス設計の根拠になる。

💡
ユースケース

スロークエリ調査
EXPLAIN ANALYZEでオプティマイザが選んだプランを確認。意図しないseq_scanが走っていないか、index_scanが期待通り使われているかを検証する。
インデックス設計
どのカラムにインデックスを張るべきかの判断材料。選択率が低い(ユニーク値が多い)カラムほどインデックスの効果が高い。
パフォーマンスチューニング
テーブルの行数が増加した後にANALYZEで統計を更新し、プランが適切に変化するか確認。autovacuumの設定見直しにも関連。
クエリリライト
サブクエリをJOINに書き換えたり、WHERE条件の順序を変えた際に、オプティマイザが異なるプランを選ぶかを比較検証。

📖
用語解説

コストモデル(Cost Model)
ディスクI/OとCPU処理を数値化するモデル。PostgreSQLではseq_page_cost=1.0、random_page_cost=4.0、cpu_tuple_cost=0.01、cpu_index_tuple_cost=0.005がデフォルト値。これらを組み合わせて各操作のコストを算出する。
選択率(Selectivity)
WHERE条件によってテーブルの何割の行がマッチするかを示す値(0〜1)。selectivity=0.01なら「全体の1%の行がマッチ」を意味する。この値が低いほどインデックスが有効。pg_statisticsのヒストグラムから推定される。
カーディナリティ(Cardinality)
カラム内のユニークな値の数。例えば「性別」カラムのカーディナリティは2〜3、「メールアドレス」カラムは行数とほぼ同じ。カーディナリティが高いカラムほどインデックスの効果が大きい。
seq_scan(Sequential Scan)
テーブルの全ページを先頭から順番に読むスキャン方式。シーケンシャルI/O(seq_page_cost=1.0)なので、大量の行を読む場合はランダムI/Oより効率的。WHERE条件で絞り込む割合が大きい場合はこちらが選ばれる。
index_scan(Index Scan)
B-Treeインデックスを辿って該当行のポインタを取得し、テーブルから行を取得する方式。ランダムI/O(random_page_cost=4.0)が発生するが、少数の行だけ読む場合は全体スキャンより圧倒的に高速。
hash_join(Hash Join)
小さい方のテーブルからハッシュテーブルを構築し、大きい方のテーブルを1行ずつ走査してハッシュテーブルと照合する結合方式。等値結合(=)で最も効率的なことが多い。メモリ上にハッシュテーブルを作るためwork_memの設定が重要。
merge_join(Merge Join)
両方のテーブルを結合キーでソートしてからマージする結合方式。ソート済みデータ(インデックスがある場合など)で特に効率的。大規模データの等値結合や範囲結合に適している。
nested_loop(Nested Loop)
外側テーブルの各行に対して内側テーブルを走査する結合方式。内側テーブルにインデックスがある場合や、結合する行数が少ない場合に高速。最悪の場合はO(n*m)になるため大規模テーブル同士では遅い。
EXPLAIN / EXPLAIN ANALYZE
EXPLAINはオプティマイザが選択した実行プランと推定コストを表示するコマンド。EXPLAIN ANALYZEは実際にクエリを実行して実測値も表示する。推定値と実測値の乖離が大きい場合はANALYZEで統計を更新する必要がある。

📋
コストベース最適化の手順

1
テーブル統計の収集(ANALYZE)
データベースはテーブルの行数、各カラムのユニーク値数(カーディナリティ)、NULL率、値の分布(ヒストグラム)を収集してpg_statisticsに保存する。PostgreSQLではautovacuumが自動的にANALYZEを実行するが、大量のINSERT/UPDATE直後は手動ANALYZEも有効。
2
SQLのパース・書き換え
SQLがパーサーで構文解析され、内部的なクエリツリーに変換される。ビューの展開やサブクエリの書き換えもこの段階で行われる。
3
候補プランの列挙
各テーブルへのアクセス方式(seq_scan / index_scan / bitmap_scan等)の組み合わせ、JOINがある場合は結合方式(nested_loop / hash_join / merge_join)と結合順序のすべての組み合わせを列挙する。テーブルが多い場合は探索空間が爆発するため、PostgreSQLはGEQO(遺伝的クエリ最適化)を使って探索を絞る。
4
コスト見積もり
各候補プランに対して、統計情報を元にI/Oコスト(ページ読み取り回数 x ページコスト)とCPUコスト(行処理数 x タプルコスト + 比較演算数 x オペレータコスト)を合算した総コストを算出する。
5
最適プランの選択
全候補プランの中から最もtotal_costが低いプランを選択する。これが実際に実行される実行プランとなる。
6
実行と結果返却
選択された実行プランに従ってエグゼキューターがデータを取得し、結果をクライアントに返す。EXPLAIN ANALYZEを使うと推定コストと実測時間の両方を確認できる。

⚖️
Cost-Based vs Rule-Based 比較

CBOが登場する前は、Rule-Based Optimization(RBO)が主流でした。RBOは「インデックスがあれば必ず使う」「JOINは常にnested_loopで行う」といった固定ルールに従います。 現代のRDBMSではCBOが標準ですが、MySQL 5.x以前やOracle 7以前はRBOがデフォルトでした。

観点Cost-Based(CBO)Rule-Based(RBO)
判断基準統計情報に基づくコスト計算事前定義された優先順位ルール
データ量への適応行数・分布に応じて最適プランが変化データ量に関係なく同じルールを適用
インデックス利用選択率に応じて使う/使わないを判断インデックスがあれば常に使用
JOIN方式コスト比較でNL/Hash/Mergeを選択固定(通常Nested Loopのみ)
統計情報必須(ANALYZE / autovacuum)不要
採用DBPostgreSQL, MySQL 8+, Oracle 10g+Oracle 7以前, 古いMySQL
精度統計が正確なら高精度データ量が増えると非効率になりやすい

関連コンテンツ