Oracle 11g 不会自动重编译 INVALID 存储过程是设计行为而非 bug;ALTER PROCEDURE 编译后仍报 ORA-04068,是因为会话缓存的包状态被主动丢弃,首次调用时触发状态不一致错误,需应用层捕获重试或运维侧分批预编译。

Oracle 11g 不会自动重编译 INVALID 存储过程,这是设计行为,不是 bug
为什么 ALTER PROCEDURE 编译后仍报 ORA-04068?
这不是编译失败,而是会话缓存状态被 Oracle 主动丢弃。当你执行 ALTER PROCEDURE xxx COMPILE 时,数据库确实完成了语法检查和依赖验证,STATUS 会变回 VALID;但当前 JDBC 连接里缓存的包状态(尤其是含全局变量或常量的 package)已失效。下一次调用该过程时,Oracle 检测到状态不一致,就抛出 ORA-04068 + ORA-04061 + ORA-06508 三连错误。
常见触发场景:
- 其他会话刚执行了
CREATE OR REPLACE PACKAGE pkg_common,而你的应用连接还拿着旧的 package 实例 - RAC 节点间时间不同步,导致
STIME和P_TIMESTAMP不一致,Oracle 认为状态不可信 - 定时 Job 执行了
DROP TABLE t1再CREATE TABLE t1,依赖该表的过程元数据被标记为无效,但连接未刷新
为什么 DBMS_UTILITY.COMPILE_SCHEMA 不起作用?
这个包只批量执行 ALTER ... COMPILE,它能修复语法正确但因依赖变更导致的 INVALID 状态,但无法解决会话级状态残留问题。即使你跑完 DBMS_UTILITY.COMPILE_SCHEMA('SCOTT'),Java 应用里复用的老连接仍然会首次调用失败。
关键限制:
- 它不会清理共享池中的旧对象句柄
- 不触发会话本地的包初始化逻辑重载
- 对
PACKAGE BODY有效,但对纯PROCEDURE或FUNCTION无额外收益
为什么不能靠“自动重编译”兜底?
Oracle 11g 没有内置的“按需自动重编译”机制。所谓“自动”,仅指某些 DDL(如 CREATE OR REPLACE)会隐式触发依赖对象的状态更新,但前提是:该对象当前未被任何会话锁定、没有编译错误、且所有依赖项都存在并有效。
实际中卡住的典型原因:
-
v$access显示某 session 正在持有该过程锁,但该 session 已是KILLED状态,PMON 尚未清理干净 - 过程引用了不存在的 dblink,编译时报
PLS-00201: identifier 'xxx@yyy' must be declared,但ALL_ERRORS里查不到——因为错误被压制在远程端 - 使用了 12c+ 才支持的语法(如
JSON_OBJECT),在 11g 下直接编译失败,状态卡死INVALID
真正有效的应对方式只有两种
第一种是防御性编码:JDBC 层捕获 SQLException,判断 getSQLState().equals("65000") 且 getMessage().contains("ORA-04068"),然后透明重试一次。第二次必成功,因为 Oracle 已重建会话状态。
第二种是预防性运维:用 dba_objects 定期扫描 + EXECUTE IMMEDIATE 'ALTER PROCEDURE ... COMPILE' 清理,但必须配合 v$access 排查锁,并跳过正在被使用的对象,否则会卡住整个编译流程。
最易被忽略的一点:不要在应用启动时批量编译所有过程——11g 下高并发编译可能引发 library cache lock 等待,反而拖慢服务就绪时间。应该让编译动作分散在低峰期、按 schema 分批执行。


















