Article
PostgreSQL JIT障害を2つのGUCで切り分ける
PostgreSQLのJITでクラッシュや誤った結果が疑われるとき、jit_expressionsとjit_tuple_deformingを使って式コンパイルとタプルデフォーミングを切り分ける方法が紹介されました。2つの設定の非対称な関係と、安全な再現テストの進め方を整理します。
Share
こはるの読みどころ
JITを丸ごと無効化して終わらず、どの処理経路まで絞れるかを順番に見ていくよ。性能調整用の設定と混同しないこともポイントだね。

PostgreSQLのクエリが特定の条件でクラッシュしたり、期待と異なる結果を返したりしたとき、jit = offで症状が消えればJITを疑えます。では、その先はどこまで絞り込めるのでしょうか。
2026年8月15日に公開された技術記事では、jit_expressionsとjit_tuple_deformingを順番に切り替え、JIT内部の二つの処理経路を分けて試す方法が示されました。どちらも真偽値ですが、独立した対称なスイッチではありません。
この非対称性を理解すると、単に「JITを切ったら直った」で止まらず、再現条件と報告内容を一段具体的にできます。設定の意味から、三段階の再現テスト、プリペアドステートメントでの注意点までつなげて見ていきます。
PostgreSQL JITは式評価とタプルデフォーミングを高速化する
PostgreSQLのJITは、実行時に汎用的な処理をネイティブコードへ変換します。PostgreSQL 18の公式文書が挙げる高速化対象は、式評価とタプルデフォーミングの二つです。
式評価は、WHERE句、ターゲットリスト、集約、射影などを処理します。クエリ固有の式に合わせたコードを生成し、汎用的な評価処理のオーバーヘッドを減らす仕組みです。
タプルデフォーミングは、ディスク上の行を、Executorが扱えるメモリ上の値へ展開する処理です。テーブルの行レイアウトと取り出す列数に合わせた関数を生成することで、NULLビットマップ、可変長値、アライメントを扱う汎用処理を減らします。
このJIT機能はPostgreSQL 11で2018年10月18日に導入され、当時は対応ビルドでも既定で無効でした。PostgreSQL 12では2019年10月3日から、JIT対応でビルドされたサーバの既定が有効になっています。現行のPostgreSQL 18でも、jit_expressionsとjit_tuple_deformingはどちらも既定値がonです。
jit_expressionsを切るとデフォーミングも動かない
二つの設定名だけを見ると、式評価とタプルデフォーミングを別々に有効化できそうです。実際には、タプルデフォーミング用コードは、デフォームした行を使う式をコンパイルする過程で生成されます。
そのため、jit_tuple_deforming = offにすると、式のJITコンパイルを残したままデフォーミングだけを外せます。一方、jit_expressions = offにするとJITプロバイダへ式コンパイルを依頼する処理自体が止まり、jit_tuple_deforming = onでもデフォーミングだけは実行されません。
現在のPostgreSQLソースコードのjit_compile_expr()も、実行状態にPGJIT_EXPRがなければプロバイダを呼ぶ前に戻ります。プラン作成側では式とデフォームのフラグを設定値から組み立てるため、Expressions false, Deforming trueは設定値として作れても、デフォーミングだけが動く実行状態にはなりません。
ここが切り分けの軸です。jit_expressionsを直接切るのではなく、まずJIT全体を外し、次に式コンパイルを残したままjit_tuple_deformingだけを外します。
三段階の再現テストでJITの失敗経路を絞る
最初に、対象セッションの状態を保存します。SHOWの結果、PostgreSQLのバージョン、実行したクエリ、パラメータ、エラーや誤結果をセットで残すと、後から条件を再構成しやすくなります。
SHOW server_version;
SHOW jit;
SHOW jit_expressions;
SHOW jit_tuple_deforming;
次に、同じクエリとデータで三つの状態を比較します。設定はセッション単位のSETで変更できるため、サーバ再起動は不要です。
-- 1. 基準: JITと二つの処理を有効にする
SET jit = on;
SET jit_expressions = on;
SET jit_tuple_deforming = on;
-- ここで再現クエリを実行
-- 2. JIT全体を無効にする
SET jit = off;
-- 同じ再現クエリを実行
-- 3. 式コンパイルを残し、デフォーミングだけを無効にする
SET jit = on;
SET jit_expressions = on;
SET jit_tuple_deforming = off;
-- 同じ再現クエリを実行
-- 調査後はセッション設定を戻す
RESET jit;
RESET jit_expressions;
RESET jit_tuple_deforming;
結果は次のように読みます。
jit = offでも症状が続く: JITだけでは再現条件を説明できません。別の実行経路も調べます。jit = offで消え、jit_tuple_deforming = offでも消える: タプルデフォーミングのJIT経路まで範囲を絞れます。jit = offで消えるが、jit_tuple_deforming = offでは続く: デフォーミング以外のJIT経路、つまり式コンパイル側を中心に調べられます。
JITが使われたかはEXPLAINのJITブロックで見られます。公式文書の例では、Options行にExpressionsとDeforming、Timing行にコード生成時間が表示されています。ただし、関数数や時間はクエリ、データ、バージョン、環境に依存するため、固定値を正常性の基準にはできません。
EXPLAIN ANALYZEと本番環境では再現テストの副作用を避ける
jit_expressionsとjit_tuple_deformingは公式文書の「Developer Options」に置かれ、同ページは開発者テスト用の設定を本番データベースで使わないよう明記しています。まず検証環境、読み取り専用の複製、または影響を限定できるセッションで再現するのが前提です。
EXPLAIN (ANALYZE, SETTINGS)は、実行統計と既定値から変えたプラン関連設定を一緒に残せます。一方で、ANALYZEを付けると対象文が実際に実行されるため、クラッシュするクエリや書き込み文をそのまま本番で再実行する用途には向きません。単なるEXPLAINでは実行時のJITブロックを確認できない点とのトレードオフがあります。
誤結果を調べる場合は、JITのオン・オフ以外の条件も固定します。データのスナップショット、セッション設定、SQLパラメータ、拡張機能、PostgreSQLとLLVMのバージョンが変わると、同じ見た目のクエリでも別の比較になってしまいます。
プリペアドステートメントは設定変更後にプランを作り直す
PostgreSQLは、JITを使うか、どこまで最適化するかをプラン作成時に判断します。公式文書は、プリペアドステートメントが汎用プランを使う場合、実行時ではなくプラン作成時のJIT関連設定が判断を左右すると説明しています。
アプリケーションの接続プールやサーバサイドPREPAREを使う環境では、SETだけを変えて既存プランを再実行すると、意図した比較にならない可能性があります。検証では設定変更後に新しい接続を使うか、対象のプリペアドステートメントをDEALLOCATEしてから作り直します。
DEALLOCATE reproduce_query;
SET jit = on;
SET jit_expressions = on;
SET jit_tuple_deforming = off;
PREPARE reproduce_query AS
SELECT /* 再現に必要なクエリ */ 1;
クライアントライブラリが自動的にプリペアする場合は、その閾値やプランキャッシュの動作も調査記録へ含めたいところです。設定を変えた時刻だけでなく、どの接続でどのプランを作ったかが比較の単位になります。
性能調整ではjit_above_costかJIT全体を評価する
この二つのGUCは、JITのどの処理で問題が起きるかを調べるための診断スイッチです。式コンパイルやデフォーミングを部分的に常時無効化し、コンパイル時間を細かく削るためのチューニング項目として扱うと、JITが本来持つ処理経路を欠いた状態が残ります。
性能が問題なら、先にクエリがJIT対象になった理由を見ます。PostgreSQLは推定総コストをjit_above_costと比較し、さらにjit_inline_above_costとjit_optimize_above_costでインライン化と高コストな最適化を判断します。現行の設定文書では、既定値は順に100000、500000、500000です。
短いクエリではコード生成の費用が短縮時間を上回ることがある一方、長時間のCPUバウンドな分析クエリではJITが有利になり得ます。したがって、性能対策は実ワークロードを測定し、コスト閾値を調整するか、必要ならjit = offを選ぶ問題です。障害切り分けの二つのスイッチとは役割を分けます。
最初の問いへの答えは明快です。jit = offでJIT全体を判定し、続いてjit_tuple_deforming = offでデフォーム経路だけを外せば、「JITかどうか」から「どちらの処理経路か」へ進めます。非対称な依存関係を踏まえた順番こそ、再現テストとバグ報告を具体的にするポイントです。
出典
- Title: Christophe Pettus: All Your GUCs in a Row: jit_expressions and jit_tuple_deforming
- URL: https://postgr.es/p/9sn
Share
Related Articles
カテゴリやタグが近い記事を続けて読めるように並べています。




