「本番環境で急にクエリが重くなったけど、再現しない……」
エンジニアなら一度は味わったことがある、あの胃がキリキリする瞬間ですよね。スロークエリログを眺めても、肝心の「実行計画(EXPLAIN)」がわからなければ、インデックスを貼るべきか、クエリを書き直すべきかの判断もつかない。
そんな時、僕が迷わず「まずこれ入れよう」と後輩に勧めるのが、PostgreSQLの標準拡張機能である `auto_explain` です。今日は、現場で地味ながらめちゃくちゃ頼りになるこのツールの使い方を、実戦的な視点で解説します。
—
なぜ `EXPLAIN ANALYZE` を手動で叩くだけではダメなのか
通常、クエリの解析には `EXPLAIN (ANALYZE, BUFFERS) SELECT …` を使いますよね。でも、これには大きな弱点があります。
1. 再現性がない: たまたま特定のタイミングでロック待ちが発生していたり、メモリ状況が悪かったりする場合、手動で叩いた時には正常(高速)に動いてしまうことがよくある。
2. 本番データとの乖離: 手元の検証環境で実行しても、本番特有の膨大なデータ量や統計情報のズレによって、実行計画が異なってしまう。
結局、「その時、本番で何が起きていたか」を知るには、そのクエリが実行された瞬間の計画を自動で記録するしかない。それを実現するのが `auto_explain` なんです。
—
さっそくセットアップしてみよう
`auto_explain` はPostgreSQLの標準モジュールなので、追加のインストールは不要です。`postgresql.conf` に以下の設定を書き加えるだけで準備完了。
共有ライブラリに読み込ませる
shared_preload_libraries = ‘auto_explain’
実行時間が1秒以上かかったらログに出力する
auto_explain.log_min_duration = ‘1s’
実行計画の詳細(バッファのヒット率など)も出力する
auto_explain.log_analyze = true
auto_explain.log_buffers = true
ネストされたクエリ(関数内など)も出力する
auto_explain.log_nested_statements = true
設定を変えたら、`pg_ctl reload` を忘れずに。これだけで、以降1秒以上かかるクエリは、その実行計画と共にログファイルへ吐き出されるようになります。
—
現場で役立つ「使いこなしのコツ」
ただONにするだけだと、ログが溢れかえって大変なことになります。現場で使う際は、以下のポイントを意識してください。
1. 閾値(log_min_duration)の調整
最初から `100ms` とかに設定すると、高負荷時にログ出力そのものがボトルネックになりかねません。最初は `1s` くらいで様子を見て、徐々に絞っていくのが安全です。
2. ログの肥大化を防ぐ
`auto_explain.log_format = json` に設定すると、ログを後から解析しやすくなります。DatadogやCloudWatch Logsなどで集計するなら、JSON形式にしておくと「どのテーブルでシークが発生しているか」をグラフ化することも可能です。
3. 「実行中」ではなく「実行後」である点に注意
`auto_explain` はクエリが完了した時点でログを出します。つまり、デッドロックで永遠に終わらないクエリには反応しません。 それはまた別の監視(`pg_stat_activity` など)でケアする必要があります。
—
どんなログが出てくるのか?
設定が正しく入っていれば、ログにはこんな感じの出力が流れてきます。
LOG: duration: 1245.320 ms plan:
Query Text: SELECT FROM users WHERE status = ‘active’ AND last_login < '2023-01-01';
Seq Scan on users (cost=0.00..45231.12 rows=1200 width=128) (actual time=0.021..1240.501 rows=8000 loops=1)
Filter: (status = 'active' AND last_login < '2023-01-01'::date)
Rows Removed by Filter: 992000
Buffers: shared hit=450 read=800
これを見た瞬間、「あ、フルスキャン(Seq Scan)してるな。`status` と `last_login` に複合インデックスを貼れば解決しそうだな」と一瞬で判断がつきますよね。これが現場における「勘」を「確信」に変える瞬間です。
---
最後に:使い終わったら「OFF」にする勇気
`auto_explain` は非常に強力ですが、あくまでトラブルシューティング用です。常用するとオーバーヘッドは避けられません。
原因調査が終わったら、設定をコメントアウトして再読み込みする。あるいは、特定のセッションだけで有効にする(`SET auto_explain.log_min_duration = 0;` など)というテクニックを使い分けるのが、スマートなDBエンジニアの流儀です。
「ログを見たけど何もわからない」という闇雲な調査から卒業して、データに基づいた最適化を楽しみましょう。何か詰まったことがあれば、いつでも相談してくださいね!
コメント