PostgreSQLのJITコンパイルをやさしく解説(入門編)

2025年9月16日火曜日

PostgreSQL

t f B! P L

JIT(Just-In-Time)は、重いSQLの“計算部分”をその場で機械語にコンパイルして速くするしくみです。I/Oがボトルネックの小さなクエリには効きにくいけど、大きめの集計・結合・フィルタでは有効です。

JITって何?なぜ速くなるの?

PostgreSQLのJITは、SQLを実行する中で発生する式評価(WHERE句やJOIN条件、投影、集計関数など)の処理を、実行時にLLVMを使って最適化された機械語に変換します。
これにより、以下のような “CPUでコツコツ計算する” ところが速くなります。

  • 複雑なWHERE条件の評価
  • JOIN時のキー比較
  • 集計(GROUP BY、集約関数)
  • たくさんの行に対する式計算(CASE式、関数呼び出し 等)

逆に、ディスクI/Oがメインのなエリ(インデックス1件取りなど)では、JITの“コンパイルにかかるコスト”のほうが上回ってしまい、効果が出にくいです。

まずは現状確認(JITが使える?使われてる?)

-- いまJITがオンかどうか
SHOW jit;

-- JIT関連の全設定を一覧
SELECT name, setting, unit, short_desc
FROM pg_settings
WHERE name LIKE 'jit%';

-- 実行計画でJITが働いたかを確認(JITセクションが出ればOK)
EXPLAIN (ANALYZE, BUFFERS, SUMMARY) 
SELECT count(*)
FROM big_table t
JOIN big_table2 s USING(key)
WHERE t.colA > 100 AND s.colB LIKE 'X%';

EXPLAIN結果にこんなセクションが出ていれば、JITが実際に使われています。

JIT:
  Functions: 6
  Options: Inlining true, Optimization true, Expressions true, Deforming true

補足:JITを使うにはPostgreSQLがLLVMサポート付きでビルドされている必要があります。配布物やOSによっては無効の場合があるので、まずは SHOW jit; で確認しましょう。

まずは3つの設定だけ覚えればOK(入門の設定)

JITはしきい値(コスト)を超えるクエリにだけ働かせるのがコツです。はじめは「重いクエリだけJIT」を目指しましょう。

  • jit … JITの有効/無効(on/off)
  • jit_above_costこのコストを超えたらJITを使う
  • jit_optimize_above_cost / jit_inline_above_cost … さらに重い時だけ最適化/インライン展開

おすすめの設定(postgresql.conf or セッション)

-- まずはJITを有効にする
SET jit = on;

-- “ほんとうに重い” クエリだけJIT(しきい値は高めから)
SET jit_above_cost = 200000;
SET jit_inline_above_cost = 300000;
SET jit_optimize_above_cost = 500000;

ポイント
しきい値(cost)は高めから始めて、効果を見ながら下げるのが安全。
小さなコストのクエリにJITを当てないための保険です。

クエリ単位でオン/オフする

-- このトランザクション内のクエリだけJITをオフ
BEGIN;
SET LOCAL jit = off;
SELECT ...;  -- 小さなOLTP向け
COMMIT;

-- 逆に、このクエリはJITを強制オンにしたい
SET LOCAL jit = on;
SELECT ...;  -- 重い集計

JITはどんな時に効く?

効きやすいケース

数百万~数千万行レコードを参照する大規模集計に有効です。 複雑な式評価や多段のJOINにも効果的です。 I/Oが十分速い(メモリヒットが多い、キャッシュが効く、NVMe等)場合、効果的です。

効きにくい(逆効果となる)ケース

対象が少ないレコード・コストの低いレコードは逆にコンパイルの手間の方に時間がかかる可能性があります。 I/O待ちが支配的なクエリでは効果がありません。(ディスクが遅い、統計が古くプランが悪い) 頻繁に形の違うSQLを都度投げるケースでは毎回コンパイルコスト発生し、場合によっては逆効果です。

かんたん実験レシピ(ベンチの取り方)

  1. 代表的な重いSQLを1つ選ぶ(業務で実際に遅いもの)

  2. 統計更新して計画ブレを減らす

    ANALYZE;
    
  3. JITオフで計測

    SET jit = off;
    EXPLAIN (ANALYZE, BUFFERS, SUMMARY) <重いSQL>;
    
  4. JITオン + しきい値低めで計測

    SET jit = on;
    SET jit_above_cost = 0; -- テスト目的で強制的に使わせる
    EXPLAIN (ANALYZE, BUFFERS, SUMMARY) <同じSQL>;
    
  5. 実行時間と JIT: セクションを比較。
    短縮率が十分なら、本番では jit_above_cost高めに戻し、“重い時だけJIT” に落とし込む。

よくある疑問と落とし穴

Q. インデックス検索も速くなりますか?
A. いいえ。JITは計算を速くします。I/Oやインデックス探索自体は別の話。まずはインデックス設計・統計の鮮度を整えること。

Q. 並列実行と一緒に使えますか?
A. 使えます。並列×JITは相性が良く、重い集計でさらに効くことがあります。ただしコンパイル時間も増えるため、しきい値の調整は必須。

Q. メモリは足りますか?
A. JIT自体のメモリは小さめですが、重いクエリ=work_mem消費が増えがち。work_memmaintenance_work_memなど全体設計の見直しも並行して行いましょう。

現場投入のミニ・ガイド(まずはこれだけ)

  1. 可用性チェックSHOW jit;pg_settings で使えるか確認
  2. 段階導入jit = on にして、jit_above_cost高め(例:200,000)に
  3. 効果測定:重い代表SQLを JIT on/off でEXPLAIN ANALYZE比較
  4. しきい値調整:効果が高いなら jit_above_cost を少し下げて適用範囲を広げる
  5. ピンポイント運用:小さなOLTPは SET LOCAL jit = off; でガード

まとめ

JITは「重い計算を速くするスイッチ」です。重いSQLの“計算部分”をその場で機械語にコンパイル、計算速度を早くします。I/Oが多い小さなクエリには効きにくいため、しきい値を高めにして重い処理だけ当てるようにしましょう。

EXPLAIN (ANALYZE)JITセクションで “効いているか” を必ず確認し、不要なコンパイルが走らないようにしましょう。小さく(コスト高めのSQLのみ)導入して、効果の高いところに集中適用するのが現場最適です。

チートシート

-- 現状確認
SHOW jit;
SELECT * FROM pg_settings WHERE name LIKE 'jit%';

-- とりあえずオン(重い時だけ)
SET jit = on;
SET jit_above_cost = 200000;
SET jit_inline_above_cost = 300000;
SET jit_optimize_above_cost = 500000;

-- クエリ単位の切り替え
SET LOCAL jit = off;  -- 小さいOLTP
SET LOCAL jit = on;   -- 重い集計

-- 実測(JITが効いたか確認)
EXPLAIN (ANALYZE, BUFFERS, SUMMARY) <SQL>;
スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ