「なぜプランナは間違えるのか」― `pg_statistic` という名の深淵を覗く
PostgreSQLのクエリチューニングにおいて、私たちは日々 `EXPLAIN ANALYZE` の出力と格闘しています。「期待したインデックスが使われていない」「Nested Loopが選ばれるはずが、なぜか巨大なHash Joinに……」。
そんなとき、多くのエンジニアはまず `ANALYZE` を実行し、それでも改善しなければインデックスを貼り直したり、クエリを書き換えたりします。しかし、最適化の「真の核心」は、テーブルのメタデータ、つまり `pg_statistic` にこそ隠されているのです。
今日は、プランナが「何を信じて」実行計画を立てているのか、その根源である `pg_statistic` について、少しディープに掘り下げてみましょう。
pg_stats と pg_statistic の決定的な違い
まず整理しておきましょう。私たちが普段 `SELECT FROM pg_stats` で見るビューは、あくまで人間が読みやすいように加工された「翻訳版」です。
本質的なデータはシステムカタログ `pg_statistic` に格納されています。ここには、ヒストグラムの境界値や、最も頻出する値(MCV: Most Common Values)の頻度など、プランナがコスト見積もりを行うための「生の情報」が詰まっています。
特に重要なのは、`stakind`(統計の種類)と `stanumbers`、`stavalues` の関係性です。これらは配列型で格納されており、人間がそのまま読むには骨が折れますが、ここから「データの偏り」や「相関関係」を読み解く能力こそが、熟練のDBエンジニアとそれ以外を分かつ境界線だと言っても過言ではありません。
なぜ「見積もり」は狂うのか
プランナが誤った判断をする最大の原因は、「情報の解像度」と「仮定の齟齬」にあります。
1. ヒストグラムの限界: `pg_statistic` のヒストグラムは、通常100個のビン(バケット)に分けられます。データ分布が非常に複雑、あるいはロングテールの分布をしている場合、この100個のバケットでは表現しきれない「微妙なズレ」が生じます。これが、大規模な結合におけるカーディナリティの見積もりエラーを誘発します。
2. 列間の相関: PostgreSQLのプランナは、デフォルトでは「カラム間の値は独立している」と仮定します。例えば `city` カラムと `zip_code` カラムがある場合、これらは明らかに強い相関がありますが、`pg_statistic` を個別に参照するだけでは、プランナはこの相関を理解できません。その結果、過小評価(Underestimation)が発生し、悲惨なNested Loopが選ばれることになります。
パフォーマンストラブルの解決策:統計情報の「解像度」をハックする
もし、特定のクエリで統計情報が追いついていないと感じたら、まず試すべきは `ALTER TABLE … ALTER COLUMN … SET STATISTICS` です。
デフォルトの「100」という値を、例えば「500」や「1000」に引き上げてみてください。これにより、`pg_statistic` に格納されるヒストグラムの粒度が細かくなり、より正確なコスト見積もりが可能になります。
ただし、注意してください。これは魔法の杖ではありません。
統計情報の精度を上げれば上げるほど、`ANALYZE` の実行時間は長くなり、カタログテーブルへの負荷も増大します。また、何でもかんでも数値を上げれば良いというものではなく、「どのカラムが見積もりのボトルネックになっているか」を `pg_stats` を見て突き止めるという、冷静な分析プロセスが不可欠です。
最後に:プランナと対話するということ
私が若手エンジニアによく言うのは、「プランナは嘘をつかないが、見ている景色が狭いだけだ」ということです。
`pg_statistic` を覗き込むことは、プランナと同じ景色を見ようと試みることです。クエリが遅いとき、ただ「書き方」を変えるのではなく、「この統計情報なら、プランナはこう見積もるしかないよな」と納得できるところまで突き詰めてみてください。
データベースの内部構造を理解し、統計情報と対話する。これこそが、PostgreSQLを「ただの保存場所」から「最強の武器」へと昇華させる唯一の道です。
皆さんのデータベースに、今日もしっかりとした統計情報が行き渡りますように。それでは、また。
コメント