本文通过可复现的 SQL 与配置核查,帮助团队在 PostgreSQL 16 环境下正确启用并行查询与 JIT(即时编译),并用查询计划与运行时统计验证效果与正确性。


## 环境前提与版本核查


  • 版本:PostgreSQL 16(`SELECT version();` 验证)。
  • 扩展:默认无需额外扩展;JIT 由 LLVM 支持,打包版通常已包含。
  • 内核参数:建议在压测与生产前固定 `work_mem` 与并行相关参数,以便复现对比。

核查关键参数(以实际环境为准):


SHOW max_parallel_workers;
SHOW max_parallel_workers_per_gather;
SHOW parallel_setup_cost; -- 并行初始化开销估算
SHOW parallel_tuple_cost; -- 并行传输元组开销估算
SHOW jit;                 -- on/off
SHOW jit_above_cost;      -- 超过该开销阈值启用 JIT
SHOW work_mem;            -- 单算子内存,影响 Hash/Sort 并行效果

## 正确启用并行与 JIT(配置与会话级)


会话级快速测试:


SET max_parallel_workers_per_gather = 2; -- 按核数与负载调整
SET jit = on;                            -- 如需关闭则设为 off

持久化配置(`postgresql.conf`):


# 并行相关(按 CPU 核数与业务并发评估)
max_parallel_workers = 8
max_parallel_workers_per_gather = 2
parallel_setup_cost = 1000
parallel_tuple_cost = 0.1

# JIT(在复杂查询或聚合场景通常收益明显)
jit = on
jit_above_cost = 100000

注意:上述数值为常见参考,务必以 `SHOW` 与实际压测结果为准;不同数据规模与查询形态下最优点不同。


## 可复现示例与计划解读


示例表与数据(规模可按需扩大):


CREATE TABLE sales (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  amount NUMERIC(12,2) NOT NULL,
  created_at TIMESTAMP NOT NULL
);

-- 生成样例数据(示意,实际可用批量插入或复制)
INSERT INTO sales (user_id, amount, created_at)
SELECT (random()*100000)::BIGINT, (random()*1000)::NUMERIC(12,2), NOW() - (random()*interval '30 days')
FROM generate_series(1, 200000);

CREATE INDEX idx_sales_user_created ON sales(user_id, created_at);

并行聚合 + JIT 验证:


EXPLAIN (ANALYZE, BUFFERS, JIT)
SELECT user_id, sum(amount)
FROM sales
WHERE created_at >= NOW() - interval '7 days'
GROUP BY user_id;

解读要点(输出中应能看到以下信号):


  • `Gather`/`Gather Merge` 节点:表示触发并行;其下应有多个 `Parallel ...` 子节点。
  • `Workers Planned` 与 `Workers Launched`:计划与实际启动的并行 worker 数。
  • `JIT` 段落:显示 `Functions`、`Options` 与 `Timing`,确认 JIT 是否启用及耗时占比。
  • `Buffers`:读写块统计,可用于对比冷热数据与 IO 压力。

## 诊断:为何未触发并行或收益不佳


  • 查询规模不足:`parallel_setup_cost`/`parallel_tuple_cost` 估算认为并行开销大于收益。
  • 算子不支持并行:某些操作或函数限制了并行路径,可从计划节点类型判断(如部分自定义函数)。
  • `work_mem` 太小:导致 Hash/Sort 溢出到磁盘,吞吐与尾延迟劣化。
  • CPU 资源紧张:`max_parallel_workers` 与数据库/系统其他负载竞争,`Workers Launched` 低于预期。

验证与定位:


-- 观察是否走并行路径(存在 Gather/Gather Merge)
EXPLAIN (ANALYZE, VERBOSE)
SELECT ...;

-- 对比关闭并行/关闭 JIT 的影响(A/B)
SET max_parallel_workers_per_gather = 0; -- 关闭并行
SET jit = off;                            -- 关闭 JIT
EXPLAIN (ANALYZE, BUFFERS, JIT) SELECT ...;

-- 恢复设置
RESET ALL;

## 最佳实践(上线前核查清单)


  • 用 `EXPLAIN (ANALYZE, BUFFERS, JIT)` 固化诊断流程,保留样例输出作为基线。
  • 控制 `work_mem` 与并行度:按查询类型与并发压测迭代,不追求全局最大并行。
  • 只对复杂聚合/连接启用 JIT:对简单查询收益有限,避免额外开销。
  • 监控 `Workers Launched` 与查询耗时分布(`p95/p99`),观察资源饱和点。
  • 变更配置后进行回归:收集关键查询计划,确保无异常退化。

## 结语


并行与 JIT 能显著提升复杂查询的吞吐,但收益依赖数据规模、查询形态与资源约束。以计划与运行时统计为依据迭代调优,才能保证优化真实可靠。



点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部