从ASH中揪出enq: TM - contention真凶需三步:先查v$active_session_history中event='enq: TM - contention'的样本;重点看p2(被争抢对象object_id),若指向子表而操作主表,极可能因外键无索引导致全表扫描持锁;再结合blocking_session(RAC下须用final_blocking_session)定位阻塞源头。

怎么从ASH里一眼揪出enq: TM - contention的真凶
ASH不是用来“看到锁”,而是帮你定位谁在等、谁在卡、等了多久——关键就看三列:event、p2、blocking_session。别一上来就查SQL,先过滤等待事件本身:
- 用
SELECT * FROM v$active_session_history WHERE event = 'enq: TM - contention'拉出样本 - 重点盯
p2字段:它就是被争抢对象的object_id,不是文件号也不是块号,直接拿去查dba_objects - 如果
blocking_session非空,说明有明确阻塞者;但注意:RAC环境下它只显示本实例的中间节点,得查gv$active_session_history并带上final_blocking_session和final_blocking_instance
为什么p2指向子表,你却在操作主表
这是外键未建索引最典型的信号。Oracle在DELETE/UPDATE主表时,若子表外键列没索引,就必须全表扫描子表做引用校验——这个动作会申请子表的TM锁(mode=4或6),而其他会话只要对子表做任何DML,就会因锁不兼容而卡在 enq: TM - contention 上。
- 查出
p2对应的表名后,立刻运行:SELECT constraint_name, r_constraint_name FROM dba_constraints WHERE table_name = '' AND constraint_type = 'R' - 再确认该外键列是否已有索引:
SELECT index_name FROM dba_ind_columns WHERE table_name = '' AND column_name IN (SELECT column_name FROM dba_cons_columns WHERE constraint_name = '') - 如果返回空,就是根因:缺索引 → 全表扫描 → 持锁 → 阻塞
ASH里看到TRUNCATE也在等enq: TM - contention,怎么办
TRUNCATE本质是DDL操作,需要排他性TM锁,但它不关心外键,只认“有没有人在动这张表”。所以它卡住,往往不是设计问题,而是有人拿着未提交事务占着锁。
- 查ASH中
program字段为oracle@host (J000)或oracle@host (M000)的会话,大概率是物化视图刷新或JOB触发的TRUNCATE - 顺着
blocking_session找过去,90%会发现一个长时间未提交的INSERT/UPDATE语句,比如批处理脚本漏了COMMIT - 特别注意:TRUNCATE还可能等
enq: DDL - contention,那是另一个会话正在ALTER/DROP同一张表,和TM锁无关,别混查
容易被忽略的两个硬坑
一是分区表外键索引必须是LOCAL且覆盖所有分区,否则仍会退化成全局扫描;二是并行DML(如 /*+ APPEND */)也会申请高阶TM锁,但它和外键无关,查 v$session 的 sql_plan_operation 能看到 LOAD AS SELECT 这类线索。这两个点一旦忽略,补了索引也解不了锁。


















