awr可定位enq: tm - contention根因但无法直接显示阻塞sql链,需结合ash或实时视图;若awr中该事件居top 3、总等待时间占比>15%且平均等待>50ms,多因外键索引缺失或并发dml冲突。

AWR能定位enq: TM - contention的根因,但不能直接告诉你哪条SQL在等哪条SQL——它只告诉你“谁在等、等得多、等得久”,真正的阻塞链必须结合ASH或实时视图补全。
从AWR报告里一眼识别TM锁争用严重性
打开AWR报告后,先跳到 Top 10 Foreground Events by Total Wait Time 部分。如果 enq: TM - contention 出现在前3名,且“Total Wait Time”占比 >15%,同时“Avg wait (ms)” > 50,基本可判定是外键索引缺失或并发DML冲突导致的典型TM锁问题。注意:该事件平均等待时间若低于10ms,更可能是短时争用,而非结构性缺陷。
接着看 SQL Statistics 中 “SQL ordered by Elapsed Time” 列表,重点关注那些执行时间长、但 %CPU + %IO 总和极低(如 enq: TM - contention 上,而非真正干活。
- 典型现象:
DELETE FROM parent_table WHERE id = :1平均耗时 280 秒,但 CPU 占比仅 0.3%,IO 占比 1.2% - 关联线索:该语句操作的表,在
Segments by Logical Reads或Segments by Row Lock Waits中未出现,说明锁不在它自己身上,而在子表或约束检查环节
定位触发TM锁的外键关系与缺失索引
TM锁最常见的成因是 DML 操作需校验外键约束,而子表外键列上无索引,导致每次 DELETE/UPDATE 父表都要全扫子表。AWR本身不存外键定义,但可通过以下方式交叉验证:
在报告中找到高耗时SQL涉及的父表(比如 MSG_MESSAGE01),然后手动运行查询确认其子表及索引状态:
SELECT a.table_name, a.column_name, b.index_name FROM user_cons_columns a JOIN user_constraints c ON a.constraint_name = c.constraint_name JOIN user_ind_columns b ON a.table_name = b.table_name AND a.column_name = b.column_name WHERE c.r_constraint_name IN ( SELECT constraint_name FROM user_constraints WHERE table_name = 'MSG_MESSAGE01' AND constraint_type = 'P' ) AND a.position = b.column_position(+);
若某子表(如 MSG_MSGREC)在外键字段上查不到 index_name,就是关键缺口。此时 AWR 中看到的 enq: TM - contention 就是 Oracle 在为约束检查反复申请和等待表级锁的表现。
- 不要依赖
dba_constraints查所有用户——必须用对应业务用户的user_constraints,否则可能漏掉权限隔离下的子表 - 即使有索引,也要检查索引列顺序是否与外键列顺序一致;顺序错位等同于无索引
避开AWR的时间盲区:用ASH补全阻塞路径
AWR快照默认每小时一次,而 enq: TM - contention 往往集中在几分钟内爆发。若问题发生在两个快照之间,AWR只会显示“有等待”,但无法还原谁在等谁。这时必须查 dba_hist_active_sess_history:
用报告中给出的 SNAP_ID(比如 2226)和事件名反查具体会话链:
SELECT blocking_session, session_id, sql_id, event, p1, p2, sample_time FROM dba_hist_active_sess_history WHERE snap_id = 2226 AND event = 'enq: TM - contention' ORDER BY sample_time DESC;
关键看 blocking_session 是否非空。若为空,说明是资源争用(如多个会话同时申请同一TM锁),不是单点阻塞;若非空,则顺着 blocking_session 找它的 sql_id,再查该SQL是否在操作同一张父表或子表。
- 注意:
p1是锁类型+模式的组合值,对 TM 锁,BITAND(p1, 65535)可解出锁模式(如 3=ROW EXCLUSIVE),但日常排查优先看blocking_session和sql_id - ASH采样是秒级,但只记录非空闲会话;若阻塞发生在亚秒级,仍可能漏掉,此时需配合
v$session_wait实时抓取
别被“DML_LOCKS参数”带偏方向
有些文档建议调大 DML_LOCKS 参数来缓解 enq: TM - contention,这是危险操作。该参数控制的是每个事务最多可持有的DML锁数量,默认值 4 * TRANSACTIONS 已足够覆盖绝大多数场景。盲目调大会掩盖真实问题,还可能引发内存压力。
真正该做的,是确认是否存在以下情况:
- 批量操作(如夜维)集中执行
DELETE父表,触发大量级联子表扫描 - 应用使用了
ON DELETE CASCADE但子表数据量大且无索引 - 同一张表被多个模块高频并发 INSERT/DELETE,且存在未索引外键
这些场景下,AWR里 enq: TM - contention 的等待次数会随并发线程数线性增长,而不是偶发抖动。此时加索引或拆分事务粒度,效果远好于调参。











