<p>enq: TM - contention 是表级锁争用等待事件,主因是外键未建索引导致主表DML时全表扫描子表并持TM锁;需查P2得子表OBJECT_ID,再通过dba_constraints与dba_ind_columns确认缺失索引的外键列并创建匹配索引。</p>
enq: tm - contention 本身不是死锁,而是表级锁争用等待事件;oracle 检测到真死锁(ora-00060)时会主动回滚一个会话,但 enq: tm - contention 长时间挂住,可能诱发或掩盖死锁——比如多个会话在等同一张子表的 tm 锁,期间又互相持有 tx 行锁,最终触发死锁图检测。
真正要解决的,是让 TM 锁不被长时间持有,而不是“处理死锁”。
查 P2 对象 ID 确认是不是外键索引缺失
enq: TM - contention 的 P2 字段值是被阻塞对象的 OBJECT_ID,它**几乎从不指向你正在操作的主表,而是子表**。
常见错误是盯着 SQL 里的主表猛查索引,却漏掉子表。
- 执行:
SELECT owner, object_name, object_type FROM dba_objects WHERE object_id = &p2_value;
- 如果返回的是子表(如
ORDER_ITEMS、MSG_MSGREC),立刻检查它是否引用了某张主表的主键/唯一键 - 再查这个子表的外键列上有没有索引:
SELECT i.index_name, ic.column_name FROM dba_indexes i, dba_ind_columns ic WHERE i.index_name = ic.index_name AND i.owner = ic.owner AND i.table_name = '&child_table' AND ic.column_name IN (SELECT column_name FROM dba_cons_columns WHERE constraint_name IN (SELECT constraint_name FROM dba_constraints WHERE r_constraint_name IN (SELECT constraint_name FROM dba_constraints WHERE table_name = '&parent_table' AND constraint_type IN ('P','U')) AND constraint_type = 'R' AND table_name = '&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); - 子表是分区表?索引必须是
LOCAL,且覆盖所有分区;GLOBAL索引在某些版本下无法避免全子表扫描 - 不要重用已有索引:如果已有索引是
(status, order_id),而外键只含order_id,该索引无效——因为order_id不是前导列
为什么建索引能解决问题
Oracle 在主表执行UPDATE 主键或 DELETE 行时,必须校验子表无悬空引用。没索引 → 全表扫描子表 → 在子表上申请 TM 锁(MODE=4,Share Row Exclusive)→ 与子表上任何未提交的 DML(MODE=3)冲突 → 等待。
- 建索引后,校验变成索引范围扫描,不用锁整张子表
- 即使子表正有 100 个并发
INSERT事务,主表操作也不会被卡住 - 注意:
ON DELETE CASCADE场景下同样适用,且更易出问题(夜维批量删主表触发级联删子表)
其他容易被忽略的 TM 锁来源
外键索引缺失占 80%+,但还有几个硬茬:-
LOCK TABLE ... IN EXCLUSIVE MODE:显式锁表,会阻塞所有 DML,检查应用代码是否写了这类语句 - 并行 DML(如
/<em>+ APPEND </em>/):可能申请高阶 TM 锁(MODE=6),和外键无关,需结合P1的 lock_mode 判断(mod(P1, 16) > 4) - DDL 正在执行中(如
ALTER TABLE ... ADD COLUMN):会持 TM 锁,此时所有 DML 都得等,查v$session的sql_id和event可确认 - RAC 环境下 LGWR 或 LMS 进程异常:极少见,但若两个节点同时报告大量 TM 等待且 P2 不集中,需查集群日志
最麻烦的点在于:TM 锁争用本身不报错,只表现为慢或 hang;而死锁(ORA-00060)只是它恶化后的症状之一。定位时别被 trace 文件里的“Deadlock graph”带偏——先砍掉 enq: TM - contention 的根,死锁自然消失。


















