加了RESULT_CACHE却没有明显提速,很多情况下并不是 Oracle 性能本身有问题,而是函数实际上根本没有进入结果缓存。这通常与三类常见限制有关:调用了非确定性函数、传入了非标量参数、或查询了非确定性数据源。另外,缓存键对字节级表示非常敏感,而且在发生 DML 操作后会按表级整体失效。

加了 RESULT_CACHE 却没有性能提升?大多数时候并非缓存效果差,而是函数压根没有成功进入缓存,问题往往出在配置方式或 PL/SQL 写法不符合规则。
为什么你的 PL/SQL 函数没有走缓存
Oracle 对 RESULT_CACHE 的使用设置了三道硬性门槛,只要任意一项不满足,就会直接跳过缓存机制,甚至不会尝试命中:
- 函数内部调用了非确定性函数,例如
SYSDATE、USER、DBMS_RANDOM.VALUE、SEQ.NEXTVAL,即使只出现一次,也会被 Oracle 排除在结果缓存之外 - 参数类型不是标量:如果传入的是
REF CURSOR、RECORD、自定义对象类型或集合类型,Oracle 无法生成可用的缓存键,通常会直接报出ORA-06553: PLS-306 - 函数内部查询了带触发器、物化视图日志,或者包含
ROWNUM/ORDER BY(但排序结果不具确定性)的表,这类对象会被 Oracle 判定为“非确定性数据源”
怎么验证缓存是否真的生效
不要只看执行计划,因为执行计划并不会明确展示 RESULT_CACHE 是否命中。要确认 Oracle 11g PL/SQL 结果缓存是否生效,必须结合运行时表现和系统视图进行交叉验证:
- 第一次调用函数后,查询
V$RESULT_CACHE_OBJECTS:只有找到对应函数对象,并且STATUS = 'Published',才说明缓存已经成功注册 - 连续两次使用相同参数调用,观察
V$SQL中相关 SQL 的EXECUTIONS字段——如果第二次执行后仍然是 1,通常说明底层 SQL 没有再次运行,缓存已命中 - 在函数体开头加入
DBMS_OUTPUT.PUT_LINE('executed'):如果第二次调用依然打印,说明本次执行完全没有使用结果缓存,需要立即回查前面的限制条件
缓存键敏感到字节级,类型或精度差一点都会失效
RESULT_CACHE 的缓存键是根据参数值的**原始字节表示**生成的,并不是按“语义相同”来匹配,因此看起来一样的值,未必会命中同一份缓存:
get_name(p_id IN NUMBER)接收'123'(VARCHAR2)会报错;但传入123(NUMBER)或TO_NUMBER('123')则可以正常匹配NUMBER(10)和NUMBER(10,0)会被视为两个不同的缓存键,因此缓存结果不会共享- 使用绑定变量传参时,调用方声明的变量类型必须与函数参数定义**完全一致**,不要依赖 PL/SQL 的隐式类型转换,否则很容易导致缓存失效
DML 后缓存整体失效,高并发写入场景要谨慎
RESULT_CACHE 的失效粒度是表级,而不是行级、字段级或单条结果级,这一点在 Oracle 性能优化中尤其需要注意:
- 如果函数内部查询了
config_table和status_ref两张表,那么只要其中任意一张发生INSERT/UPDATE/DELETE,相关缓存条目就会立即全部失效 - Oracle 不提供“局部刷新”机制——哪怕只是改动一行数据,或者更新了与查询结果无关的字段,也会触发表级缓存清空
- 如果底层表几乎每分钟都有 DML 变更,结果缓存命中率通常会接近 0,此时启用
RESULT_CACHE反而可能增加哈希键计算和维护开销,建议直接关闭
真正适合使用 RESULT_CACHE 的函数,通常只查询极少变更的基础码表,例如 country_codes、currency_types 这类稳定数据,或者本身就是纯计算逻辑(不访问数据表)。一旦函数依赖业务表、交易表或频繁更新的配置表,那么相比 Oracle 结果缓存,更适合考虑应用层缓存、物化视图等替代方案。
