并行Hash JOIN写磁盘主因是work_mem未按worker数量乘数配置,每个worker独立申请work_mem,叠加后易OOM或swap;须用EXPLAIN(ANALYZE,BUFFERS)查Disk Usage、pg_stat_progress_hash比值及数据倾斜验证,再以SET LOCAL逐步调优。

PostgreSQL 15 中并行 Hash JOIN 的内存占用不是靠“调大 shared_buffers”能解决的——它根本不用那块内存;真正起作用的是 work_mem,且必须考虑并行 worker 的乘数效应。不按这个逻辑调,轻则无效,重则 OOM 或大量 swap。
为什么并行 Hash JOIN 会写磁盘,即使 work_mem 看起来够?
因为每个 worker 进程都独立申请一份 work_mem,而你只看了单个操作的估算。比如:
-
max_parallel_workers_per_gather = 4,那一个并行 Hash JOIN 最多消耗 4 × work_mem - 同一查询里还有 Sort 和 DISTINCT,它们也各自吃
work_mem,总开销是叠加的 -
pg_stat_progress_hash只反映 leader 进程状态,worker 落盘了你也看不到 Disk Usage 字段 - 配置写成
'64M'(少个 B)会被静默转成 64 字节,直接崩
怎么确认真是 work_mem 不足,而不是数据倾斜?
别一看到 “writing to disk due to insufficient memory” 就改配置。先跑这三步:
- 用
EXPLAIN (ANALYZE, BUFFERS)看 Hash 节点的Disk Usage是否非零;为 0 就没落盘,别动work_mem - 查
pg_stat_progress_hash:如果hash_probe_total_buckets / hash_buckets_used > 3,说明哈希桶稀疏,大概率是work_mem太小被迫建了太多桶 - 快速验倾斜:
SELECT key, COUNT(*) FROM big_table GROUP BY key ORDER BY 2 DESC LIMIT 5;若 TOP1 频次 > 平均值 × 10,加work_mem基本白费
怎么设 work_mem 才安全又有效?
记住它是“每个操作”的上限,不是全局预算。生产环境推荐这样操作:
- 起点用
SET LOCAL work_mem = '8MB'(会话级,不影响别人) - 逐步试到
'32MB',每次只改当前 SQL,用EXPLAIN (ANALYZE)对比Execution Time和Disk Usage - 避免全局设高:假设
max_connections = 200,work_mem = '64MB'理论峰值占 12.8GB 内存 - 估算下限可粗略按:
(驱动表行数 × (Join 键长度 + 8)) × 1.5,但实测比公式更可靠
并行 Hash JOIN 的内存陷阱藏在“乘法”里——不是你给得不够,而是你没算清有几个 worker 在同时伸手要。调参前务必先看 pg_stat_progress_hash 和实际 Disk Usage,否则容易把资源问题误判成算法问题。

















