加了result_cache未提速是因为根本未进入缓存,因函数不满足命名存储、参数全为标量、无非确定性调用、依赖表无触发器等任一硬性条件,或缓存键字节级不匹配、dml导致全表失效。

加了 RESULT_CACHE 却没提速?不是缓存慢,是根本没进缓存。 Oracle 对 PL/SQL 函数启用结果缓存有三道硬性门槛,任一不满足就跳过缓存逻辑,连尝试都不做。
函数定义必须同时满足这四个硬性条件
缺一不可,否则调用时直接走原逻辑,RESULT_CACHE 形同虚设:
- 函数必须是 命名的、存储的函数(不能是匿名块或子程序)
- 所有
IN参数类型必须是标量:仅支持NUMBER、VARCHAR2、DATE、CHAR等,禁止REF CURSOR、RECORD、集合、对象、LOB - 函数体里不能调用任何非确定性函数:
SYSDATE、USER、DBMS_RANDOM.VALUE、SEQ.NEXTVAL、ROWNUM、ORDER BY(无确定性排序)等,哪怕只有一行也会被拒 - 查询的表不能带触发器、物化视图日志,也不能是临时表、
SYS/SYSTEM下的字典表
缓存键字节级敏感,类型/精度错一点就失效
RESULT_CACHE 的键不是按“值相等”生成,而是按参数的原始字节表示哈希。这意味着:
-
get_name(p_id IN NUMBER)接收字符串'123'会报ORA-06553: PLS-306;必须传123或显式TO_NUMBER('123') -
NUMBER(10)和NUMBER(10,0)被视为两个不同键,缓存不共享 - 绑定变量调用时,宿主变量声明类型必须与函数参数**完全一致**,PL/SQL 隐式转换不生效
- 大小写、空格、前导零都影响键值 ——
'ABC '和'ABC'是两个缓存项
依赖表 DML 后全表失效,不是局部刷新
只要函数声明了 RELIES_ON(t1, t2)(11gR1 必须写,R2 可省略但建议保留),那么:
-
t1或t2中任意一张被INSERT/UPDATE/DELETE,所有依赖它的缓存条目立即STATUS = 'Invalid' - 哪怕只改了一行无关字段,缓存照样清空;没有“按主键刷新”或“按 WHERE 刷新”机制
- 高写场景(如每分钟都有 DML 的配置表)下,命中率趋近于 0,此时开启反而增加哈希计算开销
- 真正适合的场景:查极低频更新的码表(
country_codes、currency_types)或纯计算函数(不查表)
怎么确认缓存真生效了
别信执行计划,它不显示是否命中。必须交叉验证运行时行为:
- 第一次调用后查
V$RESULT_CACHE_OBJECTS:找到对应函数名,且STATUS = 'Published'才算注册成功 - 连续两次相同参数调用,查
V$SQL中该 SQL 的EXECUTIONS字段:第二次应仍为 1(说明没重跑底层查询) - 在函数体开头加
DBMS_OUTPUT.PUT_LINE('executed'):如果第二次还打印,说明完全没走缓存,立刻回查前三条限制项 - 用
DBMS_RESULT_CACHE.MEMORY_REPORT查内存使用,确认没因RESULT_CACHE_MAX_RESULT(默认仅占总缓存 5%)被截断
最常被忽略的是缓存键的字节级敏感性和 DML 失效粒度——你以为传了 123 就能复用,实际可能因为前端传的是字符串、或调用方变量类型是 NUMBER(8),导致缓存完全隔离;你以为改个状态字段不影响,其实只要表在 RELIES_ON 列表里,缓存就全丢了。











