ora-00060是oracle自动检测到死锁后终止事务的错误,需从逻辑上规避而非捕获重试;应立即查v$locked_object和v$session定位阻塞链,统一表访问顺序、避免长事务、确保索引有效、合理使用savepoint。
ora-00060 是 oracle 自动检测并终止死锁后抛出的错误,不是你要“手动捕获并重试”的异常,而是必须从逻辑上规避的问题。 它意味着你的事务设计已出现资源争用循环,靠加 exception when others 捕获再重试只会掩盖根本问题,甚至放大风险。
ORA-00060 错误出现时该做什么
看到 ORA-00060: deadlock detected while waiting for resource,第一反应不是改代码,而是查现场:
- 立刻执行
SELECT * FROM V$LOCKED_OBJECT确认哪些会话、哪些对象被锁住 - 用
SELECT sid, serial#, sql_id, blocking_session FROM V$SESSION WHERE blocking_session IS NOT NULL找出阻塞链头 - 通过
V$SQLTEXT_WITH_NEWLINES关联sql_id查出具体卡住的 SQL(注意按piece排序拼接) - 不要直接
ALTER SYSTEM KILL SESSION—— 先确认是否是开发测试环境、事务是否可丢弃;生产环境建议先ALTER SYSTEM DISCONNECT SESSION 'sid,serial#' IMMEDIATE,更安全
为什么按固定顺序访问表能减少死锁
死锁高频场景就是两个事务以不同顺序更新同一组表。比如事务 A 先 UPDATE employees 再 UPDATE departments,而事务 B 反过来。Oracle 行级锁本身不保证顺序,但你的业务逻辑可以。
- 所有涉及
employees和departments的 PL/SQL 过程,统一约定:先操作employees,再操作departments - 在包规范中定义常量顺序,如
c_emp_first CONSTANT VARCHAR2(1) := 'E';,并在关键过程注释里显式声明依赖顺序 - 避免在触发器里隐式更新关联表——触发器执行时机不可控,极易打破你定好的顺序
- 如果必须动态决定表名(如配置化逻辑),那就用
DBMS_LOCK做应用层协调锁,而不是依赖数据库行锁
PL/SQL 中提交事务的时机很关键
长事务 = 长时间持锁 = 更高死锁概率。Oracle 不会在 COMMIT 前释放 DML 锁,哪怕你只改了一行。
- 把大事务拆成多个小事务:比如批量处理 1000 条记录,每 50 条
COMMIT一次,而不是全做完再提交 - 避免在循环内做 DML 后不提交,又在后续循环中访问相同主键范围的数据
- 不要在
FOR UPDATE游标里做耗时操作(如调用 HTTP、写文件),游标打开期间锁一直持有 - 使用
SAVEPOINT替代全程回滚:出错时只回滚到最近 savepoint,不影响前面已确认的逻辑
索引缺失导致的隐性死锁容易被忽略
没走索引的 UPDATE 或 DELETE 会升级为表级锁或大量行锁,大幅提高冲突面。这不会报 ORA-00060,但会让死锁更频繁、更难定位。
- 对所有
WHERE条件字段建组合索引,尤其多表 JOIN + WHERE 的场景 - 用
EXPLAIN PLAN FOR ...看执行计划,确认是否走了预期索引;若出现FULL TABLE SCAN且数据量大,立即优化 - 避免在 WHERE 子句中对字段用函数,如
WHERE UPPER(name) = 'ABC'—— 索引失效,可能引发全表扫描锁 - 定期检查
V$SEGMENT_STATISTICS中logical reads和physical reads异常高的表,它们往往是锁争用热点
真正棘手的从来不是怎么查死锁,而是怎么让开发团队在写第一个 UPDATE 之前就想好锁的边界和顺序。一个没加索引的 WHERE 条件、一段没约定表访问顺序的业务逻辑、一次延迟提交的批量处理——这些细节堆在一起,比任何单点故障都难排查。











