AWR中enq: TM - contention需满足排前3、Total Wait Time占比>15%且Avg wait (ms)>50才表明外键索引缺失等结构性问题;若平均等待时间低于10ms,则多为短时争用,非索引缺陷。

AWR里一眼识别enq: TM - contention是否真由外键索引缺失引起
别一看到enq: TM - contention就急着建索引。先看AWR报告的Top 10 Foreground Events by Total Wait Time部分:如果它排进前3,且Total Wait Time占比 >15%、Avg wait (ms) > 50,才值得深挖;若平均等待
再翻到SQL Statistics → SQL ordered by Elapsed Time,找那些耗时长但%CPU + %IO极低(比如总和 DELETE FROM MSG_MESSAGE01 WHERE id = :1 耗时280秒,CPU仅0.3%,IO仅1.2%。
关键佐证点:去Segments by Logical Reads和Segments by Row Lock Waits里搜这条SQL涉及的表名,如果完全没出现,说明锁不在它自己身上,而在子表约束检查环节。
用AWR线索反查外键关系与子表索引状态
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
LEFT JOIN user_ind_columns b
ON a.table_name = b.table_name
AND a.column_name = b.column_name
AND a.position = b.column_position
WHERE c.r_constraint_name IN (
SELECT constraint_name
FROM user_constraints
WHERE table_name = 'MSG_MESSAGE01'
AND constraint_type = 'P'
)
AND c.constraint_type = 'R';
结果中若某子表(如MSG_MSGREC)对应外键列的index_name为NULL,就是缺口所在。注意:外键列顺序必须和索引列顺序一致,否则仍无效。
常见陷阱:user_ind_columns只查当前用户下的索引;跨schema外键需用all_视图,并确认owner匹配。
为什么没索引会导致TM锁长时间持有
Oracle执行DELETE或UPDATE主表时,只要子表有外键约束(无论是否带ON DELETE CASCADE),就必须校验子表中是否存在引用行。若外键列无索引,数据库只能全表扫描子表——这个扫描过程需要持TM锁(mode=6,即Exclusive),且持续到整个事务结束。
此时其他会话哪怕只是UPDATE子表任意一行,也会因无法获取TM锁而陷入enq: TM - contention等待。
- 不是锁“卡住”,而是多个会话争抢同一张子表的TM锁
- 即使外键没级联,只要DML涉及被引用列(如
INSERT INTO child (parent_id) VALUES (123)),同样触发全表扫描 - RAC环境下还可能叠加DRM(Dynamic Resource Management)开销,加剧等待
验证修复效果的关键观察点
建完索引后别只看enq: TM - contention等待是否消失——要盯三个地方:
在下一份AWR中检查:Segments by Table Scans里该子表的扫描次数是否断崖下降;SQL Statistics → SQL ordered by Gets Per Execution中原来那条DELETE语句的Buffer Gets是否从百万级降到千级以内;同时确认v$segment_statistics里该索引的logical reads开始上升,证明它真被用上了。
最容易被忽略的是:索引建完但统计信息未更新,CBO仍可能拒绝走新索引。务必在建索引后立刻执行DBMS_STATS.GATHER_TABLE_STATS,且method_opt要包含外键列,比如'FOR COLUMNS parent_id SIZE AUTO'。











