Oracle 12.1.0.2为何会跳过结果缓存表上的函数调用?
嘿,这个问题我在12.1.0.2版本上碰到过好多次,算是这个早期版本的典型坑了,结合你的测试用例,我给你捋捋为啥会出现这种情况:
1. DDL操作搞乱了缓存依赖的元数据
你测试用例里对test_users做了一连串DDL:建表、加主键约束、加唯一约束,然后才插数据。在12.1.0.2里,如果你的结果缓存函数依赖这张刚被DDL折腾过的表,Oracle的缓存机制很容易“懵圈”——它会错误地认为这张表的元数据发生了不可逆的变化,直接跳过缓存检查,每次调用函数都老老实实去查数据库,完全不用缓存。
举个例子,假设你建了这么个依赖test_users的结果缓存函数:
CREATE OR REPLACE FUNCTION get_username(p_user_id IN INTEGER) RETURN VARCHAR2 RESULT_CACHE RELIES_ON(test_users) IS v_username VARCHAR2(32); BEGIN SELECT username INTO v_username FROM test_users WHERE user_id = p_user_id; RETURN v_username; END; /
如果在创建这个函数之前,test_users刚执行过ALTER TABLE操作,12.1.0.2就大概率识别不了表的正确状态,直接把缓存给忽略了。
2. 结果缓存的依赖跟踪有bug
12.1.0.2在结果缓存的依赖跟踪上有个挺烦人的缺陷:当函数依赖的表有频繁DML(比如你测试用例里的INSERT),缓存失效逻辑很容易误判。比如你插入一条user_id=3的记录,但调用的是get_username(1)——正常来说这条插入不影响缓存结果,缓存应该保留,但12.1.0.2可能会直接把整个缓存废掉,或者干脆跳过缓存直接执行查询。
更坑的是,如果表上有主键、唯一约束这类对象,Oracle在处理缓存依赖时会把约束的元数据变化和表数据变化混为一谈,直接导致缓存机制罢工。
3. 会话参数的坑
先检查下你的会话参数:如果RESULT_CACHE_MODE设成了MANUAL,而函数调用又没加/*+ RESULT_CACHE */提示,或者RESULT_CACHE_MAX_SIZE被设成了0,那Oracle肯定不会用缓存。不过这还不算完,12.1.0.2里还存在参数读取异常的bug——哪怕全局参数配置正确,会话级参数也可能被莫名重置,导致缓存失效。
怎么解决?
针对这些问题,你可以试试这几个办法:
- 先重置缓存:执行
ALTER SYSTEM FLUSH RESULT_CACHE;或者会话级的ALTER SESSION FLUSH RESULT_CACHE;,之后再测试函数调用; - 升级补丁:Oracle在12.1.0.2的后续补丁集(比如12.1.0.2.180717)里修复了好几个结果缓存的依赖跟踪bug,能从根源解决问题;
- 强制缓存:如果没法升级,就在函数调用时显式加
/*+ RESULT_CACHE */提示,逼着Oracle用缓存; - 避免DDL后直接用函数:如果必须在依赖表上做DDL,做完之后重新编译下依赖的函数,比如
ALTER FUNCTION get_username COMPILE;
内容的提问来源于stack exchange,提问作者Pusikas

