hash join写磁盘需先确认:若explain中disk usage为0或pg_stat_progress_hash显示桶严重稀疏,则非work_mem不足;数据倾斜、类型不匹配、统计信息过期等才是主因。

Hash Join写磁盘了,work_mem真不够用吗?
不是所有超大表JOIN慢都该调 work_mem——先确认它是不是真在写磁盘。运行 EXPLAIN (ANALYZE, BUFFERS),重点看 Hash 节点里有没有非零的 Disk Usage;如果为 0,说明根本没落盘,调 work_mem 没用。
再查 pg_stat_progress_hash(PostgreSQL 12+):hash_probe_total_buckets / hash_buckets_used 比值若远大于 3,说明哈希桶严重稀疏,是 work_mem 太小被迫建了过多桶,内存被浪费而非不够用。
快速验数据倾斜:SELECT key, COUNT(*) FROM table GROUP BY key ORDER BY 2 DESC LIMIT 5。如果 TOP1 频次 > 平均值 × 10,加 work_mem 基本白费——此时该考虑分区裁剪、预过滤或改用 Merge Join。
work_mem 不是“整个查询”的内存,而是“每个操作节点”的上限
一个含两个 Hash Join、一个 Sort、一个 DISTINCT 的查询,最多可能吃掉 4 × work_mem。并行查询更危险:max_parallel_workers_per_gather = 4 时,主进程 + 4 个 worker 各自申请一份 work_mem,总开销是 5 × work_mem。
work_mem 单位必须写对:'64MB' 才生效;错写成 '64M' 会被静默转成 64 字节,直接崩。
连接池(如 pgbouncer)可能拦截 SET 命令,需确认其配置允许运行时参数修改,否则 SET LOCAL work_mem = '64MB' 会被静默丢弃。
调大后还是慢?这些细节常被忽略
JOIN 字段类型不一致,比如 CAST(u.id AS TEXT) = o.user_id:索引失效,优化器被迫选 Hash,且无法缩小驱动表规模。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
统计信息过期:大表增删改超 20% 后未运行 ANALYZE table_name,优化器误估驱动表大小,runtime 才发现溢出。
本该用 Nested Loop 的场景硬塞 Hash:左表仅 100 行、右表有索引且字段高选择性时,SET enable_hashjoin = off 反而更快(仅用于诊断,勿上线)。
临时调优别碰全局配置,用 SET LOCAL 最安全
生产环境推荐起点:SET LOCAL work_mem = '32MB' 或 '64MB',只对当前事务生效,事务结束自动还原。
估算下限可参考:(驱动表行数 × (Join 键长度 + 8)) × 1.5(1.5 是哈希填充因子)。例如驱动表 1000 万行、INT 键(4 字节),粗略需 ≈ 96MB,那就从 '128MB' 开始试。
别指望 shared_buffers 或 effective_cache_size 能“帮忙”——它们不参与哈希表构建,调了也没用。
真正卡住的从来不是单次 work_mem 设多少,而是并发下多个操作叠加申请、类型不匹配导致计划走歪、以及统计信息滞后让优化器“瞎猜”。这些地方一漏,调再大也白搭。










