背景
最近在做的项目是银行的项目,由于现在的银行项目一般都要求数据库国产化,那么首选的国产化数据库就是达梦数据库了,在使用达梦数据库的时候,会遇到死锁的情况,那么遇到这种死锁情况该如何快速处理呢?今天主要先来模拟一下死锁,然后在解决死锁。可以让大家更直观的看到死锁的处理效果。
模拟阻塞产生
这里我在本地达梦数据库首先创建一个表,然后插入一条数据,但是不关闭查询框,不提交 COMMIT 事务。新建SQL查询语句窗口 A
CREATE TABLE TEST.TBL_TEST(ID INT PRIMARY KEY, NAME VARCHAR(50)); INSERT INTO TEST.TBL_TEST VALUES(1, '中文');
执行当前语句可以看到执行效果
再新建SQL查询语句窗口B,同时输入查询语句
INSERT INTO TEST.TBL_TEST VALUES(1, '中文');
执行语句可以看到如下的执行效果
这里显示【语句依次执行...】但是一直没有执行成功,其实就是对于主键约束字段,窗口A插入sql一直没有提交,窗口B再次插入相同主键数据时就会阻塞。
查询阻塞语句
这里通过 V$TRXWAIT 视图查看进程,找到ID,ID对应着V$SESSIONS视图的SESS_ID
select * from V$TRXWAIT ;
执行查询死锁语句后可以看到
其中:WAIT_FOR_ID 表示正在执行的事务ID,也就是说事务ID 12155 在等待 12154执行完成。
再通过V$SESSIONS视图,查找对应的ID号确定阻塞语句
SELECT SESS_ID,SQL_TEXT,TRX_ID from V$SESSIONS;
执行查询语句之后就可以看到具体的sql 阻塞内容
解决阻塞
解决阻塞其实也简单,当你找到需要解决的阻塞产生的SESS_ID了,你就可以调用 SP_CLOSE_SESSION(SESS_ID);系统函数来关闭会话
SP_CLOSE_SESSION(1866105112); SP_CLOSE_SESSION(1859760408);
执行关闭回话系统函数之后,查看执行效果
再次查询产生阻塞产生的sql 内容查询语句
SELECT SESS_ID,SQL_TEXT,TRX_ID from V$SESSIONS;
可以看到刚才产生阻塞的两条语句已经没有了
那么到这里在遇到阻塞问题时的解决办法也就说完了,比较简单。当dm.ini中的参数ENABLE_MONITOR=1时,使用死锁历史动态试图V$DEADLOCK_HISTORY,可以查询到发生过的死锁信息。
SELECT * FROM V$DEADLOCK_HISTORY;
避免死锁
那么如何避免死锁呢,通常业务情况下,可能会出现这样的场景,比如数据库有两条数据,两个进程或者线程来操作这两条数据。首先查询数据表
SELECT * FROM TEST.TBL_TEST;
这时候你打开两个SQL查询窗口,在窗口A执行语句
UPDATE TEST.TBL_TEST SET NAME='1111' WHERE ID = 1;
在窗口B执行语句
UPDATE TEST.TBL_TEST SET NAME='2222' WHERE ID = 2;
在窗口A继续执行语句
UPDATE TEST.TBL_TEST SET NAME='2222' WHERE ID = 2;
在窗口B继续执行语句
UPDATE TEST.TBL_TEST SET NAME='1111' WHERE ID = 1;
此时可以看到窗口A的第二条更新语句一直显示 【语句正依次执行...】
而窗口B的第二条语句已经显示【死锁】了
死锁与阻塞的不同之处在于死锁包括两个或者多个已阻塞事务,它们之间形成了等待环,每个都等待其他事务释放锁。例如事务1给表T1上了排他锁,第二个事务给表T2上了排他锁,此时事务1请求T2的排他锁,就会处于等待状态,被阻塞。若此时T2再请求表T1的排他锁,则T2也处于阻塞状态。此时这两个事务发生死锁,DM数据库会选择牺牲掉其中一个事务。
死锁的本质也是锁等待,所以解决锁等待的问题,就解决了死锁的问题。