pl/sql函数必须显式声明result_cache且满足deterministic要求才能启用缓存,否则编译报错或缓存失效;它依赖底层表变更自动失效,仅适用于准静态参考表,且受内存参数严格限制。
pl/sql函数必须声明 result_cache 才能启用缓存
oracle不会自动缓存任意pl/sql函数的返回值。必须在函数定义中显式添加 result_cache 子句,且函数需满足确定性(deterministic)要求——即相同输入必得相同输出,无副作用(不能修改表、调用 sysdate、dbms_random 等非确定性函数)。否则编译会报错或运行时被跳过缓存。
常见错误现象:ORA-06553: PLS-905: object ... is invalid 或缓存未命中但执行计划里没出现 RESULT CACHE 操作符。
- 正确写法:
CREATE OR REPLACE FUNCTION get_state_name(p_id NUMBER) RETURN VARCHAR2 RESULT_CACHE DETERMINISTIC IS ... - 不加
DETERMINISTIC:即使加了RESULT_CACHE,Oracle也会拒绝缓存 - 函数体中含
INSERT/UPDATE/SELECT ... FOR UPDATE:直接报错PLS-00999: result-cached function cannot reference package variables or session state
RESULT_CACHE 缓存依赖底层表变更,只适用于准静态数据
缓存不是永久有效的。只要函数体内任何 SELECT 引用的表发生 DML(INSERT/UPDATE/DELETE)或 DDL,所有依赖该表的缓存条目会在事务提交后立即失效——下一次调用会重新执行函数并重建缓存。
这意味着它对频繁更新的表(如 ORDERS、TRANSACTIONS)几乎无效,反而增加失效开销;但对参考表(如 STATES、COUNTRIES、PRODUCT_CATEGORIES)效果极佳。
- 确认依赖关系:查视图
V$RESULT_CACHE_DEPENDENCY,过滤OBJECT_NAME可看到函数依赖哪些表 - 避免隐式依赖:函数内不要用动态 SQL(
EXECUTE IMMEDIATE),否则 Oracle 无法追踪依赖,缓存可能陈旧 - 不要试图“绕过”失效:比如在函数里加
PRAGMA AUTONOMOUS_TRANSACTION写日志——这会导致缓存完全不可用
缓存大小和单条限制由参数硬性控制,超限即静默跳过
即使函数声明了 RESULT_CACHE,若结果集超过内存限额,Oracle 也不会报错,而是直接跳过缓存,退化为普通函数执行。关键参数是:
-
RESULT_CACHE_MAX_SIZE:整个缓存池上限(默认约 SGA 的 1%,如为 0 则全局禁用) -
RESULT_CACHE_MAX_RESULT:单个缓存条目最大占比(默认 5%,即最多占池子的 5%)
例如池子总大小为 10MB,默认单条结果最大只能缓存 512KB。若函数返回一个含 CLOB 的大结果集,大概率被丢弃。
实操建议:
- 用
SELECT * FROM V$RESULT_CACHE_MEMORY查当前使用量和碎片情况 - 用
SELECT * FROM V$RESULT_CACHE_OBJECTS WHERE CACHE_ID LIKE '%get_state_name%'确认是否真被缓存 - 不要盲目调大
RESULT_CACHE_MAX_RESULT:过大容易挤占其他缓存,引发闩锁争用(result cache latch)
与 SQL 查询结果缓存共存时,注意提示优先级和模式冲突
PL/SQL 函数缓存和 SQL 查询结果缓存(/*+ RESULT_CACHE */)共享同一内存池,但控制逻辑独立。若函数内部嵌套了带 RESULT_CACHE 提示的查询,两者会叠加生效;但若数据库级设置 RESULT_CACHE_MODE = FORCE,而函数又用了 NO_RESULT_CACHE 提示,则函数缓存仍有效——因为函数级声明优先于 SQL 级提示。
真正容易被忽略的点是:函数缓存的 key 仅基于输入参数值,不包含会话上下文(如 USER、SYSDATE)。所以绝不能在函数里写 WHERE created_by = USER 这类语句——不同用户调用会污染彼此缓存,返回错误结果。
安全做法:
- 所有 WHERE 条件字段必须是函数参数显式传入,而非隐式依赖会话状态
- 慎用
RESULT_CACHE RELIES_ON (table_name)子句:它只是文档化依赖,不改变行为,且 Oracle 12c 后已废弃 - 上线前务必用多个会话并发调用测试,验证缓存隔离性











