如何通过Result Cache优化Oracle中频繁调用的短小函数执行效率?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1019个文字,预计阅读时间需要5分钟。
相关专题:
PL/SQL函数加了RESULT_CACHE却没生效?先看这三条硬限制
绝大多数缓存失效不是配置问题,而是撞上了 oracle 的强制校验规则。函数体里只要出现以下任一情况,缓存直接被跳过,且不报错、不警告:
- 调用了任何未声明为
DETERMINISTIC的函数(包括你自己写的 PL/SQL 函数,哪怕逻辑完全不变) - 访问了任何非只读对象:比如
SYSDATE、USER、序列NEXTVAL、临时表、自治事务中的 DML - 参数或返回值类型含
REF CURSOR、BLOB、CLOB等大对象类型(Oracle 不缓存这些)
验证是否真走缓存最简单的方法:在函数体里加一行 DBMS_OUTPUT.PUT_LINE('executed')。连续两次相同参数调用,如果第二次还打印,说明根本没进缓存路径——立刻回头检查上面三点。
查V$RESULT_CACHE_OBJECTS确认缓存状态,别只看执行计划
V$RESULT_CACHE_OBJECTS 是唯一可信的缓存登记簿。光看执行计划里有没有 RESULT CACHE 操作符没用,它只表示“尝试启用”,不代表缓存已建立或命中。
执行完函数后立即查:
SELECT id, type, status, cache_id, creation_timestamp FROM V$RESULT_CACHE_OBJECTS WHERE cache_id LIKE '%your_function_name%';
关键看 STATUS = 'Published' 且 TYPE = 'Function'。如果状态是 Invalid 或查不到记录,说明函数第一次执行就因上述限制被拒绝缓存;如果是 Pending,大概率是依赖的底层表刚被其他会话修改过(DML 触发自动失效)。
绑定变量传参时类型必须严格一致,隐式转换会绕过缓存
PL/SQL 调用方用绑定变量传参时,变量声明类型必须和函数参数定义**逐字节匹配**。常见陷阱:
- 函数参数是
NUMBER(10),但调用时用NUMBER(12)或PLS_INTEGER—— 缓存键不同,算作新调用 - 字符串参数是
VARCHAR2(30),但传入的是CHAR(30)(尾部空格参与哈希计算) - 使用
TO_NUMBER()等函数包装参数再传入,导致实际传入的是表达式而非纯值
最稳妥做法:在调用前用 DESCRIBE 查函数签名,然后按原样声明变量。例如函数定义为 FUNCTION f(p_id IN NUMBER),就声明 v_id NUMBER;,不要写 v_id PLS_INTEGER;。
缓存失效比你想象中更频繁,高并发写场景下慎用
RESULT_CACHE 不是独立内存池,它和函数里所有 SELECT 涉及的表强绑定。只要任意一张被查询的表发生 DML(INSERT/UPDATE/DELETE),哪怕来自另一个会话、另一个应用,该函数所有缓存项立刻置为 Invalid。
这意味着:
- 查配置表、码表(如
sys_config、country_code)很适合;查交易主表(如orders)基本无效 - RAC 环境下,一个实例的 DML 会通过 RCBG 进程广播给所有实例,缓存同步开销不可忽略
- 如果函数背后查的表平均每分钟有 5 次以上 DML,实测缓存命中率常低于 10%,此时开启反而增加哈希键计算负担
真正值得加 RESULT_CACHE 的函数,通常满足两个条件:背后无 SQL 或只查极静态表 + 参数组合有限(比如几十种以内)。否则不如用应用层缓存或物化视图。
本文共计1019个文字,预计阅读时间需要5分钟。
相关专题:
PL/SQL函数加了RESULT_CACHE却没生效?先看这三条硬限制
绝大多数缓存失效不是配置问题,而是撞上了 oracle 的强制校验规则。函数体里只要出现以下任一情况,缓存直接被跳过,且不报错、不警告:
- 调用了任何未声明为
DETERMINISTIC的函数(包括你自己写的 PL/SQL 函数,哪怕逻辑完全不变) - 访问了任何非只读对象:比如
SYSDATE、USER、序列NEXTVAL、临时表、自治事务中的 DML - 参数或返回值类型含
REF CURSOR、BLOB、CLOB等大对象类型(Oracle 不缓存这些)
验证是否真走缓存最简单的方法:在函数体里加一行 DBMS_OUTPUT.PUT_LINE('executed')。连续两次相同参数调用,如果第二次还打印,说明根本没进缓存路径——立刻回头检查上面三点。
查V$RESULT_CACHE_OBJECTS确认缓存状态,别只看执行计划
V$RESULT_CACHE_OBJECTS 是唯一可信的缓存登记簿。光看执行计划里有没有 RESULT CACHE 操作符没用,它只表示“尝试启用”,不代表缓存已建立或命中。
执行完函数后立即查:
SELECT id, type, status, cache_id, creation_timestamp FROM V$RESULT_CACHE_OBJECTS WHERE cache_id LIKE '%your_function_name%';
关键看 STATUS = 'Published' 且 TYPE = 'Function'。如果状态是 Invalid 或查不到记录,说明函数第一次执行就因上述限制被拒绝缓存;如果是 Pending,大概率是依赖的底层表刚被其他会话修改过(DML 触发自动失效)。
绑定变量传参时类型必须严格一致,隐式转换会绕过缓存
PL/SQL 调用方用绑定变量传参时,变量声明类型必须和函数参数定义**逐字节匹配**。常见陷阱:
- 函数参数是
NUMBER(10),但调用时用NUMBER(12)或PLS_INTEGER—— 缓存键不同,算作新调用 - 字符串参数是
VARCHAR2(30),但传入的是CHAR(30)(尾部空格参与哈希计算) - 使用
TO_NUMBER()等函数包装参数再传入,导致实际传入的是表达式而非纯值
最稳妥做法:在调用前用 DESCRIBE 查函数签名,然后按原样声明变量。例如函数定义为 FUNCTION f(p_id IN NUMBER),就声明 v_id NUMBER;,不要写 v_id PLS_INTEGER;。
缓存失效比你想象中更频繁,高并发写场景下慎用
RESULT_CACHE 不是独立内存池,它和函数里所有 SELECT 涉及的表强绑定。只要任意一张被查询的表发生 DML(INSERT/UPDATE/DELETE),哪怕来自另一个会话、另一个应用,该函数所有缓存项立刻置为 Invalid。
这意味着:
- 查配置表、码表(如
sys_config、country_code)很适合;查交易主表(如orders)基本无效 - RAC 环境下,一个实例的 DML 会通过 RCBG 进程广播给所有实例,缓存同步开销不可忽略
- 如果函数背后查的表平均每分钟有 5 次以上 DML,实测缓存命中率常低于 10%,此时开启反而增加哈希键计算负担
真正值得加 RESULT_CACHE 的函数,通常满足两个条件:背后无 SQL 或只查极静态表 + 参数组合有限(比如几十种以内)。否则不如用应用层缓存或物化视图。

