快速定位持锁者需先查v$session找等待会话,再通过p1raw解析锁类型,用p2/p3反查v$transaction定位真实持锁事务,最后结合v$session确认应用上下文,严禁盲目kill。
怎么快速定位谁在堵着你?
遇到 enq: tx - row lock contention,第一反应不是查sql,而是找“持锁者”——那个没提交、没回滚、卡在半路的会话。oracle不会主动告诉你谁锁了哪行,但所有线索都藏在 v$session、v$lock 和 v$transaction 里。
- 先看谁在等:
SELECT sid, event, p1raw, p2, p3 FROM v$session WHERE event = 'enq: TX - row lock contention' - 从
p1raw解出锁类型:比如54580006→ 前4位5458是 "TX",后2位06表示独占模式(X mode),说明是 UPDATE/DELETE 等待;若为54580004,大概率是唯一键冲突或 ITL 不足 - 用
p2和p3反查事务:SELECT xidusn, xidslot, xidsqn, ses_addr FROM v$transaction WHERE xidusn = &usn AND xidslot = &slot AND xidsqn = &seq(注意:p2是usn,需转成十进制再拆) - 最后连到会话:
SELECT sid, serial#, sql_id, blocking_session, status, logon_time FROM v$session WHERE saddr = '&ses_addr'
别跳过 status 字段——INACTIVE 且长时间没动作的会话,90% 是应用忘了 commit 或异常退出没清理事务。
为什么 mode=4 和 mode=6 的处理方式完全不同?
mode=6 是标准行锁等待:A 更新了某行没提交,B 想更新同一行,就卡住。解决路径清晰——要么让 A 提交/回滚,要么杀掉 A。
mode=4 就麻烦得多:它通常不表示“别人锁了这行”,而是“你和别人同时想插入/更新同一个唯一键值”,或者 ITL 槽不够、位图索引被并发修改。这时候即使 kill 掉等待者也没用,因为冲突根源在语义层。
- 唯一键冲突:两个会话同时
INSERT INTO t(pk) VALUES (1),第一个拿到 TX 锁,第二个等锁释放,但释放后立刻报ORA-00001—— 所以等待只是表象,根本问题是业务没做幂等或前置校验 - ITL 不足:高并发 UPDATE 同一数据块时,块头 ITL 槽耗尽,会触发
enq: TX - allocate ITL entry,但它常被误判为row lock contention;可通过dba_segments查ini_trans/max_trans,或加ALTER TABLE ... STORAGE (INITRANS 16)缓解 - 位图索引:并发更新同一键值范围会争用位图段,建议 OLTP 场景慎用位图索引
ASH 报告里哪些字段真正有用?
别只盯着 “Top SQL with Top Events” —— 那里显示的是“谁在等”,不是“谁在锁”。关键要看 blocking_session 和 blocking_session_status 列,尤其当 blocking_session_status = 'VALID' 时,直接顺藤摸瓜。
- 查阻塞链:
SELECT session_id, blocking_session, sql_id, event, time_waited FROM v$active_session_history WHERE sample_time > SYSDATE-1/24 AND event = 'enq: TX - row lock contention' ORDER BY sample_time DESC - 注意
time_waited单位是毫秒,但 ASH 是采样数据,单次值不准;重点看持续出现的blocking_session组合 - 如果
blocking_session显示为 0 或空,可能是事务已结束但锁未及时释放(极少见),也可能是统计信息延迟,此时要切回v$transaction手动核对
ASH 里 sql_id 对应的 SQL 很可能只是“受害者”,真正的根因 SQL 往往在 blocking_session 对应的会话历史中——比如一个批量 UPDATE 跑了 20 分钟没提交,后面所有简单 SELECT FOR UPDATE 全部排队。
杀会话前必须确认的三件事
别一看到 SID 就 ALTER SYSTEM KILL SESSION。杀错会话可能中断关键批处理、导致数据不一致,甚至引发全局死锁(尤其在 11.2.0.3–11.2.0.4 版本中)。
- 确认该会话是否真在持锁:
SELECT * FROM v$lock WHERE sid = &sid AND type = 'TX';若无结果,说明锁已释放,等待是残留状态或误报 - 确认事务是否可安全回滚:
SELECT used_ublk, used_urec FROM v$transaction WHERE ses_addr = (SELECT saddr FROM v$session WHERE sid = &sid);used_urec过大(如 >10000)意味着回滚耗时长,可能比等更糟 - 确认应用上下文:
SELECT program, module, action, client_info FROM v$session WHERE sid = &sid;如果是 ETL 工具或定时任务,先联系负责人,而不是直接 kill
最危险的操作是:在没查 v$transaction 的情况下,仅凭 v$session.blocking_session 杀掉“看起来最老”的会话——有时候那个会话只是刚执行完 COMMIT,而锁其实由另一个隐式事务持有。

















