enq: tm - contention本质是多会话争抢表级tm锁的并发控制现象,高占比表明存在设计瓶颈;外键未建索引会导致主表dml时全表扫描子表并持exclusive tm锁,引发阻塞,需通过ash查p2定位对象、结合约束与索引视图确认缺失索引。

enq: TM - contention 不是锁被“卡住”了,而是多个会话在争抢同一张表的 TM 锁(Table-level DML lock),本质是并发控制机制在起作用。它本身不是故障,但高占比(比如 AWR 中 >5%)一定意味着业务或设计存在瓶颈,必须干预。
为什么外键没索引会导致enq: TM - contention
Oracle 在执行 DELETE 或 UPDATE 主表时,如果子表有 ON DELETE CASCADE 外键但没建索引,数据库必须全表扫描子表来确认约束完整性——这个过程需要持有子表的 TM 锁(mode=6,即 Exclusive),且持续到事务结束。其他会话哪怕只更新子表任意一行,都会因无法获取 TM 锁而等待。
- 典型场景:夜维程序批量删主表,触发级联删子表,子表无外键索引 → 全表扫描 + 长时间持锁
- 验证方式:查
v$session_wait中等待enq: TM - contention的会话,用P1解析锁名(DBMS_UTILITY.CONVERT_NUMBER_TO_VARCHAR2(P1)可得锁类型和模式),再结合blocking_session找出持锁 SQL - 注意:即使外键没
CASCADE,只要涉及INSERT/UPDATE到被引用列,且无索引,也会触发相同问题
如何快速定位缺失的外键索引
别靠猜,用脚本直接扫。以下查询能列出所有未被索引覆盖的外键列(含顺序):
SELECT
c.constraint_name,
c.table_name,
cc.column_name,
cc.position,
'CREATE INDEX idx_' || c.table_name || '_' || cc.column_name ||
' ON ' || c.table_name || '(' || cc.column_name || ');' AS ddl
FROM user_constraints c
JOIN user_cons_columns cc ON c.constraint_name = cc.constraint_name
WHERE c.constraint_type = 'R'
AND NOT EXISTS (
SELECT 1
FROM user_ind_columns ic
WHERE ic.table_name = c.table_name
AND ic.column_name = cc.column_name
AND ic.column_position = cc.position
)
ORDER BY c.table_name, cc.position;
- 重点看
position:外键多列时,索引字段顺序必须与外键定义完全一致,否则不生效 - 结果中若出现
idx_t_scs_order_order_id这类建议,说明t_scs_order表的外键列缺索引,立即建 - 建索引后,级联操作不再全表扫描,
TM锁持有时间从秒级降到毫秒级
DML_LOCKS 参数调大有用吗?
没用,还可能有害。该参数默认值为 4 * TRANSACTIONS,仅控制实例级可分配的 TM 锁总数,不是并发上限。真正瓶颈从来不在锁数量,而在锁持有时间。
- 调大
DML_LOCKS只会让更多会话排队等锁,而不是减少等待——AWR 里enq: TM - contention的平均等待时间(Avg Wait(ms))不会下降 - 若真遇到
ORA-00055: maximum number of DML locks exceeded,说明有大量未提交事务长期持锁,应查v$transaction和v$session找出长事务,而非调参 - 生产环境严禁随意修改该参数,它影响共享池中锁结构内存分配,改错可能引发 ORA-4031
并行 DML 和 APPEND 提示会加重 TM 锁争用
使用 /*+ APPEND */ 或 ALTER SESSION ENABLE PARALLEL DML 后,Oracle 会以 mode=6 持有目标表的 TM 锁,直到整个并行事务提交。这比普通 DML 更容易阻塞其他会话。
- 确认方法:查
v$sql中对应 SQL 的sql_profile或hints字段;或看执行计划是否有LOAD AS SELECT或PX相关步骤 - 临时规避:对高并发表禁用
APPEND,改用常规INSERT /*+ NOAPPEND */;并行 DML 改为串行执行 - 根本解法:这类操作应避开业务高峰,并确保目标表外键已建索引——否则并行只会放大争用
真实问题往往藏在“看似无关”的细节里:比如一个 ON DELETE CASCADE 外键没索引,会让整张子表变成单点瓶颈;又比如一条带 APPEND 的夜间导入 SQL,会把白天的更新全部堵死。解决 enq: TM - contention 的关键,从来不是调参或加资源,而是找到那个让锁持有时间异常延长的操作路径。











