Memoize节点仅在Nested Loop Join中生效,且需同时满足:执行计划为Nested Loop、内表有合适索引、外表JOIN键值高度重复(如仅5个不同值),此时缓存内表结果可显著提升性能。

Memoize节点只在Nested Loop Join中生效,且仅当内表JOIN键值高度重复时才有意义。它不是通用加速器,而是一个针对特定数据分布的缓存优化——如果你的查询没走Nested Loop,或者内表键值几乎不重复,Memoize节点根本不会出现,强行开启也无用。
什么时候能看到Memoize节点?
必须同时满足三个条件:
- 执行计划明确是
Nested Loop(不是Hash Join或Merge Join) - 内表(即被循环扫描的右表)有合适的索引支持快速单值查找(如
uj索引) - 外表(左表)JOIN键的取值非常有限(例如
t.j只有5个不同值),导致大量重复查找同一组内表行
典型场景是LATERAL JOIN、按状态/类型分组关联维度表,或外表经过强过滤后结果集小但键值集中。
Memoize如何工作?
它本质是在Nested Loop每次迭代前查一个哈希缓存:
- 第一次遇到
t.j = 3,执行Index Scan on u拿到全部u.j = 3的行,并把结果存入以t.j为key的内存哈希表 - 后续再遇到
t.j = 3,直接从哈希表返回缓存结果,跳过索引扫描和行构造开销 - 缓存项不跨查询保留,也不持久化,仅当前执行生命周期有效
注意:Cache Key: t.j出现在EXPLAIN里,说明缓存键来自外表列;如果看到Cache Key: u.j,那基本是误读——Memoize永远缓存内表结果,键一定来自外表。
为什么有时开了enable_memoize却没效果?
常见原因不是参数关了,而是执行计划根本没选Nested Loop:
- 外表太大或内表键值太分散(比如
u.j有20000个不同值),优化器会倾向选Hash Join,Memoize节点自然消失 - 外表没走顺序扫描,而是用了Index Scan + LIMIT,导致驱动行数不可预测,优化器放弃缓存假设
-
work_mem严重不足,导致Memoize内部哈希结构被迫落盘,反而比直扫还慢 - 查询涉及volatile函数(如
now()、random())或子事务,PostgreSQL会禁用Memoize以保证语义正确
最直接验证方式:先用SET enable_hashjoin = off; SET enable_mergejoin = off;强制走Nested Loop,再看EXPLAIN是否出现Memoize节点——但这仅用于诊断,切勿在生产环境长期关闭其他连接方式。
真正关键的不是“有没有Memoize”,而是“为什么优化器认为Nested Loop+缓存比Hash Join更便宜”。这背后是外表基数估算、内表索引选择性、以及random_page_cost等参数的综合博弈——调参前务必先看EXPLAIN (ANALYZE, BUFFERS)里真实行数和IO分布。

















