ORA-00060死锁异常不会自动回滚事务,必须在EXCEPTION块中显式ROLLBACK;需精准捕获SQLCODE = -60,配合延时重试(≤3次)和业务层优化。
ORA-00060 报错时 PL/SQL 事务不会自动回滚
oracle 遇到死锁会强制终止其中一个会话并抛出 ora-00060,但这个异常**不会触发隐式回滚**——你当前事务里已执行的 dml(比如 update、insert)仍处于未提交状态。如果不手动处理,后续语句可能报 ora-00068(事务状态不一致)或覆盖错误逻辑。
- 必须在
EXCEPTION块中显式写ROLLBACK;,不能依赖 Oracle 自动清理 - 如果用了自治事务(
PRAGMA AUTONOMOUS_TRANSACTION),要特别注意:主事务的ROLLBACK不影响自治事务,反过来也不传播 - 捕获位置很重要:得在最内层可能触发死锁的 DML 后立即包围
EXCEPTION,而不是整个存储过程末尾才处理
用 OTHERS 捕获不够,必须精确匹配 SQLCODE = -60
OTHERS 会兜底所有异常,但掩盖了死锁的特殊性——它需要重试逻辑,而其他错误(如约束冲突)重试只会重复失败。只靠 WHEN OTHERS THEN 无法区分场景,容易把死锁当成普通错误吞掉。
- 正确做法是写
WHEN OTHERS THEN IF SQLCODE = -60 THEN ... END IF; - 不要用
WHEN DUP_VAL_ON_INDEX THEN这类具体异常名替代,ORA-00060没有预定义异常名,只能靠SQLCODE - 注意
SQLCODE在异常块外不可用,必须在EXCEPTION内部立刻读取
简单重试逻辑要加延时,否则大概率再次死锁
死锁本质是两个会话循环等待对方持有的锁,直接重试往往复现相同竞争路径。不加控制的循环重试(比如 FOR i IN 1..3 LOOP ... EXCEPTION WHEN ... THEN CONTINUE; END LOOP;)会让问题更糟。
- 每次重试前加
DBMS_LOCK.SLEEP(0.1);(单位秒),哪怕 100ms 也能错开时间窗口 - 重试次数建议 ≤ 3 次,再多说明业务设计有问题(比如长事务没拆分、更新顺序不统一)
- 避免在循环里反复
COMMIT或ROLLBACK,应确保整个逻辑单元原子性;重试时从头开始,不是接着上次断点
应用层也要配合:超时和隔离级别不能全甩给数据库
PL/SQL 层能做的有限——它没法解决跨服务、跨库、或应用直连 JDBC 的死锁。很多 ORA-00060 其实根子在外部调用没设查询超时,或者用了 SELECT FOR UPDATE 但没及时释放。
- Java 应用里检查是否设置了
queryTimeout,防止一个慢查询长期占着行锁 - 避免在事务里调用外部 HTTP 接口,网络延迟会拉长锁持有时间
- 如果业务允许,把
READ COMMITTED改成READ ONLY事务,彻底规避写锁竞争
死锁不是“修个异常捕获”就能根治的问题,它暴露的是并发访问路径的设计缺陷。代码里加 ROLLBACK 和重试只是止血,真正要查的是哪些表被高频交叉更新、有没有统一的加锁顺序、事务边界是否合理。










