ORA-00060是死锁,非单纯竞争;Oracle检测到多个会话互持对方所需锁并主动回滚其中一个事务,需通过trace文件分析Deadlock graph定位具体行锁冲突及session,不可盲目重试。
ORA-00060 是死锁还是竞争?先看日志里真正冲突的 SQL
ora-00060 不代表“你的代码写错了”,而是 oracle 检测到两个或多个会话互相持有对方需要的锁,且无法继续推进。关键在 ora-00060 错误附带的 trace 文件——它会明确列出谁在等哪一行、哪个事务在持有哪些行锁。
实操建议:
- 立即查
user_dump_dest或diagnostic_dest下最新生成的 trace 文件,搜索Deadlock graph段落 - 重点关注
sessionID 和rowid(如AAAR3sAAFAAAABgAAA),而不是只盯着你自己的 SQL - 如果 trace 里出现
TX row lock contention而非Deadlock detected,那实际是锁等待超时(ORA-00054),不是死锁,重试逻辑要区分对待
Java 中捕获 ORA-00060 后该不该直接重试?
不能无条件重试。ORA-00060 触发时,Oracle 已回滚**其中一个会话的整个事务**(被选为 victim 的那个),但你的 Java 代码可能还在执行后续语句,导致数据不一致或重复提交。
实操建议:
- 必须在
catch块里检查异常的 SQLState:"61000"或getErrorCode() == 60,而非只靠消息字符串匹配 - 重试前先确认当前事务是否已回滚:调用
connection.getAutoCommit()和connection.isClosed();若使用 Spring,检查TransactionStatus.isCompleted() - 最多重试 2 次,且第二次重试前加
Thread.sleep(50 + new Random().nextInt(100))避免雪崩
避免死锁比重试更重要:UPDATE 顺序和索引设计
80% 的 ORA-00060 来自不同业务模块以不同顺序更新同一组表。比如 A 服务先 UPDATE orders 再 UPDATE order_items,B 服务反过来,就极易触发死锁。
实操建议:
- 所有涉及多表更新的逻辑,强制按主键升序排列更新顺序(例如总是先按
order_id升序,再按item_id升序) - 检查
WHERE条件是否走索引:全表扫描会导致锁住大量无关行,把SELECT ... FOR UPDATE改成带唯一索引键的精确查询 - 避免在事务中混合 DML 和长耗时操作(如 HTTP 调用、文件读写),否则锁持有时间不可控
PL/SQL 里怎么安全重试?别用 AUTONOMOUS_TRANSACTION
有人想用 AUTONOMOUS_TRANSACTION 在存储过程中捕获 ORA-00060 后重试,这是危险操作——自治事务会脱离原事务上下文,导致一致性校验失效,甚至掩盖真正的并发问题。
实操建议:
- 在 PL/SQL 中用
EXCEPTION WHEN OTHERS THEN捕获后,显式ROLLBACK,再重新执行整个逻辑块(不要只重试 UPDATE) - 用
DBMS_LOCK.SLEEP(0.1)替代空循环,避免 CPU 空转 - 记录重试次数到
DBMS_APPLICATION_INFO.SET_MODULE,方便后续从 AWR 报告里定位高频重试点
死锁不是异常类型,而是资源调度结果。最常被忽略的是:你以为在重试“一条 UPDATE”,其实是在重试“一个业务语义完整的事务单元”。漏掉幂等性控制或状态校验,重试只会让问题更难排查。

















