統計情報は「クエリの羅列」ではなく「地図」である
PostgreSQLを触り始めて数年、あるいはDBAとして現場を渡り歩いてきた皆さんなら、一度は`ANALYZE`コマンドの挙動に泣かされたことがあるはずです。
「なぜ実行計画が急に悪化したのか?」
「なぜ自動ANALYZEが走ると、本番環境のCPUが跳ね上がるのか?」
多くのエンジニアにとって、`ANALYZE`は単なる「おまじない」かもしれません。しかし、クエリプランナ(Cost-Based Optimizer)にとって、`pg_statistic`に格納される統計情報は、未知の領域を航海するための「地図」そのものです。この地図が古ければ、どんなに高度なアルゴリズムも的外れなパスを選択します。
今日は、このエンジンの心臓部とも言える統計情報生成のメカニズムと、現場で遭遇する「罠」について、少し深掘りしてみましょう。
—
統計情報のサンプリング:その「精度」の境界線
`ANALYZE`が何をしているか。一言で言えば、「全件スキャンを避けるための近似」です。
PostgreSQLは、テーブルのサイズがどれほど巨大であっても、デフォルトでは特定の行数をランダムにサンプリングして、データの分布を推定します。ここで重要になるのが `default_statistics_target` です。
- デフォルト値(100): 多くのケースで十分ですが、データ分布に偏り(Skew)がある場合には不足します。
- 調整の勘所: 実行計画が「件数の見積もり」で大きく外している場合、まずは`ALTER TABLE … ALTER COLUMN … SET STATISTICS`で、特定のカラムの解像度を上げるのが定石です。
ここで注意すべきは、統計情報の「精緻さ」と「CPU負荷」のトレードオフです。統計ターゲットを1000まで上げれば見積もり精度は劇的に向上しますが、`ANALYZE`実行中の読み取り負荷や計算コストも増大します。盲目的に値を上げるのは、現場では悪手となることが多いですね。
—
自動ANALYZE(autovacuum)のトリガーをハックする
皆さんが頭を悩ませる「自動ANALYZE」のトリガー条件、改めて確認しましょう。
`autovacuum_analyze_threshold` と `autovacuum_analyze_scale_factor` がその正体です。
> 更新行数 = `threshold` + (`タプル総数` `scale_factor`)
ここでのポイントは、大規模テーブルであればあるほど、デフォルトの `scale_factor` (0.1 = 10%) は「遅すぎる」ということです。1億行のテーブルであれば、1,000万行の更新がないと走らない。これでは、統計情報は常に「過去の遺物」です。
現場でよく行うチューニング:
巨大なテーブルに対しては、この `scale_factor` を 0.01(1%)や0.005(0.5%)まで絞り込みます。ただし、頻繁に更新されるテーブルでこれをやりすぎると、`autovacuum`プロセスが常に張り付くことになり、I/Oのボトルネックを招きます。
「何をもって統計を更新すべきタイミングとするか」を、テーブルごとの更新特性(トランザクションの頻度やデータの重要度)に合わせて個別に設定する。これこそが、熟練のDBAとそうでない者の分かれ道です。
—
トラブルシューティング:統計情報が「嘘」をつく時
たまにありますよね。`EXPLAIN`で見た見積もり件数が「1」なのに、実際は「100万」返ってくるようなケース。
これの多くは「相関関係」の罠です。PostgreSQLの標準的な統計情報は、カラム単位で独立して保持されます。つまり、「Aカラムのこの値」と「Bカラムのこの値」が組み合わさった時にどれくらいの件数になるか、という多変量相関をデフォルトでは考慮できません。
そんな時に試すべきは、`CREATE STATISTICS` です。
CREATE STATISTICS stats_correlation (dependencies) ON col_a, col_b FROM my_table;
これを作成し、再度`ANALYZE`を走らせる。これでプランナは、カラム間の相関関係を考慮した計算を行うようになります。統計情報の設計は、単なる「更新の自動化」から「相関の定義」へと進化しているのです。
—
最後に:データベースと対話するということ
結局のところ、`ANALYZE`の最適化に魔法の杖はありません。
- 実行計画が怪しいと感じたら、まずは `pg_stats` を覗く。
- `n_distinct`(ユニーク数)や `most_common_vals`(頻出値)が、現実のデータ分布と乖離していないか疑う。
- それでもダメなら、統計ターゲットを調整するか、拡張統計を作成する。
PostgreSQLは、非常に正直なデータベースです。統計情報という「地図」さえ正しく渡してやれば、驚くほど賢いクエリパスを提示してくれます。
皆さんのデータベースが、今日も安定して最適なパスを選択し続けてくれることを願っています。次は、`VACUUM`の挙動とI/O負荷のバランス調整について、もう少し深い話をしましょうか。
コメント