通过gv$session视图中blocking_session字段可定位阻塞链,非空值表示被阻塞,其值指向持有锁的会话id;执行指定sql可生成kill语句并拉出完整阻塞树。

查当前阻塞链:用 gv$session 连接 blocking_session
最直接的入口是看谁在等、谁在卡。Oracle 的 gv$session 视图里,blocking_session 字段非空就说明该会话正被阻塞;而 blocking_session 本身指向另一个会话 ID,就是持有锁的源头。
执行这条 SQL 能快速拉出完整阻塞树(尤其适合 RAC 环境):
SELECT 'alter system kill session ''' || sid || ',' || serial# || ',@' || inst_id || ''' immediate;' AS sqlstr, SYS_CONNECT_BY_PATH(sid || '@' || inst_id, '
-
tree列末尾那个sid@inst_id就是最终阻塞者,优先处理它 -
isleaf = 1表示该会话是链末端(即被阻塞但没再阻塞别人) -
event值如果是enq: TX - row lock contention,基本可断定是行级锁争用 - RAC 下必须关注
inst_id,跨实例阻塞容易被忽略
定位锁类型和对象:关联 gv$lock 和 dba_objects
只看到会话 ID 不够,得知道锁在什么资源上。TX 锁(事务锁)通常不对应具体对象,TM 锁(表级 DML 锁)才关联到表或索引。
下面这条语句把锁模式、请求状态、持有者/等待者、以及对象名都串起来:
SELECT
l.inst_id, l.sid, l.type, l.id1, l.id2, l.lmode, l.request,
o.owner, o.object_name, o.object_type
FROM gv$lock l
LEFT JOIN dba_objects o ON l.id1 = o.object_id AND l.type = 'TM'
WHERE l.type IN ('TX', 'TM') AND (l.lmode > 0 OR l.request > 0);
-
type = 'TX'且lmode = 6(排他)→ 当前事务正在修改数据,未提交 -
type = 'TM'且request = 4(Share)→ 正在等待对某张表加共享锁,常见于外键约束缺失索引 - 若
object_name为空但type = 'TM',可能是临时段或分区对象,需查dba_tab_partitions
确认未提交事务:查 v$transaction 关联会话
很多阻塞根本原因就是“忘了 COMMIT”。长时间运行的事务即使没做 DML,只要开了事务,就可能持锁(比如 SELECT FOR UPDATE 后没释放)。
这条语句能揪出所有活跃事务及其起始时间:
SELECT s.sid, s.serial#, s.username, s.status, s.sql_id, t.start_time, t.used_ublk, t.used_urec FROM v$session s JOIN v$transaction t ON s.taddr = t.addr;
-
used_ublk > 100或used_urec > 1000表示事务已修改大量数据,回滚代价高,杀之前得评估 -
status = 'ACTIVE'但sql_id为空 → 很可能卡在应用层,没发 SQL,只是事务开着 - 注意
start_time是事务开始时间,不是会话登录时间,比logon_time更关键
终止会话前必须验证三件事
别一看到 blocking_session 就 ALTER SYSTEM KILL SESSION。生产环境误杀可能引发长事务回滚、日志风暴甚至实例压力陡增。
- 先确认目标会话的
sql_id是否还在跑——查v$sql看executions和last_active_time,避免杀掉刚提交完正清理的会话 - 检查该会话是否属于关键应用进程(如 ETL 工具、定时 JOB),
program和machine比用户名更可靠 - 如果阻塞链里有
event = 'library cache lock'或'cursor: pin S wait on X',大概率是对象编译或统计信息收集导致,杀会话治标不治本
真正难的不是找到谁在阻塞,而是判断“这个锁能不能等”“杀完会不会更糟”。比如一个跑了 2 小时的批量更新,杀掉后回滚可能要 4 小时——这时候宁愿让业务稍慢,也别贸然中断。











