20250714--长连接应用中ORA-04061: existing state of has been invalidated
问题背景
接到报障,应用中报错:ORA-04061: existing state of has been invalidated。提示package_P是无效状态。
但在数据库中查看,发现package_P是正常的,且通过PL SQL工具调用该包时正常。
另,据同事反馈,应用重启后问题就可解决。
数据库版本是:Oracle 11g。
分析
确认问题
- 数据库中,package_P确实正常,Plsql 工具可以调用,但应用中报错。
- 重启应用有,问题解决。可以复现;如,再次直接编译package,应用就又开始报错。
查阅资料,提示是Oracle的共享池缓存技术+应用连接池长连接导致。
原理
解决方法
方法1:刷新共享池【无效】
理论上讲,如果package报错,再次调用时会自动重新编译。但这里是不能编译成功。可能是共享池导致,建议手工刷新共享池。
ALTER SYSTEM FLUSH SHARED_POOL; -- 谨慎使用,可能影响性能
结论:无效。在开发环境验证,应用继续报同样错误。
方法2:分离package中的包变量
- 原来的package_P是先定义了5个包体变量、若干业务方法;
- 新的:将package分拆为2个,1个新包,只包含5个包体变量;旧包,只包含业务方法,调用新包的变量。
结论:可行。无论是重新编译新包、还是业务包体,都不影响应用。
验证 :package包体变量的机制
按前文【原理】部分的内容,每个会话有自己的会话独立的包状态,包含:变量、游标、状态等。
注意:这与java的全局变量完全不同,java的全局变量是jvm种共用;oralce的package的变量,相当于只能在当前会话、多个package内的方法共用。
实验设计:
- 设计包体包含1个包体变量、1个方法对包体变量+1;
- 在不同的会话中调用方法,看:变量是否不同?
- 重新编译package,看:已打开的会话,调用方法会否报错。
CREATE OR REPLACE PACKAGE pkg_global_var ASg_counter NUMBER := 0; -- 全局变量PROCEDURE increment;
END pkg_global_var;CREATE OR REPLACE PACKAGE BODY pkg_global_var ASPROCEDURE increment ISBEGINg_counter := g_counter + 1; -- 修改全局变量dbms_output.put_line(SYS_CONTEXT('USERENV', 'SID')||':'||g_counter); --输出会话IDEND;
END pkg_global_var;
会话不同,变量不同
分别开2个会话,执行方法:值不同。
重新编译package,调用方法会报错,可复现问题
总结
- 因包体变量的原因,重新编译package后,已打开会话中的缓存会失效,但无法自动更新。
- 重启应用,即重建了数据库连接,有效但不可接受。
- 通过分离包体变量和业务方法,可以解决。
- 后续注意:尽量少使用包体变量。