PostgreSQLのJITは「諸刃の剣」である:パラメータ調整で戦うための勘所
PostgreSQL 11でJIT(Just-In-Time)コンパイルが導入されたとき、多くのエンジニアは「これでクエリの実行速度が劇的に変わる」と期待したはずです。確かに、複雑な式評価や集計処理において、LLVMを用いた機械語への変換は驚異的なパフォーマンス向上をもたらしました。
しかし、現場で運用していると気づくはずです。「なぜかJITが動くと、かえって遅くなるクエリがある」と。
今日は、PostgreSQLのJITチューニングにおいて、教科書的な説明の先にある「現場のリアリティ」について話をしようと思います。
—
JITのオーバーヘッドという「隠れたコスト」
まず大前提として、JITコンパイルは無料ではありません。クエリプランに対してLLVMを呼び出し、機械語を生成する時間(コンパイル時間)が必要です。
もしそのクエリが数ミリ秒で終わるような軽量なものだったらどうでしょう? 実行時間の大部分が「機械語生成」に費やされ、肝心のSQLの実行効率が上がっても、トータルのレスポンスタイムは悪化します。これが、我々がJITパラメータを慎重に扱うべき最大の理由です。
制御の要:3つの閾値と向き合う
PostgreSQLのJIT制御において、特に意識すべきは以下の3つのパラメータです。
- `jit_above_cost`
- JITコンパイルを行うかどうかの最大の判定基準です。デフォルトは100,000。プランナーが見積もったコストがこの値を超えると、JITが発動します。
- `jit_inline_above_cost`
- 関数呼び出しをインライン化するかどうかの閾値です。インライン化は実行速度を上げますが、コンパイルコストも増大させます。
- `jit_optimize_above_cost`
- LLVMの最適化パスをどれだけ深くかけるかという基準です。これもまた、コンパイル時間の増加と引き換えに実行効率を追求します。
現場でのチューニングの指針
もしあなたのシステムで「特定のクエリだけJITのせいで遅い」という現象が起きているなら、まずはそのクエリの `EXPLAIN (ANALYZE, VERBOSE)` を取ってみてください。
もし「JIT: on」となっていて、コンパイル時間が実行時間の大半を占めているなら、`jit_above_cost` を引き上げるべきです。デフォルトの100,000はあくまで汎用的な値であり、ハードウェア環境やワークロードの性質によっては、もっと高い値(例えば 200,000 や 500,000)を設定した方が、システム全体のレイテンシが安定することは珍しくありません。
パフォーマンストラブルシューティングの落とし穴
私が現場でよく遭遇するのは、「統計情報の不備」に起因するJITの誤判定です。
PostgreSQLはコストベースのオプティマイザ(CBO)です。統計情報が古く、実際の行数よりも過小に見積もられている場合、本来ならJITで高速化すべき複雑なクエリがJITの対象外になり、逆に非常に単純なクエリに対してJITが走り、オーバーヘッドで沈没する……といった現象が起きます。
JITのパラメータをいじる前に、まずは `ANALYZE` を実行し、統計情報を最新に保つ。これが、どんな高度なチューニングよりも先にやるべき「基本中の基本」です。JITの挙動がおかしいと感じたら、まずは「プランナーは本当にそのクエリが重いと判断しているのか?」を疑うのが、熟練エンジニアの視点です。
結論:JITとどう付き合うか
個人的な意見ですが、JITは「数秒以上かかるような重い分析クエリ」には最高の武器になります。一方で、OLTPのような「短時間で大量に発行されるクエリ」にとっては、過度なJITはノイズになり得ます。
もし皆さんの環境がOLTP寄りであれば、あえて `jit = off` にしてベースラインを測ってみるのも一つの勇気ある決断です。逆にDWH用途であれば、`jit_above_cost` を下げて、より積極的にJITを働かせるチューニングが正解になるでしょう。
JITは「魔法の杖」ではありません。パラメータをいじる際は、必ず「どのクエリが、どの程度のコンパイル時間を費やし、結果としてどれだけの実行時間短縮を得ているのか」を計測してください。
PostgreSQLの深淵は、こうした地道な計測と、論理的な推論の先にあります。皆さんの環境でも、ぜひ一度、JITのコストバランスを見直してみてください。新しい発見が、そこにあるはずです。
コメント