<p>先查 waiting 状态会话定位阻塞点,再通过 p2/p3 反查 v$transaction 找持锁事务,结合 rowwait* 定位热点数据行,最后依据 p1raw 值(54580006 为行锁,54580004 为 ITL 或唯一键冲突)判断是否需 kill 或优化设计。</p>

查谁在等:先抓 waiting 状态的会话
遇到响应变慢、应用“点不动”,第一反应不是翻 AWR,而是立刻查当前正在等待的会话——v$session 里 event = 'enq: TX - row lock contention' 的记录最直接。这个查询能快速暴露卡点:
SELECT sid, serial#, sql_id, blocking_session, event, p1raw, p2, p3 FROM v$session WHERE event = 'enq: TX - row lock contention'-
blocking_session为 NULL 不代表没被堵,只说明它不是二级阻塞(比如被 A 堵,A 被 B 堵);真正持锁者可能藏在更深层 -
p1raw值必须看:若为54580006,是标准行锁(mode 6),大概率是 UPDATE/DELETE 冲突;若为54580004,别急着 kill,很可能是 ITL 不足或唯一键冲突,杀错会白忙
找谁在锁:从 p2/p3 反查真实事务
Oracle 不直接暴露“哪个会话锁了哪行”,但 p2 和 p3 是线索钥匙:p2 是 undo segment + slot 编码,p3 是序列号。用它们反查 v$transaction,才能定位到真正没提交的事务:
- 先把
p2转成十六进制,拆出 usn(高位)和 slot(低位),例如p2 = 131075→ 十六进制0x20003→ usn=2, slot=3 SELECT xidusn, xidslot, xidsqn, ses_addr FROM v$transaction WHERE xidusn = 2 AND xidslot = 3 AND xidsqn = &p3- 再用
ses_addr关联v$session:SELECT sid, serial#, sql_id, status, logon_time FROM v$session WHERE saddr = '&ses_addr' - 重点看
status:如果INACTIVE且logon_time是几小时前,90% 是应用异常退出没 commit,不是 SQL 本身有问题
确认锁在哪一行:用 row_wait_* 定位热点数据块
知道谁持锁后,还得知道它锁的是哪张表、哪一行,否则没法判断是不是业务逻辑缺陷。关键字段都在 v$session 的 row_wait_* 列里:
-
row_wait_obj#→ 查dba_objects得表名:SELECT owner, object_name FROM dba_objects WHERE object_id = &row_wait_obj# -
row_wait_file#,row_wait_block#,row_wait_row#组合可定位到具体数据块和行号(生产环境一般不查行内容,但能确认是否集中在某主键值或索引字段) - 如果
row_wait_obj#是 -1,说明不是行锁,而是 ITL 或唯一键冲突导致的伪等待,得回头检查p1raw和索引结构 - 高频出现同一
object_name+ 同一sql_id,基本可断定是业务未做幂等校验(比如并发 INSERT 相同主键)
别跳过 mode=4 的陷阱:ITL 不足和唯一键冲突不是 kill 能解决的
看到 p1raw = 54580004 就想 kill 持锁者?停手。mode=4 的本质不是“别人占着锁不放”,而是资源不足或语义冲突:
- ITL 不足:高并发 UPDATE 同一数据块时,块头没空槽可用。查
dba_segments的ini_trans,低值(如 2)就危险;ALTER TABLE ... STORAGE (INITRANS 16)才是正解 - 唯一键冲突:两个会话同时
INSERT INTO t(pk) VALUES (1),第一个拿到 TX 锁,第二个等;锁一释放立刻报ORA-00001—— 等待只是表象,根子在应用没做前置校验 - 位图索引并发更新也会触发 mode=4,但这类索引本身就不适合 OLTP 场景,该换结构而不是调参
真正的难点不在查,而在区分:是事务没结束(mode 6,可 kill),还是设计有缺陷(mode 4,kill 无用)。查完 v$session 和 v$transaction 后,务必再看一眼 p1raw 和业务 SQL 逻辑。


















