RESULT_CACHE函数缓存需显式声明DETERMINISTIC并严格满足确定性约束,依赖对象级DML/DDL触发全量失效,且易受隐式依赖、绑定变量及自动动态采样干扰,仅适用于只读表、参数有限、调用频繁的场景。
result_cache 函数缓存不是“开个开关就自动生效”的功能,它要求显式声明、严格满足确定性约束,且极易因隐式依赖或参数变化而失效。直接在函数定义里加 result_cache 提示,不等于结果真被缓存。
PL/SQL函数必须显式声明 RESULT_CACHE 且标记为 DETERMINISTIC
Oracle 不会自动缓存任意函数返回值;只有明确标注 RESULT_CACHE 和 DETERMINISTIC 的函数才可能进入缓存:
-
DETERMINISTIC是硬性前提:函数输出必须仅由输入参数决定,不能依赖SYSDATE、USER、序列、包变量、全局临时表等任何会变的状态 -
RESULT_CACHE必须写在函数头(不是调用处),且不能和PIPELINED、AGGREGATE等特性共存 - 若函数内部执行了 DML 或查询了非确定性对象(如
DBA_TABLES),即使声明了也会被忽略——缓存创建失败,但无报错,仅日志中出现ORA-04091类似提示
正确写法示例:
CREATE OR REPLACE FUNCTION get_emp_count(p_dept_id NUMBER) RETURN NUMBER RESULT_CACHE RELIES_ON (employees) DETERMINISTIC IS l_cnt NUMBER; BEGIN SELECT COUNT(*) INTO l_cnt FROM employees WHERE department_id = p_dept_id; RETURN l_cnt; END;
缓存失效不是按行触发,而是按对象级 DML/DDL 全量清空
函数缓存的依赖关系是 object-level 的,不是 row-level。只要 RELIES_ON 指定的表(如上例中的 employees)发生任意一次 INSERT/UPDATE/DELETE 或 ALTER,所有依赖该表的缓存条目立刻失效——不管那条记录是否和你的函数参数有关。
- 这意味着:对高频更新的表(如订单、日志),哪怕只插入一行,
get_emp_count的所有缓存结果全丢 -
RELIES_ON列表只能是基表名,不能是视图或远程表;若省略,Oracle 默认不建立依赖,缓存永不自动失效(但结果可能过期) - 注意:
GRANT/REVOKE权限操作也会触发缓存失效——这点常被忽略
避免 _optimizer_ads_use_result_cache=TRUE 引发的 RC Latch 争用
Oracle 12c 默认启用自动动态采样(ADS),它会悄悄把采样 SQL 的结果也塞进 Result Cache,导致大量并发请求争抢 Result Cache: RC Latch,反而拖慢系统。
- 即使你只对自定义函数启用缓存,ADS 生成的临时 SQL 仍可能抢占同一缓存池资源
- 确认问题存在:查
v$session_event中等待事件是否含latch free且p2text='Result Cache: RC Latch' - 根治方案:禁用 ADS 对 Result Cache 的使用:
ALTER SYSTEM SET "_optimizer_ads_use_result_cache" = FALSE; - 该参数是隐含参数,需 DBA 权限;重启实例不生效,但动态设置后立即起效
验证缓存是否真实生效,别只看执行计划
执行计划里出现 RESULT CACHE 操作不代表缓存命中;它只说明本次执行“尝试”走缓存路径。真正生效要看运行时统计和缓存状态。
- 检查缓存条目:
SELECT * FROM v$result_cache_objects WHERE type = 'Function';—— 若无记录,说明函数根本没进缓存 - 观察命中率:
SELECT name, value FROM v$result_cache_statistics WHERE name IN ('Find Count', 'Create Count Success');,比值接近 1 才算稳定复用 - 绑定变量影响极大:函数参数若来自绑定变量,且变量值未固化(如应用层每次传新值),缓存无法复用——本质是不同调用被视为不同函数实例
- 函数内嵌 SQL 的 hint(如
/*+ RESULT_CACHE */)无效;缓存行为完全由函数定义控制,与内部 SQL 写法无关
缓存失效机制是粗粒度的,而业务数据往往是细粒度变更的。如果你的函数依赖一个每天更新几百次的表,缓存几乎总处于“刚写入就失效”状态,此时强行启用 RESULT_CACHE 只会增加 latch 争用,不提升性能。真正适合它的场景,是那些背后只读表 + 参数组合有限 + 调用频次高的函数。


















