テックカリキュラム

カーディナリティ推定誤差分析:SQL最適化の核心を理解する

カーディナリティ推定誤差分析:SQL最適化の核心を理解する

SQLチューニングの世界では、インデックス設計やSQLリライト以上に重要で、かつ見落とされがちな領域があります。それがカーディナリティ推定(Cardinality Estimation)です。

データベースのクエリオプティマイザは、実行計画を作る際に「各クエリステップで何行返ってくるか」を推定します。この推定値が実データとかけ離れると、最適化が崩れ、本来指数関数的に増えるべきでないコストが爆発します。

つまり、SQLが遅い原因の多くは「推定誤差」なのです。


■ カーディナリティ推定とは?

DBが実行計画を選ぶ際、以下を予測します:

  • WHERE条件で何行残るか
  • JOIN後に何行になるか
  • GROUP BYで何グループにまとまるか

これらの推定はデータ分布統計(Statistics)を基に計算されます。

例:PostgreSQLのEXPLAINでの比較

Rows (estimated): 100 Rows (actual): 50,000 

この差が「誤差」であり、誤差が大きいほど実行計画は破綻します。


■ 代表的な推定アルゴリズム

① 独立性仮定(Independence Assumption)

最も基本的で、最も壊れやすい仮定:

複数条件は互いに独立であるとみなす。

WHERE gender = 'M' AND pref = 'Tokyo' 

実際には性別と地域には強い偏りがあるのに、DBは:

[ P(M AND Tokyo) = P(M) \times P(Tokyo) ]

と計算する。

② 均等分布(Uniform Distribution Assumption)

値が均等に分布していると仮定する。

現実:

  • 売上の90%がトップ10商品
  • 月末にデータが偏る

これは均等ではありません。

③ ヒストグラム(MCV / Frequency Histogram)

PostgreSQLやOracleは、代表値(Most Common Values)や値レンジ(Histogram)を持ち、偏りを部分的に反映します。

しかし、それでも複合条件やJOINでは誤差が積み上がるため不完全です。


■ 推定誤差が実行計画に与える致命的影響

① Nested Loop が選ばれるべきでない場面で選ばれる

例:

estimated rows: 100 actual rows: 100000 

この誤差だと、オプティマイザは

「小さい表だからNested Loopが良いはず」

と安易に判断します。

→ 実際は10万行で爆死(指数関数コスト)


② Hash Join を選ばず、Seq Scanが乱発される

Hashが作れないと思われたら、低品質の実行計画が選ばれます。


③ GROUP BY で誤ったメモリ割り当てが行われる

予測が小さすぎると、Hash Aggregate が spill(ディスク退避)し、性能が激落ちします。


■ よくある推定誤差の原因

① 統計情報が古い

ANALYZE table_name; 

更新直後のデータを扱う場合、大きくズレることがある。


② WHERE 条件の相関(Correlation)が無視される

WHERE sex = 'F' AND perfume_favorite = true 

独立ではない条件の組み合わせが誤差を生む。


③ 複合インデックスが無い場合の誤差

単一カラムのヒストグラムでは、複合条件を正しく扱えない。


④ サブクエリでの選択度推定が破綻(特にNOT EXISTS)

WHERE NOT EXISTS (...) 

これは推定が極めて難しい。


■ PostgreSQLでの実際のEXPLAIN分析例

例:誤差が3桁以上に広がったパターン

 EXPLAIN ANALYZE SELECT * FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.region = 'Kanto' AND c.gender = 'F'; 

結果例:

Rows (estimated): 120 Rows (actual): 45,391 

→ Nested Loop が選ばれ、1秒のはずが50秒超え。


■ 推定誤差の改善方法

① 統計ターゲットの引き上げ(PostgreSQL)

ALTER TABLE customers ALTER COLUMN region SET STATISTICS 2000; ANALYZE customers; 

高頻度アクセスのカラムは「統計精度を上げる」のが王道。


② 拡張統計の導入(Postgres 10+)

相関関係を学習できる。

CREATE STATISTICS stat_region_gender (dependencies) ON region, gender FROM customers; ANALYZE customers; 

これにより誤差が大きく改善される。


③ 複合インデックスの検討

CREATE INDEX idx_region_gender ON customers(region, gender); 

推定精度の向上に直結。


④ クエリ rewrite(条件順序の変更)

推定誤差が小さい条件からフィルタするように書き換えると良い。


■ 推定誤差の数理的考察

推定誤差は、以下のような確率モデルのズレによって発生します。

[ EstimatedRows = TotalRows \times Selectivity ]

実際:

[ ActualRows = TotalRows \times (Selectivity + ε) ]

この誤差 ( ε ) が累積し、JOINが増えるほど指数的に膨張します。

JOIN深度 n の誤差:

[ Error \approx \prod_{i=1}^{n} (1 + ε_i) ]

小さなズレが多段JOINで巨大化する理由がここにあります。


■ まとめ

  • カーディナリティ推定はSQL最適化の最も重要な基盤
  • 誤差はNested Loop暴走・Hash失敗など深刻な性能劣化を招く
  • 統計情報の精度・拡張統計・複合インデックスが改善の鍵
  • 多段JOINでは誤差が指数的に膨らむため要注意

「SQLが遅い時、SQLを疑う前に推定誤差を疑え」 これはDBAの基本的な考え方です。