「統計情報が古い」でクエリが死ぬ前に。PostgreSQLのautovacuumと仲良くなる方法
やあ。データベースの運用、順調かな?
今日はPostgreSQLの縁の下の力持ち、というか「気づかないうちにめちゃくちゃ働いてくれているけど、たまに機嫌を損ねると大事故を起こす」アイツ、autovacuumの話をしようと思う。
特に「クエリが急に遅くなった!」という相談を受けたとき、原因の8割は統計情報の鮮度が落ちていることにある。オプティマイザは「今のテーブルはスカスカだ」と思っているのに、実際は数百万行に増えていた……なんて悲劇、現場ではあるあるだよね。
今日は、autovacuumがどうやって統計情報を守っているのか、そして「ここだけは触っておけ」という実践的な勘所を、現場の視点で共有するよ。
—
なぜ「自動」のANALYZEが必要なのか
まず前提として、PostgreSQLのクエリオプティマイザは「統計情報」という名の地図を見て、実行計画を立てる。テーブルに何行入っているか、値がどれくらい偏っているか。この地図が古ければ、当然、無謀なフルスキャンを選ぶし、インデックスも無視される。
通常、`ANALYZE`コマンドを手動で叩けば統計は更新されるけど、運用中のシステムでいちいちそれを手動で行うのは現実的じゃない。そこで登場するのが、autovacuumプロセスがバックグラウンドでやってくれる自動ANALYZEだ。
autovacuumが動き出す「閾値」の仕組み
autovacuumが「お、そろそろ統計情報を更新するか」と判断する基準は、主にこの2つのパラメータで決まる。
- autovacuum_analyze_scale_factor: テーブルの行数の何%が変更されたらANALYZEするか(デフォルトは0.1=10%)
- autovacuum_analyze_threshold: 変更された行数の最小値(デフォルトは50行)
つまり、「テーブルの10%が書き換わるか、あるいは50行以上変更されたらANALYZEするよ」というルールだね。
でも、ここで落とし穴がある。「1億行あるテーブル」を想像してみてほしい。
デフォルトの `0.1` のままだと、1,000万行更新されるまでANALYZEが走らない。これじゃあ、途中でデータの分布がガラリと変わったときに、実行計画が最適化されなくて悲惨なことになるよね。
—
実践的なチューニング:ここを調整せよ
大規模なテーブルや、書き込みが激しいテーブルに対しては、デフォルト設定はちょっと大雑把すぎる。僕が現場でよくやるのは、テーブルごとに個別に閾値を調整することだ。
— 大規模テーブル(例:orders)の設定を調整する例
ALTER TABLE orders SET (
autovacuum_analyze_scale_factor = 0.01, — 1%の変更でANALYZEを発火させる
autovacuum_analyze_threshold = 1000 — 最低でも1000行は変わってからにする
);
こうすることで、テーブルが大きくなっても「統計情報が古すぎてクエリが死ぬ」リスクをぐっと下げられる。
「統計情報が更新されてるか」を確認する魔法のクエリ
「今、ちゃんと統計情報は最新なのか?」と不安になったら、まずはこれを見てほしい。
SELECT
relname AS table_name,
last_analyze,
last_autoanalyze,
n_dead_tup
FROM pg_stat_user_tables
WHERE relname = ‘orders’;
`last_analyze` や `last_autoanalyze` の日付を見て「あれ、先週から止まってる?」なんてことがあれば要注意だ。また、`n_dead_tup`(削除されたり更新されたりした行数)が異常に溜まっていないかも、合わせてチェックしておこう。
—
最後に:先輩からのアドバイス
autovacuumは「守り」の機能だけど、ここを理解しているかどうかで、データベースの安定感は段違いになる。
ただし、一つだけ注意点。「何でもかんでも厳しくANALYZEさせればいい」というわけじゃない。 頻繁すぎるANALYZEはCPUやI/Oを消費する。特に書き込み負荷が高いシステムでは、バランスを見極めるのがエンジニアの腕の見せ所だ。
まずは、ログを見て「バキュームが追いついていないな」とか「統計情報の更新間隔が長すぎるな」と感じるテーブルから、個別に調整を試してみてほしい。
何か困ったことがあったら、いつでも聞いてくれ。データベースは正直だから、ちゃんと手入れをしてあげれば、必ず期待に応えてくれるよ。
それじゃ、良い開発ライフを!
コメント