Merge Join在大批量有序数据场景下最快,因其跳过建哈希表和随机查找,仅用双指针流式归并,内存恒定、支持早期终止;但严格依赖输入数据在连接键上已有序,否则实际性能崩盘。

Merge Join 在大批量有序数据场景下最快,不是因为它“聪明”,而是它彻底跳过了其他连接算法必须做的两件事:建哈希表、反复随机查找。
为什么Merge Join不需要建哈希表
Hash Join 必须先把小表所有连接键(比如 user_id)读进内存,构建哈希表,再逐行扫描大表去查——这一步内存开销大、且无法流式输出。而 Merge Join 只维护两个指针,每次只比对当前两行的键值:a.id 和 b.id,相等就输出,不等就移动较小一方的指针。整个过程内存占用恒定,通常几KB就够。
- 即使两表各 1000 万行,Merge Join 也只做约 2000 万次比较(
O(N + M)),而不是 Hash Join 的建表 + 查表两阶段 - 没有哈希冲突、无需扩容桶、不触发 memory spilling
- 如果查询带
LIMIT 100,Merge Join 可能扫不到 1% 数据就返回结果;Hash Join 必须建完全部哈希表才吐第一行
为什么Merge Join不依赖索引存在,但极度依赖索引“用得对”
Merge Join 不关心有没有索引,只认执行计划里子节点是否带 Ordered="true"。但现实中,让数据流有序最靠谱的方式就是走索引扫描。问题在于:索引建了≠能用上。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
-
ON a.x = b.y要生效,a 表需有INDEX (x),b 表需有INDEX (y);若写成ON a.x = UPPER(b.y),b 表索引直接失效,触发隐式Sort - 联合索引
INDEX (status, created_at)无法支撑WHERE created_at > '2025-01-01'的有序流,因为没命中最左前缀 - 字段类型不一致(如
INTvsBIGINT)会触发隐式转换,导致索引无法提供有序输出
怎么确认Merge Join真正在“免排序”运行
别只看执行计划顶上写了 Merge Join 就以为赢了。真正关键在它的两个直接子节点。
- 用
EXPLAIN (ANALYZE, BUFFERS),找到<RelOp LogicalOp="Merge Join">节点 - 往下看它的左右子节点:必须都是
Index Scan或Index Only Scan,且属性含Ordered=true - 如果任一子节点是
Sort或Seq Scan,说明数据库正在后台偷偷排序——这时 CPU 高、执行慢,根本不是 Merge Join 的错,是数据没按它要的顺序来 - 对比
Rows Removed by Filter和Rows Read:如果前者远大于后者(比如读 10 万行,滤掉 9.8 万),说明排序后大量数据被丢弃,归并效率崩盘
真正难的从来不是加索引,而是让查询条件、字段类型、索引定义、统计信息四者严丝合缝地对齐——漏掉任意一环,Merge Join 就会从加速器变成隐形拖油瓶。

















