【テクニカル・上級編】 統計情報とANALYZE – PostgreSQL

統計情報の「鮮度」と「解像度」:クエリプランナーを飼い慣らすための作法

PostgreSQLのクエリプランナーは、ある種の「職人」のようなものです。彼がどれだけ優秀なプランを提示できるかは、すべて「手元にある資料(統計情報)」の精度にかかっています。

現場で「なぜかインデックスが使われない」「NESTED LOOPが暴発している」というトラブルに遭遇したとき、多くのエンジニアはまず `EXPLAIN ANALYZE` を叩きます。しかし、プランナーの勘違いを正すためには、統計情報がどのように生成され、どう利用されているのかという「裏側」を理解しておく必要があります。

今日は、PostgreSQLの統計情報の核心に深く切り込んでみましょう。

—

pg_statistic の深淵:プランナーが見ている世界

クエリプランナーがコスト計算を行う際、最も頻繁に参照するのが `pg_statistic` です。このシステムカタログには、各カラムのデータ分布、nullの割合、頻出値(MCV: Most Common Values)、そしてヒストグラムデータが格納されています。

特に重要なのがヒストグラムです。これは、カラムの値の範囲を一定のバケットに分割し、データの密度を表現したものですが、ここにはプランナーのコスト試算を左右する「解像度」が存在します。

  • デフォルトの解像度: `default_statistics_target` は標準で100です。これはヒストグラムのバケット数が最大100であることを意味します。
  • チューニングの罠: データ分布が極端に偏っている(例えば、特定のIDにリクエストが集中するようなケース)場合、この100という解像度はあまりに粗すぎます。プランナーは「分布が均一である」という誤った前提でコスト計算を行い、結果として不適切なプランを選択します。

特定のカラムに対して `ALTER TABLE … ALTER COLUMN … SET STATISTICS 500;` といった調整を行うのは、プランナーに「もっと高精細な地図を渡す」行為に他なりません。無闇に上げれば `ANALYZE` の負荷が増大しますが、特定のホットカラムには必須の処方箋です。

—

ANALYZE のタイミング:バックグラウンドプロセスとの駆け引き

`autovacuum` が動いているから安心、というのは半分正解で半分間違いです。

`autovacuum` は、`autovacuum_analyze_scale_factor` に基づいて更新行数を検知し、統計情報を再収集します。しかし、バッチ処理で数百万行を一気に書き込むような環境では、この「しきい値」に達するまでの間、プランナーは古い統計情報を信じ切ってクエリを実行し続けます。これが「バッチ直後にシステムが急激に遅くなる」典型的な原因です。

熟練のエンジニアは、統計情報の鮮度を自分たちでコントロールします。

大量のDMLが確定したトランザクションの直後に、明示的に `ANALYZE` を発行するのは定石です。ただし、ここで注意すべきは「ロック」です。`ANALYZE` はテーブルに対して `ACCESS SHARE` ロックしか取りませんが、巨大なテーブルに対して実行すると、一時的にI/O負荷が跳ね上がります。

本番環境のピークタイムに、無策で `ANALYZE` を投げるのは自殺行為です。私は、バッチ処理の終了直後に `VACUUM ANALYZE` を実行し、統計情報の更新と不要領域の回収を同時に行うことで、次のサイクルへの準備を整えることを推奨しています。

—

プランナーが「嘘」をつくとき:相関関係の壁

もう一つ、深い話をしましょう。PostgreSQLの統計情報は基本的に「カラム単体」で管理されます。

つまり、`WHERE col_a = 1 AND col_b = 2` というクエリがあったとき、プランナーは「`col_a=1` の選択率」と「`col_b=2` の選択率」を別々に計算し、単純に掛け合わせます。もし `col_a` と `col_b` に強い相関関係(例:郵便番号と住所)がある場合、この計算は大きく外れます。

この「相関の無視」による推定ミスを回避するために、PostgreSQL 10以降では 拡張統計(Extended Statistics) が導入されています。

CREATE STATISTICS stats_col_ab ON col_a, col_b FROM my_table;

これを定義することで、プランナーは複合列の相関関係を意識したコスト計算が可能になります。もし、複雑な複合条件でプランが跳ねているなら、インデックスを追加する前に `CREATE STATISTICS` を検討してください。これが「職人」の腕の見せ所です。

—

最後に:統計情報は「生き物」である

データベースのパフォーマンスチューニングに銀の弾丸はありません。ある日完璧に見えたクエリプランも、データ量が増え、分布が歪めば、次の瞬間にはゴミのようなプランに成り下がります。

`pg_stats` ビューを定期的に眺め、`null_frac`(nullの割合)や `n_distinct`(ユニークな値の数)が、実態と乖離していないかを確認する。そして、クエリプランが期待通りに動かないときは、プランナーの「目の前の資料」が古くないか、解像度が足りていないかを疑う。

この「プランナーとの対話」を疎かにしないことこそが、PostgreSQLを極める最短ルートだと私は信じています。

皆さんのデータベースに、常に最適で安定したプランが降りてきますように。

コメント

タイトルとURLをコピーしました