LMON不是死锁源头,真正引发RAC GES死锁的是持有TX锁未提交的用户会话;应查GV$GES_BLOCKING_ENQUEUE定位BLOCKING_INSTANCE源头节点,并结合XID与LOGMINER还原SQL执行顺序确认锁序冲突。
LMON不是死锁源头,别查错进程
lmon(lock monitor)进程本身不会“引发”死锁,它是rac中负责维护全局资源目录和检测节点间不一致的后台进程。你看到lmon在top或ps里cpu高、或出现在awr报告top 5 event里(如“global cache cr request”),大概率是它正在处理已发生的死锁后续协调,而非问题根源。真正要盯的是持有tx锁却迟迟不提交的用户会话——lmon只是那个被迫出来拉架的保安。
查GV$GES_BLOCKING_ENQUEUE定位跨实例阻塞源头
RAC死锁必须用gv$ges_blocking_enqueue,不能只查v$session或v$lock。这个视图直接反映LMD(Lock Manager Daemon)视角下的全局队列阻塞关系,字段BLOCKING_INSTANCE就是你要找的“哪个节点最先卡住别人”:
-
BLOCKING_INSTANCE非空 → 该行对应的INST_ID就是源头节点号(比如值为2,说明实例2上某个会话持锁未释放) -
TYPE为TX且ID1/ID2相同 → 表明两个会话在争同一事务槽(XID),这是典型行级死锁信号 -
STATE为GRANTING→ 该会话当前正持有锁;REQUESTING→ 正在等锁,但还没被LMON标记为“死锁牺牲者”
执行:
SELECT INST_ID, SID, SERIAL#, TYPE, ID1, ID2, BLOCKING_INSTANCE, BLOCKING_SID FROM gv$ges_blocking_enqueue WHERE BLOCKING_INSTANCE IS NOT NULL;
结合XID和LOGMINER还原真实SQL执行顺序
仅靠gv$session.sql_id可能不准:RAC中SQL可能在不同节点硬解析、共享池内容不一致、或语句刚执行完就被刷出缓存。更可靠的方式是抓两个死锁会话的XID(从gv$transaction或gv$session查),再用LOGMINER解析对应时间窗口的归档日志:
- 先查:
SELECT xidusn, xidslot, xidsqn FROM gv$transaction WHERE sid IN (xxx, yyy);得到两个XID的三元组 - 用
DBMS_LOGMNR.START_LOGMNR加归档日志,过滤SEG_OWNER和XID,导出完整DML序列 - 重点比对:谁先UPDATE了A行又等B行,谁先UPDATE了B行又等A行 —— 这才是业务层真正的锁序冲突点
注意:LOGMINER需提前开启补充日志(ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;),否则XID无法关联到具体行
别碰v$global_blocked_locks,它默认不生效
很多文档说查v$global_blocked_locks能看全局阻塞链,但在12c RAC生产环境里,它默认返回空结果。原因很实在:_enable_global_blocked_locks=true是隐含参数,未获Oracle官方支持,启用后LMD进程额外开销大,且重启即失效。真要确认是否启用,运行:
SELECT ksppinm, ksppstvl FROM x$ksppi a, x$ksppcv b WHERE a.indx = b.indx AND ksppinm = '_enable_global_blocked_locks';若
ksppstvl为FALSE,就别在这视图上浪费时间了。
复杂点在于:RAC死锁不是“瞬间事件”,而是60秒检测周期内累积的GC协调压力暴露出来的事务设计缺陷。你看到LMON忙,本质是应用没按主键升序更新、外键约束触发了跨表锁、或者一个存储过程里混着DDL和DML——这些都得从LOGMINER还原的SQL流里反推,而不是盯着LMON进程杀或调参。


















