必须用GV$ACTIVE_SESSION_HISTORY,因其是RAC环境下唯一能跨实例聚合、含blocking_instance字段的实时视图,可完整还原阻塞链;V$或DBA_HIST视图会漏掉90%根阻塞源。

必须用 GV$ACTIVE_SESSION_HISTORY,只查单实例的 V$ACTIVE_SESSION_HISTORY 或历史视图 DBA_HIST_ACTIVE_SESS_HISTORY 会漏掉 90% 的根阻塞源,尤其在 RAC 环境下。
为什么一定要用 GV$ACTIVE_SESSION_HISTORY
RAC 中真正持锁的会话可能运行在另一个节点上。比如 blocking_session = 1234、blocking_instance = 2,但你在节点 1 上查 V$ACTIVE_SESSION_HISTORY 根本看不到这个会话——它不在当前实例内存里。而 GV$ACTIVE_SESSION_HISTORY 是跨实例聚合视图,自带 blocking_instance 字段,是唯一能还原完整阻塞链的实时数据源。
常见误操作包括:
- 用
DBA_HIST_ACTIVE_SESS_HISTORY做实时排查:采样间隔长(默认每小时 1 次)、已聚合、不含blocking_instance,无法定位跨节点锁源 - 连错实例后只查
V$视图:看到一堆library cache lock却blocking_session IS NULL,误判为“无阻塞者”,实际是阻塞源在别处 - 不显式指定
inst_id或加blocking_instance IS NOT NULL条件:查询结果混入大量无关样本,掩盖关键阻塞路径
关键过滤条件和时间窗口怎么设
查得太宽会扫全内存 buffer(ASH 默认保留约 1 小时),轻则慢,重则因权限或资源报 ORA-00942;查得太窄又容易错过瞬态毛刺。正确做法是:
- 时间必须用动态表达式:
SAMPLE_TIME > SYSDATE - INTERVAL '5' MINUTE(不是写死时间字符串,避免 NLS/时区问题) - 核心 WHERE 条件只有三个:
event = 'library cache lock'、wait_time = 0(表示正卡着,非历史等待)、blocking_session IS NOT NULL - 必须加
blocking_instance IS NOT NULL或显式WHERE inst_id = &block_inst,否则无法区分跨节点关系
示例片段:
SELECT inst_id, session_id, blocking_instance, blocking_session, p3, sql_id, sample_time FROM gv$active_session_history WHERE event = 'library cache lock' AND wait_time = 0 AND blocking_session IS NOT NULL AND blocking_instance IS NOT NULL AND sample_time > SYSDATE - INTERVAL '5' MINUTE;
P3 值怎么快速解出 namespace 和 mode
P3 是十六进制整数,格式为高位拼接:高 4 字节 → object_id,中间 4 字节 → namespace,低 4 字节 → mode。不能直接除 100 解析。
例如 P3 = 61959198277635:
- 转十六进制:
TO_CHAR(p3, 'XXXXXXXXXXXXXX')→385A00010003 - 拆解:
385A(高 4 字节)→ object_id = 14426 → 查DBA_OBJECTS定位对象 -
0001(中 4 字节)→ namespace = 1 → 对应 CRSR(游标),重点查V$SQL中version_count > 50的 SQL -
0003(低 4 字节)→ mode = 3 → 独占锁,说明有会话正在硬解析或重载包体
常见 namespace 含义:
- namespace = 1(CRSR):绑定变量为空、ACS Bug、JDBC 驱动未设 bind peeking 导致子游标爆炸
- namespace = 2(TABLE/PROCEDURE):DDL 正在执行(如
ALTER PACKAGE ... COMPILE),查DBA_OBJECTS.last_ddl_time - namespace = 79(ACCOUNT_STATUS):不是 SQL 问题,是高频登录失败(
returncode = 1017),查DBA_AUDIT_SESSION
查到 blocking_session 后下一步必须做什么
GV$ACTIVE_SESSION_HISTORY 是采样快照,不是实时状态。查到 blocking_instance = 2、blocking_session = 1058 后,立刻切到对应节点执行:
- 确认该会话是否仍在运行:
SELECT status, sql_id, prev_sql_id, program FROM gv$session WHERE inst_id = 2 AND sid = 1058; - 看它在等什么:
SELECT event, p1, p2, p3 FROM gv$session WHERE inst_id = 2 AND sid = 1058; - 若
status = 'INACTIVE'且sql_id IS NULL,大概率是事务未提交或 PL/SQL 调用卡住,需结合gv$transaction查start_time和used_ublk - 不要直接 kill —— 先查它执行的是什么:
SELECT sql_text FROM gv$sql WHERE sql_id = '<sql_id>';</sql_id>,很多 case 是批处理脚本忘了COMMIT
最易被忽略的一点:阻塞链常是多级的(A→B→C),blocking_session 只指向上一级,而 final_blocking_session 才指向根因。RAC 下务必优先查这个字段,否则你 kill 掉 B,A 还在持锁,C 立刻又被阻塞。


















