Oracle死锁的本质是判断阻塞关系与处置策略,需结合v$locked_object、gv$session递归链路及SQL上下文定位源头,RAC环境必须用gv$视图,kill session应优先用immediate参数并验证事务影响。

Oracle死锁不是“有没有”的问题,而是“谁卡住谁、卡在哪条语句、要不要杀”的判断问题。直接查 v$session 只能看到表象,真正定位根源得结合锁链路径和会话上下文——否则容易 kill 错会话,甚至让应用反复重试加剧阻塞。
怎么快速确认是不是真死锁,而不是普通锁等待
不能只看 lockwait 字段是否为空:死锁发生后,Oracle 通常已自动选一个会话做牺牲者(status = 'KILLED' 或 'INACTIVE'),此时它的 lockwait 往往已被清空。
- 更可靠的信号是:
sid出现在v$locked_object中,且对应会话的status = 'ACTIVE'、sql_id非空、event是enq: TX - row lock contention或类似事务类等待 - 如果用的是 RAC 环境,必须查
gv$session而非v$session,否则可能漏掉其他节点上的阻塞源头 -
ORA-00060错误日志一定出现在 alert log 中,但不是所有ORA-00060都对应当前活跃死锁——它只记录历史事件,需结合实时视图交叉验证
怎么一次性看到完整的阻塞链路
v$session.blocking_session 只反映“当前一级阻塞”,比如 A → B → C → D 的锁链,D 的 blocking_session 只显示 C,不会直接暴露 A。
- 要展开整条链,必须递归查询:
SELECT ... FROM gv$session START WITH blocking_session IS NOT NULL CONNECT BY PRIOR sid = blocking_session - 优先看
final_blocking_session字段(11gR2+ 且启用_kill_blocker相关参数时才稳定)——它直接指向最上层源头会话,省去层层追溯 - 如果
blocking_session为null,但会话仍卡在TX等待上,大概率是它自己持有了锁却没提交(比如未commit的UPDATE),这时重点看它的sql_id和logon_time
查到源头会话后,alter system kill session 怎么写才安全
直接执行 alter system kill session 'sid,serial#' 是最常用方式,但它只是“标记终止”,会话可能仍在清理事务,客户端看到的仍是 ORA-00028: your session has been killed,而非立即消失。
- 加
immediate参数可强制中断:alter system kill session '123,456' immediate(注意:不是所有版本都支持,12c+ 更可靠) - 如果是 RAC,必须带上实例号:
alter system kill session '123,456,@2',否则可能在错误节点上执行 - 别光看
program就杀——同一个应用可能有多个合法长事务;先用v$sql查sql_text,确认是否是未提交的 DML,再决定是否杀
为什么查 v$locked_object 后还找不到对应 SQL
v$locked_object 只存对象级信息(如 object_id),不保存 SQL 文本。靠 sql_hash_value 关联 v$sql 常常失败,因为该值在游标老化或硬解析后就失效了。
- 应优先用
sql_id关联v$sql:SELECT sql_text FROM v$sql WHERE sql_id = 'xxx' - 如果
sql_id为空或查不到,说明该会话的 SQL 已被刷出共享池,此时只能靠machine、program、osuser结合业务日志反推 - 对长期运行的批处理任务,建议在代码里显式打日志(如写入自定义表),避免事后排查时 SQL 丢失
真正难的不是查到哪条语句卡住了,而是判断这条语句该不该杀、杀完会不会触发重试风暴、下游系统有没有幂等保障——这些没法靠 SQL 解决,得翻应用日志、看事务边界、问开发同学。











