达梦数据库死锁与阻塞分析解决

一、阻塞与死锁的简单定义:

1、什么是死锁?

死锁:是两个事务都在等待对方持有的资源锁,要等待对方释放有的资源锁之后才能继续工作,两者互不想让,坚持到底,都在等待彼此完成才继续工作,就是这样的状态,双方都完成不了,从而陷入死循环。

遇到死锁解决策略:

1、数据库中的机制是当发生死锁时会牺牲其中的一个进程来让其继续执行下去。

2、那种情况是应用程序BUG产生的,需要调整程序的逻辑结构,在对多表进行操作时,尽量按照相同的顺序执行,避免同时锁定两个资源,必须同时锁定两个资源的时候,要保证在任何时候都要按照相同顺序来锁定资源。

2、什么是阻塞?

阻塞:是第一个事务占有资源没有释放,而第二个事务需要获取这个资源,如果第一个事务没有提交或者回滚,第二个事务会一直等待下去,直到第一个事务释放该资源为止

遇到阻塞解决策略:

1、被阻塞的事务会一直挂起、直到持有锁的事务放弃锁定的资源为止(提交/回滚)

2、程序应用bug产生,需要调整程序的逻辑结构,尽量形成短事务,快速提交,避免阻塞时间过长,不要将其他无关操作放到容易引起阻塞的模块。

二、达梦数据库死锁与阻塞解释

       阻塞和死锁是会与并发事务一起发生的两个事件,它们都与锁相关。当一个事务正在占用某个资源的锁,此时另一个事务正在请求这个资源上与第一个锁相冲突的锁类型时,就会发生阻塞。被阻塞的事务将一直挂起,直到持有锁的事务放弃锁定的资源为止。死锁与阻塞的不同之处在于死锁包括两个或者多个已阻塞事务,它们之间形成了等待环,每个都等待其他事务释放锁。例如事务1给表 T1 上了排他锁,第二个事务给表 T2 上了排他锁,此时事务 1请求 T2 的排他锁,就会处于等待状态,被阻塞。若此时 T2 再请求表 T1 的排他锁则T2 也处于阻塞状态。此时这两个事务发生死锁,DM 数据库会选择牺牲掉其中一个事务

         在DM 数据库中,INSERT、UPDATE、DELETE 是最常见的会产生阻塞和死锁的语句。                 INSERT 发生阻塞的唯一情况是,当多个事务同时试图向有主键或 UNIOUE 约束的表中插入相同的数据时,其中的一个事务将被阻塞,直到另外一个事务提交或回滚。一个事务提交时,另一个事务将收到唯一性冲突的错误:一个事务回滚时,被阻塞的事务可以继续执行。
        当 UPDATE 利 DELETE 修改的记录,已经被另外的事务修改过,将会发生阻塞,直到另一个事务提交或回滚。

三、如何排查死锁与阻塞

A、排查死锁方式:

1、使用系统视图查看死锁:

SELECT * FROM  V$DEADLOCK_HISTORY;

select dh.trx_id,sh.sess_id,wm_concat(top_sql_text) 
from V$DEADLOCK_HISTORY dh,V$SQL_HISTORY sh 
where dh.trx_id=sh.trx_id 
and dh.sess_id=sh.sess_id 
group by dh.trx_id,sh.sess_id;

查询结果:

达梦数据库死锁与阻塞分析解决_第1张图片

       由于死锁会被数据库系统自动识别并终止,所以从会话视图中是感受不到的,程序会反馈一个死锁错误,通过死锁历史动态视图V$DEADLOCK_HISTORY,可以查询到发生过的死锁信息。

2、通过事务ID(trx_id)去sql日志中查找对应的事务确定完整的事务信息。根据完整的事务信息和死锁规则,去找其他影响的事务操作确定业务功能设计从而优化处理逻辑消除或者降低死锁频率。

ps:sql日志在数据库安装目录的log文件夹里

B、排查阻塞方式

1、使用系统视图查询

SELECT SYSDATE AS STATTIME,
       DATEDIFF(SS,S1.LAST_SEND_TIME,SYSDATE) AS SS,
       '被阻塞的信息' AS WT,
	   S1.SESS_ID AS WT_SESS_ID,
	   S1.SQL_TEXT AS WT_SQL_TEXT,
       S1.STATE AS WT_STATE,
	   S1.TRX_ID AS WT_TRX_ID,
	   S1.USER_NAME AS WT_USER_NAME, 
       S1.CLNT_IP AS WT_CLNT_IP,
	   S1.APPNAME AS WT_APPNAME,
	   S1.LAST_SEND_TIME AS WT_LAST_SEND_TIME,
	   '引起阻塞的信息' AS FM,
	   S2.SESS_ID AS FM_SESS_ID,
	   S2.SQL_TEXT AS FM_SQL_TEXT,
	   S2.STATE AS FM_STATE,
	   S2.TRX_ID AS FM_TRX_ID,
	   S2.USER_NAME AS FM_USER_NAME,
	   S2.CLNT_IP AS FM_CLNT_IP,
	   S2.APPNAME AS FM_APPNAME,
       S2.LAST_SEND_TIME AS FM_LAST_SEND_TIME
FROM V$SESSIONS S1,
V$SESSIONS S2 ,V$TRXWAIT W
WHERE S1.TRX_ID = W.ID
AND S2.TRX_ID = W.WAIT_FOR_ID;

达梦数据库死锁与阻塞分析解决_第2张图片

SELECT timestampdiff(ss,LAST_RECV_TIME,SYSDATE) AS SS,
SF_GET_SESSION_SQL(SESS_ID) AS SESS_ID , * 
FROM SYS."V$SESSIONS"
where SESS_ID <> SESSID
ORDER BY 1 DESC;

达梦数据库死锁与阻塞分析解决_第3张图片

2、通过事务ID(trx_id)去sql日志中查找对应的事务确定完整的事务信,和查询中被阻塞的事务sql进行对照确认。

ps:sql日志在数据库安装目录的log文件夹里

四、怎样解锁死锁与阻塞

       死锁不用人工干涉,数据库系统自动识别并解除。但是死锁是非正常情况,需要找到死锁原因后,从应用逻辑层解决。
      阻塞 可能是应用流程涉及提交慢导致,但是最终也会提交或者回滚,所以一般情况下也会自动消除。如果阻塞很长时间没有消除,在数据库层可以通过 SP_CLOSE_SESSION(SESSID);来强制终阻塞源头的会话使其回滚,来解除阻塞,但是这个需要用户确认,不能私自操作。

1、从系统时间中可以查看锁等待事件和地锁事件发生的次数和时间来评估是否存在死锁和阻塞

查询语句:

SELECT * FROM V$SYSTEM_EVENT ORDER BY TIME_WAITED DESC;

2、可以通过辅助表跟踪阻塞

STEP1:创建辅助记录表:

CREATE TABLE TRX_WAIT(
"STATTIME" TIMESTAMP,
"SS" INTEGER,
"WT" VARCHAR2(30),
"WT_SESS_ID" BIGINT,
"WT_SQL_TEXT" VARCHAR(1000),
"WT_STATE" VARCHAR(10),
"WT_TRX_ID" BIGINT,
"WT_USER_NAME" VARCHAR(128),
"WT_CLNT_IP" VARCHAR(128),
"WT_APPNAME" VARCHAR(128),
"WT_LAST_SEND_TIME" DATETIME(6),

"FM" VARCHAR2(30),
"FM_SESS_ID" BIGINT ,
"FM_SQL_TEXT" VARCHAR(1000),
"FM_STATE" VARCHAR(10),
"FM_TRX_ID" BIGINT,
"FM_USER_NAME" VARCHAR(128),
"FM_CLNT_IP" VARCHAR(128),
"FM APPNAME" VARCHAR(128),
"FM LAST SEND TIME"DATETIME(6)
);

STEP2:创建辅助存储过程GET_TX_WAIT

CREATE PROCEDURE GET_TX_WAIT AS
BEGIN
INSERT INTO TRX_WAIT
SELECT SYSDATE AS STATTIME,
       DATEDIFF(SS,S1.LAST_SEND_TIME,SYSDATE) AS SS,
       '被阻塞的信息' AS WT,
	   S1.SESS_ID AS WT_SESS_ID,
	   S1.SQL_TEXT AS WT_SQL_TEXT,
       S1.STATE AS WT_STATE,
	   S1.TRX_ID AS WT_TRX_ID,
	   S1.USER_NAME AS WT_USER_NAME, 
       S1.CLNT_IP AS WT_CLNT_IP,
	   S1.APPNAME AS WT_APPNAME,
	   S1.LAST_SEND_TIME AS WT_LAST_SEND_TIME,
	   '引起阻塞的信息' AS FM,
	   S2.SESS_ID AS FM_SESS_ID,
	   S2.SQL_TEXT AS FM_SQL_TEXT,
	   S2.STATE AS FM_STATE,
	   S2.TRX_ID AS FM_TRX_ID,
	   S2.USER_NAME AS FM_USER_NAME,
	   S2.CLNT_IP AS FM_CLNT_IP,
	   S2.APPNAME AS FM_APPNAME,
       S2.LAST_SEND_TIME AS FM_LAST_SEND_TIME
FROM V$SESSIONS S1,
V$SESSIONS S2 ,V$TRXWAIT W
WHERE S1.TRX_ID = W.ID
AND S2.TRX_ID = W.WAIT_FOR_ID;
COMMIT;
END;

STEP3:创建作业定时收集阻塞数据:

想要了解更多:达梦数据库 - 新一代大型通用关系型数据库 | 达梦在线服务平台

你可能感兴趣的:(数据库)