先查v$session定位等待会话,再通过p1raw解析锁类型,用p2/p3反查v$transaction找持锁事务,最后连v$session确认应用上下文;mode=6为行锁等待,mode=4多因唯一键冲突或itl不足。

查 v$session 找出正在等行锁的会话
行锁等待最直接的表现是会话卡在 enq: TX - row lock contention 事件上。先确认哪些会话被堵住:
- 执行
SELECT sid, serial#, username, sql_id, event, seconds_in_wait, blocking_session FROM v$session WHERE event = 'enq: TX - row lock contention' AND status = 'ACTIVE' - 重点关注
blocking_session列:若非空,说明有明确持有锁的源头会话;若为空,可能是自身事务未提交,或锁已被释放但等待残留(少见) -
seconds_in_wait超过 30 秒就该介入;超过 5 分钟基本可判定为应用逻辑问题
用 dba_locks 和 v$locked_object 定位被锁的具体行
仅知道会话 ID 不够,得知道它锁了哪张表、哪一行。关键不是“谁在等”,而是“谁在持锁 + 锁在哪”:
- 查持锁会话和对象:
SELECT s.sid, s.username, s.sql_id, o.object_name, o.object_type FROM v$session s JOIN v$locked_object lo ON s.sid = lo.session_id JOIN dba_objects o ON lo.object_id = o.object_id - 如果
object_type是TABLE,再结合 SQL 中的WHERE条件(比如WHERE id = 123),就能定位到具体行 - 注意:PL/SQL 存储过程里没显式写
COMMIT,哪怕只有一条UPDATE,也会让整张表的对应行一直被锁住
看 v$sql 和存储过程源码判断是否隐含长事务
很多 PL/SQL 行锁问题不是 SQL 写错,而是事务边界失控。重点检查:
- 从
v$session拿到sql_id后,查SELECT sql_text FROM v$sql WHERE sql_id = '<xxx>'</xxx>,看是不是调用了某个PROCEDURE或FUNCTION - 立刻去查这个过程定义:
SELECT text FROM dba_source WHERE name = 'P_TEST_UPDATE' ORDER BY line(把过程名换成实际值) - 常见陷阱:
UPDATE后没COMMIT;EXCEPTION块里漏写ROLLBACK;循环中反复UPDATE却只在最后COMMIT - 特别警惕带
PRAGMA AUTONOMOUS_TRANSACTION的子过程——它自己开事务,不影响外层,但容易让人误以为“已经提交了”
用 DBMS_LOCK.SLEEP 复现并验证锁行为
开发阶段想提前暴露这类问题,别等上线才踩坑。可以在测试环境主动模拟:
- 新开一个会话,执行
UPDATE t1 SET x = x + 1 WHERE id = 100,不COMMIT - 另一个会话执行你的 PL/SQL 过程,看是否立刻卡住;再查
v$session确认等待事件 - 过程中加
DBMS_OUTPUT.PUT_LINE打点,确认执行到哪一步停住——常卡在第二个UPDATE或INSERT上 - 真实业务里,如果过程里混用 DML 和远程调用(如调用 WebService),更要小心:网络延迟可能让事务挂起几十秒,期间锁一直占着
真正麻烦的不是锁本身,是 PL/SQL 里那些看不见的事务边界。一个 COMMIT 漏写,或者异常路径没兜底,就会让锁在后台无声无息地卡住其他所有业务。查的时候别只盯着 SQL,得顺藤摸到过程体里那几行缩进最深的代码。











