「なぜかこのクエリだけ、急に遅くなるんだよな……」
現場でそんな悩みに直面したとき、実行計画(`EXPLAIN ANALYZE`)を見て、「よし、インデックスは効いてるな。あれ、でもなんでコストがこんなに高く見積もられてるんだ?」と首を傾げた経験はありませんか?
実は、PostgreSQLのクエリプランナが「どの道を通るのが一番速いか」を判断する際、唯一頼りにしているのが統計情報です。そして、その統計情報の心臓部こそが、今回深掘りするシステムカタログ`pg_statistic`です。
今日は、教科書には載っていない(というか、載っていても読み飛ばされがちな)「統計情報の裏側」について、実務的な視点から話してみようと思います。
—
1. pg_statsビューの「さらにその先」を見よう
皆さんが普段、手軽にデータの分布を確認するために使うのは`pg_stats`ビューですよね。
`SELECT FROM pg_stats WHERE tablename = ‘users’;` とか、よくやるはずです。
でも、ちょっと待ってください。`pg_stats`はあくまで人間が見やすいように加工された「読み取り専用ビュー」に過ぎません。その元データであり、プランナが直接脳みそとして使っているのが、システムカタログ`pg_statistic`です。
なぜわざわざ`pg_statistic`を意識する必要があるのか? それは、プランナが「何を勘違いしているか」を特定するためです。
2. なぜ統計情報が「ズレる」のか
PostgreSQLのプランナは、`pg_statistic`にあるヒストグラムを見て「この値は全体の何%くらい含まれているか」を推測します。しかし、現実はそう甘くありません。
- データ分布の偏り: 特定のIDにデータが集中している場合、単純なサンプリングでは誤差が大きくなります。
- 相関関係の欠如: カラムAとカラムBが連動している場合(例:「都道府県」と「市区町村」)、プランナはそれぞれ独立していると仮定して計算してしまい、見積もりが大きく外れることがあります。
こういう時、プランナは「あ、これなら全件走査(Seq Scan)したほうが速そうだぞ」と、とんでもない判断を下します。これが「急に遅くなる」の正体です。
3. 実務での調査手順:プランナの脳内を覗く
もしクエリが遅いと感じたら、まずは`pg_statistic`を直接覗いてみましょう。
— 特定のカラムの統計情報を確認
— stakind1は、統計の種類(1は頻出値、2はヒストグラムなど)
SELECT
attname,
stakind1,
stavalues1,
stanullfrac,
stadistinct
FROM pg_statistic s
JOIN pg_attribute a ON a.attrelid = s.starelid AND a.attnum = s.staattnum
WHERE starelid = ‘users’::regclass;
ここで注目してほしいのが `stadistinct` です。ここが実際のデータ数と大きく乖離しているなら、プランナは「重複が少ない」と勘違いして、インデックスを有効に使わない選択をしてしまいます。
—
4. 現場で使える「泥臭い」解決策
統計情報がズレていると分かったら、どう対処すべきか。いきなり「全部`ANALYZE`すればいい」なんて安易なことは言いません。本番環境の巨大なテーブルでそれをやると、逆に負荷が跳ね上がりますからね。
① `ALTER TABLE … SET STATISTICS` で精度を上げる
特定のカラムだけヒストグラムのバケット数を増やしましょう。デフォルトは100ですが、これを最大1000まで増やせます。
— データの偏りが激しいカラムに対して精度を上げる
ALTER TABLE users ALTER COLUMN status_code SET STATISTICS 500;
ANALYZE users;
これだけで、プランナの「視力」が劇的に良くなることがあります。
② 多列統計(Extended Statistics)を使う
これ、意外と知られていないんですが、最強の武器です。「カラムAとBの組み合わせで検索することが多い」なら、これを使わない手はありません。
— 複数のカラム間の相関関係を学習させる
CREATE STATISTICS users_geo_stats (dependencies) ON city_id, prefecture_id FROM users;
ANALYZE users;
これを作っておくと、プランナは「あ、この都道府県ならこの市町村が来るのは必然だな」と理解し、見積もりの精度が跳ね上がります。
—
最後に:データベースと「対話」しよう
データベースのパフォーマンスチューニングは、機械的な設定変更の積み重ねではありません。「プランナがなぜその判断を下したのか」という、データベースの思考プロセスを追いかける「対話」なんです。
`pg_statistic`は、その対話のログであり、設計図です。もしクエリプランナと話が噛み合わないなと思ったら、まずはこのテーブルを覗いてみてください。きっと、プランナが「だって、こんなデータ分布だと思ってたんだもん!」と弁解している声が聞こえてくるはずですよ。
また次回、もっとディープな現場のネタでお会いしましょう。チューニング、楽しんで!
コメント