死锁与阻塞:

阻塞:多个线程之间的相互影响,等待临界资源;

死锁:多个线程之间互相等待,形成等待环;

 

对于死锁,达梦数据库会自动检测,并选择牺牲掉其中的一个事务,保证其它事务正常运行。

死锁一般是由于应用程序bug导致的,当有修改多表的事务时,应尽量保证每个事务以相同的顺序锁定表。

可以通过V$DEADLOCK_HISTORY查询死锁历史,其中记录了产生死锁后被牺牲掉的事务的事务ID、会话ID、执行的SQL语句以及死锁发生时间(没有记录造成死锁的其它事务)。

 

对于阻塞,达梦数据库不会自动处理,被阻塞的事务会一直挂起,直到获取到所需的资源。

阻塞一般是由应用程序bug造成的,在应用编写中,应尽量形成短事务,快速提交。

 

什么情况下会形成阻塞?

在达梦数据库中,默认采用读提交的机制,查询永远不会被阻塞。查询一般情况下也不会阻塞增删改操作,SELECT FOR UPDATE的情况除外。

INSERT语句被阻塞的情况:多个事务同时向有主键或唯一约束的表中插入相同的数据;

删、改语句被阻塞的情况:所需要操作的数据被其它事务修改过,且一直没有提交或回滚;

 

查询阻塞的SQL语句:

WITH TRX_TAB AS
(SELECT DISTINCT O1.NAME,L1.TRX_ID FROM V$LOCK L1,SYSOBJECTS O1 WHERE L1.TABLE_ID = O1.ID AND O1.ID <> 0),
TRX_SESS AS
(SELECT L.TRX_ID WT_TRXID,L.TID BLK_TRXID,L.BLOCKED,(SELECT NAME TABLE_NAME FROM TRX_TAB A WHERE A.TRX_ID = L.TRX_ID) WT_TABLE,
S1.SESS_ID WT_SESS,S2.SESS_ID BLK_SESS,
S1.USER_NAME WT_USER_NAME,S2.USER_NAME BLK_USER_NAME,S1.SQL_TEXT,S1.CLNT_IP,DATEDIFF(SS,S1.LAST_SEND_TIME,SYSDATE) SS
FROM V$LOCK L,V$SESSIONS S1,V$SESSIONS S2
WHERE L.TRX_ID = S1.TRX_ID AND L.TID = S2.TRX_ID)
SELECT SYSDATE STATTIME,* FROM TRX_SESS WHERE BLOCKED = 1;

注:以上语句适用于V8环境,V7中不适用,因为V7和V8中V$LOCK里标识阻塞源头的事务ID的字段不一样了,V8中是TID,V7中是ROW_IDX。

 

如何跟踪是否有阻塞发生:

1. 创建辅助表,用于存阻塞记录;

CREATE TABLE BLK_HISTORY AS
WITH TRX_TAB AS
(SELECT DISTINCT O1.NAME,L1.TRX_ID FROM V$LOCK L1,SYSOBJECTS O1 WHERE L1.TABLE_ID = O1.ID AND O1.ID <> 0),
TRX_SESS AS
(SELECT L.TRX_ID WT_TRXID,L.TID BLK_TRXID,L.BLOCKED,(SELECT NAME TABLE_NAME FROM TRX_TAB A WHERE A.TRX_ID = L.TRX_ID) WT_TABLE,
S1.SESS_ID WT_SESS,S2.SESS_ID BLK_SESS,
S1.USER_NAME WT_USER_NAME,S2.USER_NAME BLK_USER_NAME,S1.SQL_TEXT,S1.CLNT_IP,DATEDIFF(SS,S1.LAST_SEND_TIME,SYSDATE) SS
FROM V$LOCK L,V$SESSIONS S1,V$SESSIONS S2
WHERE L.TRX_ID = S1.TRX_ID AND L.TID = S2.TRX_ID)
SELECT SYSDATE STATTIME,* FROM TRX_SESS WHERE BLOCKED = 1;

2. 创建存储过程,查询阻塞,并写入辅助表;

CREATE OR REPLACE PROCEDURE GATHER_BLK_HIS AS
BEGIN 
 INSERT INTO BLK_HISTORY 
 WITH TRX_TAB AS
(SELECT DISTINCT O1.NAME,L1.TRX_ID FROM V$LOCK L1,SYSOBJECTS O1 WHERE L1.TABLE_ID = O1.ID AND O1.ID <> 0),
 TRX_SESS AS
(SELECT L.TRX_ID WT_TRXID,L.TID BLK_TRXID,L.BLOCKED,(SELECT NAME TABLE_NAME FROM TRX_TAB A WHERE A.TRX_ID = L.TRX_ID) WT_TABLE,
 S1.SESS_ID WT_SESS,S2.SESS_ID BLK_SESS,
 S1.USER_NAME WT_USER_NAME,S2.USER_NAME BLK_USER_NAME,S1.SQL_TEXT,S1.CLNT_IP,DATEDIFF(SS,S1.LAST_SEND_TIME,SYSDATE) SS
 FROM V$LOCK L,V$SESSIONS S1,V$SESSIONS S2
 WHERE L.TRX_ID = S1.TRX_ID AND L.TID = S2.TRX_ID)
 SELECT SYSDATE STATTIME,* FROM TRX_SESS WHERE BLOCKED = 1;
 COMMIT;
END;

3. 配置定时作业,定时调用存储过程,收集阻塞记录(定时作业最短执行周期为一分钟,所以小于一分钟的阻塞可能不会被记录下来);

CALL GATHER_BLK_HIS();

 

分析阻塞与死锁是以事务为单位,而不是以单个SQL语句为单位。要想看到完整的事务,需要开启SQL日志,然后以记录的事务号去查询SQL日志。生产环境中,怕开启SQL日志影响性能的话,可以开异步日志,分析完后再关掉。

 

注:在阻塞过程中,也可以查询V$TRXWAIT获得当前等待事务与阻塞事务的事务ID,以及等待时间。