統計情報は「羅針盤」だ。`track_counts`を軽視してはいけない理由
PostgreSQLのパフォーマンスチューニングを語るとき、多くのエンジニアはまず`EXPLAIN ANALYZE`や`pg_stat_statements`に目を向けます。しかし、その土台となる「統計情報」の鮮度や精度が担保されていなければ、どんな高度なクエリプランも空回りしてしまう。
今回は、PostgreSQLのクエリチューニングにおいて、空気のように当たり前でありながら、実は非常に深い意味を持つ設定項目`track_counts`について少し掘り下げてみたいと思います。
—
なぜ `track_counts` がすべての出発点なのか
結論から言えば、`track_counts` がオフになっているPostgreSQLは、視力を失ったパイロットのようなものです。
この設定を有効にすると、PostgreSQLはテーブルやインデックスへのアクセス(スキャン回数、タプル挿入・更新・削除数など)を追跡し始めます。これらは単なる「数字」ではありません。Autovacuumの起動タイミングを決定し、クエリプランナが「シーケンシャルスキャンを選ぶか、インデックススキャンを選ぶか」という生死を分ける判断を下すための、極めて重要な「根拠」なのです。
もし本番環境でこの設定をオフにしているなら、それは致命的なリスクを背負っていると同義です。
内部アーキテクチャ:統計コレクタの裏側
`track_counts`が有効なとき、バックエンドプロセスは統計情報を直接カタログに書き込むわけではありません。もしそうすれば、すべてのクエリが統計の更新のために競合を起こし、システム全体がロックの嵐に飲まれてしまうからです。
代わりに、PostgreSQLはUDPパケットを使って「統計コレクタプロセス」に統計情報を送信します。
1. バックエンドプロセスがクエリを実行。
2. 内部カウンタをインクリメントし、情報をコレクタへ送信。
3. 統計コレクタプロセスが一時ファイル(`pg_stat_tmp`ディレクトリ下)に情報を蓄積。
4. `pg_stat_user_tables` などのビューを参照する際、この一時ファイルの内容が読み込まれる。
このアーキテクチャの素晴らしい点は、「統計情報の更新がメインのクエリ実行パスを阻害しない」ように設計されていることです。しかし、裏を返せば、負荷が極端に高い環境ではこのUDPパケットの欠落や、コレクタプロセスの処理遅延が、統計情報の「ズレ」を招く可能性があることにも注意が必要です。
「統計が古い」という罠:現場でのトラブルシューティング
僕が現場でよく遭遇するパフォーマンス低下の相談の多くは、実は`track_counts`そのものよりも、「その統計をどう解釈するか」のズレにあります。
たとえば、大量の更新が走るテーブルで、統計情報の更新が追いついていない(あるいは`autovacuum`が起動しきれていない)場合、プランナは「テーブルはまだ小さい」と誤認します。結果、プランナは「インデックスを使うよりフルスキャンの方が速い」という誤った判断を出し、システムが突如として重くなる。
このトラブルに遭遇したとき、僕は以下のポイントをチェックします。
- `pg_stat_user_tables.n_mod_since_analyze` を見る:
前回のANALYZEからどれだけの行が変更されたか。これが閾値を超えてもAutovacuumが動いていないなら、統計収集のサイクルに問題があります。
- `pg_stat_activity` との相関:
特定のバックエンドが統計情報の送信で詰まっていないかを確認します。
- UDP通信のボトルネック:
稀なケースですが、I/O負荷が高すぎて`pg_stat_tmp`への書き込みがボトルネックになることがあります。その場合は、`stats_temp_directory`を高速なRAMディスク(tmpfs)に移すというチューニングも選択肢に入ります。
エンジニアとしての心構え
「設定をオンにして終わり」ではありません。真に熟練したエンジニアは、統計情報が「いつ、どのようなタイミングで更新され、現在のクエリプランにどう反映されているか」というプロセスの流れを頭の中で可視化しています。
PostgreSQLは非常に正直なデータベースです。統計情報という「鏡」を綺麗に保ちさえすれば、必ず最適なプランを返してくれます。もしあなたのDBが期待通りに動いていないなら、まずは `track_counts` が正しく機能し、統計情報が今のデータの実態を正しく反映しているか、今一度確認してみてください。
技術の深淵は、こうした地味な設定の積み重ねの先にあるのです。
コメント