「クエリが急に遅くなった」を防ぐ!PostgreSQL統計情報管理のリアルな処方箋
やあ。最近、データベースのパフォーマンスチューニングに追われてる?
「さっきまで爆速だったクエリが、急にフルスキャンを始めた」なんていう、悪夢のようなトラブルに遭遇したことはないかな。
PostgreSQLを長く使っていると、一度は必ずぶつかる壁がある。そう、「統計情報(Statistics)」だ。
今回は、教科書的なマニュアルには載っていない、現場で泥臭く生き抜くための統計情報管理の勘所について話そうと思う。大規模データセットを扱うとき、デフォルトの設定だけで戦おうとするのは、丸腰で戦場に行くようなものだからね。
—
なぜ「ANALYZE」が運命を握るのか
PostgreSQLのオプティマイザは、賢いようでいて実はかなりの「情報依存症」だ。クエリを実行する際、`pg_statistic` というテーブルにあるデータを頼りに、「インデックスを使うべきか、全表走査するべきか」を判断している。
この統計データが現実と乖離した瞬間、オプティマイザは平気で的外れな実行計画(プラン)を立てる。これが「急に遅くなる」の正体だよ。
まずは自動ANALYZEのチューニングから
PostgreSQLには `autovacuum` が付随する `autoanalyze` 機能があるけれど、デフォルトの設定をそのままにしておくのはおすすめしない。特に数千万行を超えるテーブルでは、デフォルトの閾値だと「統計情報が古すぎて使い物にならない」状態になることが多いんだ。
まずは、特定のテーブルだけ感度を上げてやろう。
— 特定テーブルの統計情報更新の閾値を下げる
ALTER TABLE large_orders SET (
autovacuum_analyze_scale_factor = 0.01, — 1%更新でANALYZE発動
autovacuum_analyze_threshold = 1000 — 少なくとも1000行更新で発動
);
こうすることで、データの変化に対してオプティマイザがより敏感に反応できるようになる。
—
「手動ANALYZE」の賢い使いどころ
じゃあ、常に `ANALYZE` を回せばいいのかというと、そうじゃない。`ANALYZE` はテーブルのサンプリングを行うため、それなりのCPUリソースを食う。高負荷な時間帯に重いテーブルへ闇雲に投げると、本番環境が悲鳴を上げることになる。
僕が推奨する運用ルールはこうだ。
1. バッチ処理の直後に叩く
ETL処理や大規模なデータインポートがあるなら、終わった直後の「静かな時間」に、必ず `ANALYZE` を実行すること。
2. `ANALYZE VERBOSE` をログに仕込む
cron等で回すときは、必ずログを残すようにして。どの程度サンプリングされたかが見えると、チューニングの精度が格段に上がるよ。
—
統計情報の「ロック」という裏技
ここからが少し高度な話。頻繁にデータを入れ替えるテーブルで、どうしても「特定の実行計画を維持したい」という場面があるとする。
通常、`ANALYZE` が走ると統計情報が更新され、プランが変わってしまうことがある。これを防ぐために、あえて統計情報を固定化するテクニックがあるんだ。
— 統計情報の更新を一時的に無視させる(応用編)
— ※基本はおすすめしないが、どうしてもプランを安定させたい時の最終手段
ALTER TABLE critical_table SET (
autovacuum_enabled = false — 慎重に検討すること!
);
※注意:これをやるときは、自分で責任を持って手動で `ANALYZE` を管理する覚悟が必要だよ。
—
統計情報の「精度」を調整する
デフォルトの統計情報の精度(`statistics target`)は、通常 `100` だ。だけど、特定のカラム(例えば、NULLが非常に多いカラムや、偏った分布を持つカラム)の検索精度が低いときは、この数値を上げてやると劇的にプランが改善することがある。
— カラム単位で統計情報の収集精度を上げる
ALTER TABLE orders ALTER COLUMN status SET STATISTICS 500;
ANALYZE orders;
`500` に上げれば、それだけ詳細なヒストグラムを作成してくれる。ただし、上げすぎると `ANALYZE` の実行時間が長くなるから、まずは `200` 〜 `300` あたりから試すのが大人の嗜みだ。
—
まとめ:現場のエンジニアへのアドバイス
結局のところ、DBのチューニングに「銀の弾丸」はないんだ。
- 観測する: `pg_stat_user_tables` を定期的に見て、いつ `last_analyze` が走ったか常に把握しておくこと。
- 影響を最小化する: 重要なバッチの後は手動で `ANALYZE` を行い、自動更新に頼りすぎない。
- プランを見る: `EXPLAIN ANALYZE` を見て、見積もり行数(`rows`)と実測行数(`actual rows`)の乖離が大きいなら、迷わず統計情報を疑う。
データベースは生き物だ。統計情報という「地図」が古ければ、どんなに優秀なオプティマイザも迷子になる。
君が管理しているDBという戦場で、常に最新の地図を渡してあげること。それが、優秀なDBエンジニアの仕事だよ。
また何か詰まったら、いつでも聞きに来てくれ。健闘を祈る!
コメント