客户反映前台业务卡住了,怀疑是有锁表,锁表时查询等待事件是enq: TM - contention,因为是业务高峰期,所以直接把锁杀掉了,事后想要查一下锁表原因。
客户数据库环境是11g rac,我采集了锁表时间段两节点的awr报告。发现等待事件排在第一位的都是enq: TM - contention。
节点一等待事件:
节点二等待事件:
节点一SQL ordered by Elapsed Time:
节点二SQL ordered by Elapsed Time:
节点二top sql对表MSG_MESSAGE01插入的等待事件过长。同时发现同一时间段还有对该表MSG_MESSAGE01的删除操作。但是一般情况下对同一表同时进行insert和delete操作不会导致TM锁,所以我们还需要进一步确认是否是这个delete操作导致的死锁。
这里我们需要查询一下发生死锁时间段的等待事件的源头。这里我选择了10点到10点30,我尝试过查询十点之前的发现并没有等待事件。我们可以查询相应时间段v$active_session_history或者dba_hist_active_sess_history视图
select * from v$active_session_history
where sample_time >
to_timestamp('2024-02-02 10:00:00',
'yyyy-mm-dd hh24:mi:ss')
and sample_time <
to_timestamp('2024-02-02 10:30:00',
'yyyy-mm-dd hh24:mi:ss');
查询到blocking_session锁对应的sql_id:6qmt9una23q3s 就是节点二上对表MSG_MESSAGE01进行delete的那条sql。
锁的源头找出来了,那么问题来了,一个常规的delete操作为什么会导致锁的?
有一种可能就是,MSG_MESSAGE01表是某张表的父表,在删除父表记录的之前要将子表对应数据先删掉,而该子表数据量比较多并且对应的外键上没有索引,索引才导致删除表MSG_MESSAGE01记录的时候产生等待。
查找与表MSG_MESSAGE01关联的子表:
这里要用表所属的用户查询,才能查到,我也不知道为什么。
select * from user_constraints where
R_CONSTRAINT_NAME in (select constraint_name from user_constraints where table_name = 'MSG_MESSAGE01') ;
这里查到两个与表MSG_MESSAGE01关联的子表。其中表MSG_MSGREC数据量为5万,其键值关系和索引如下图所示:
MSG_MSGREC表的外键上确实没有索引,这就会导致,在删除父表记录的时候,会全表扫描MSG_MSGREC表。
我们只需在MSG_MSGREC表外键上加上索引即可。