oracle 12c中enq: tm - contention主因是子表外键列缺失索引,p2值指向子表object_id,需通过dba_objects确认阻塞对象,再联查dba_constraints与dba_ind_columns定位未索引外键并创建匹配顺序的local索引。

Oracle 12c 出现严重 enq: TM - contention 等待,95% 以上是子表外键列缺失索引导致的,不是数据库版本问题,而是约束校验机制触发整表 TM 锁争用。
查 P2 值确认阻塞对象是不是子表
这个等待事件的 P2 字段值是被阻塞对象的 OBJECT_ID,它几乎从不指向你正在操作的主表,而是子表。直接查:
SELECT owner, object_name, object_type FROM dba_objects WHERE object_id = &p2_value;
如果返回的是类似 ORDER_ITEMS、MSG_MSGREC 这类明显是子表的名字,就坐实了根因方向。别在主表上反复检查索引——那是白费时间。
验证外键是否真没索引:用 dba_constraints + dba_ind_columns 联查
先找出所有引用主表主键的子表约束:
SELECT c.owner, c.table_name AS child_table, c.constraint_name
FROM dba_constraints c
WHERE c.constraint_type = 'R'
AND c.r_constraint_name IN (
SELECT constraint_name
FROM dba_constraints
WHERE table_name = '&parent_table'
AND constraint_type IN ('P', 'U')
);
再对每个 child_table 检查其外键列是否有匹配索引:
- 外键是单列(如
order_id),索引必须以该列为前导列:CREATE INDEX idx_order_items_order_id ON order_items(order_id); - 外键是多列(如
(order_id, line_no)),索引列顺序必须一致且不能跳过前导列:CREATE INDEX idx_order_items_oid_lno ON order_items(order_id, line_no); - 已有索引是
(status, order_id)?无效——order_id不是前导列,Oracle 无法用它加速外键校验 - 子表是分区表?索引必须是
LOCAL,GLOBAL索引在某些 12c 补丁版本下仍会触发全扫描
为什么 12c 还要建这个索引?算法没变
Oracle 9i 后确实优化了外键锁申请逻辑,但「无索引时仍需全表扫描子表」这一行为没改。只要子表没索引,主表执行 DELETE 或 UPDATE 主键列,Oracle 就必须扫全表确认无悬空引用——这个过程需要在子表上申请 TM 锁(MODE=4),而该锁与子表上任何未提交的 DML(MODE=3)互斥。结果就是:一个会话卡在 enq: TM - contention,BLOCKING_SESSION 显示另一个会话正对子表做 INSERT 且没 COMMIT。
并行 DML、LOCK TABLE、RAC 的 GC 争用也可能引发该等待,但占比极低;生产环境里,先查 P2、再盯子表外键索引,5 分钟内就能定位到真正瓶颈。











