Oracle会自动检测死锁并回滚其中一个事务(victim),抛出ORA-00060错误;真正需人工干预的是长期持锁不释放的阻塞会话,须结合v$locked_object与v$session定位blocker,查prev_sql_id或v$sqltext还原加锁语句,并谨慎使用ALTER SYSTEM KILL SESSION IMMEDIATE。

Oracle 不会“自动处理”死锁,而是由数据库引擎检测到后立即回滚其中一个事务(victim),并抛出 ORA-00060 错误。真正需要人工干预的,是那些未被自动识别为死锁、但长期持锁不释放的阻塞会话——它们常被误认为“死锁”,实则是锁等待超时或应用未提交导致的假死状态。
查 v$locked_object 和 v$session 定位真实阻塞源
死锁发生后,ORA-00060 日志里只记录 victim 信息,但 blocker 往往还在运行。光看 v$locked_object 只能知道哪些对象被锁,必须关联 v$session 才能定位到具体会话:
-
v$locked_object.session_id对应v$session.sid,不是v$session.serial# - 用
SELECT sid, serial#, username, status, sql_id, prev_sql_id FROM v$session WHERE sid IN (SELECT session_id FROM v$locked_object)查活跃会话 - 注意
status = 'INACTIVE'不代表空闲,可能正卡在未提交事务中;status = 'ACTIVE'且sql_id IS NULL说明当前没执行 SQL,但事务仍开启 - 若
lockwait字段非空(如0000000000000000以外值),说明该会话正在等待某个锁,大概率是 blocker
用 v$sql 和 v$sqltext 还原阻塞 SQL
仅靠 v$session.sql_id 可能拿不到完整语句,因为事务中多条 SQL 切换后,sql_id 只保留最后执行的那条。要还原最初加锁的语句,得查 prev_sql_id 或拼接 v$sqltext:
- 先查
SELECT prev_sql_id, prev_child_number FROM v$session WHERE sid = - 再用
SELECT sql_text FROM v$sql WHERE sql_id = '' AND child_number = <pre class="brush:php;toolbar:false;" v_child_number>
- 如果
prev_sql_id为空,说明会话自登录后还没执行过任何 SQL,锁来自隐式 DML(如未提交的UPDATE)或显式LOCK TABLE - 避免直接查
v$sqltext全表:它按piece分片存储,需ORDER BY piece拼接,且无索引,大库上慢
ALTER SYSTEM KILL SESSION 的 immediate 时机与风险
执行 ALTER SYSTEM KILL SESSION 'sid,serial#' 后,Oracle 会向目标会话发送中断信号,但该会话可能仍在清理资源。若长时间没退出(如正在写回滚段),就会报 ORA-00031: session marked for kill:
- 此时必须加
IMMEDIATE:ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE -
IMMEDIATE强制终止会话进程,不等事务回滚完成,后续由 PMON 进程异步清理——这会导致短暂的锁残留和回滚段压力上升 - 不要在 RAC 环境中只查本地
v$session就杀会话:需确认inst_id,否则可能杀错节点上的会话 - 杀之前先查
v$transaction:若used_ublk > 0,说明事务已占用大量回滚块,强制杀可能延长恢复时间
为什么 SELECT ... FOR UPDATE 容易引发误判死锁
开发常把 SELECT ... FOR UPDATE 当作“加锁检查”,但没配 WAIT 或 NOWAIT,结果一执行就无限挂起,看起来像死锁:
- 默认行为是无限等待,直到锁释放或超时(由
enqueue_resources参数隐式控制) - 正确做法是显式设等待时间:
SELECT * FROM emp WHERE empno = 7499 FOR UPDATE WAIT 3,超时抛ORA-30006 - 更安全的是用
NOWAIT+ 应用层重试逻辑:SELECT * FROM emp WHERE empno = 7499 FOR UPDATE NOWAIT,失败立刻返回,由业务决定是否重试或提示用户 - 注意:同一事务内多次
FOR UPDATE不会报错,但若跨会话更新同一行,第二个会话会阻塞——这不是死锁,只是正常锁等待
最易被忽略的点:死锁图(deadlock graph)只出现在 alert 日志里,且默认只记录 victim 侧信息;blocker 的完整上下文(如是否在 PL/SQL 匿名块里、是否调用了自治事务)往往需要结合 v$session_longops 和会话跟踪(DBMS_MONITOR.SESSION_TRACE_ENABLE)才能还原。别只盯着 ORA-00060 报错本身。











