阻塞的产生往往与事务的并发一起出现,产生的原因与锁有关。当一个事务正在占用某个资源的锁,此时另一个事务正在请求这个资源上与第一个锁相冲突的锁类型时 ,就会发生阻塞。被阻塞的事务将一直挂起,直到持有锁的事务放弃锁定的资源为止。
举个例子:事务1对TABLE1执行写操作(INSERT、DELETE、UPDATE等DML语句),会给TABLE1上一个排他锁,此时事务2请求事务1的排他锁,就会处于等待状态(事务1此时没有执行完毕,因此表处于被锁状态),产生阻塞。
我们通过实际操作来查看这中情况:
1、首先,创建一个表TEST2
CREATE TABLE "SYSDBA"."TEST2" ( "NAME" VARCHAR(20), "ID" INT NOT NULL, NOT CLUSTER PRIMARY KEY("ID") ) ;
2、之后,需要打开两个SYSDBA的连接模拟并发的情况 :
3、在连接1中插入一条数据并不执行commit;
4、在连接2中同样插入该条数据
因为第一条插入的数据没有commit的原因,事务没有结束,此时因为存在主键约束,虽然在连接2中查不到对应的数据,但同样无法插入,此时发生阻塞。
5、此时我们通过v$trxwait视图可以看到id为47811的事务被阻塞了,那么这个47811是什么呢?
6、此时可以从v$session视图中看到,TRX_ID为47811正是连接2的插入语句。
那么此时阻塞已经产生了,如何接触阻塞呢,共有两种方法:
1)、在连接1处提交或回滚事务,
即commit或rollback
连接1执行rollback之后回滚自己的事务,连接2便可以成功插入数据
此时连接2执行语句但没有commit,当连接1执行相同的插入语句时会被再次阻塞,此时连接2执行commit提交事务
结果如图:
连接1显示执行失败,违反表的唯一性约束。
2)调用SP_CLOSE_SESSION(SESS_ID);系统函数来关闭会话。
连接2结果如图:
会话关闭之后会话2的操作被取消 。
以上是插入过程中出现阻塞的情况,实际上,在事务1和事务2同时修改或删除一个表中一条数据的情况下产生阻塞的情况会发生的更多。
死锁与阻塞的不同之处在于死锁包括两个或者多个已阻塞事务,它们之间形成了等待环,每个都等待其他事务释放锁。
用上述阻塞的例子,我们再假设这样一种情况:事务 1 给表 TABLE1 上了排他锁,第二个事务给表 TABLE2 上了排他锁 ,此时事务1请求 T2 的排它锁,就会处于等待状态,被阻塞。 若此时事务2 再请求表 TABLE1 的排他锁,则事务2 也处于阻塞状态。此时这两个事务发生死锁
模拟:
1、创建一个表名为TEST3
create table test3( id2 int PRIMARY key, name2 VARCHAR(20) )
2、为表TEST3插入一条数据(1,'name1')
insert into SYSDBA.TEST3 VALUES(1,'name1');
3、此时事务1修改TEST2的name,事务2修改TEST的name且都不提交事务。
#事务1 update SYSDBA.TEST2 set name='name2' where id2=1; #事务2 update SYSDBA.TEST3 set name2='name2' where id2=1;
4、两个事务在都占有对应的排他锁时,再互相访问对方的排他锁(根据阻塞原理,此时双方都会被阻塞),两个事务都不会放弃自己所持有的锁,同时都不能访问对方的锁,因此进入循环等待状态,产生死锁。依次执行下列语句:
#事务1 update SYSDBA.TEST3 set name2='name3' where id2=1; #事务2 update SYSDBA.TEST2 set name='name3' where id=1;
5、最终结果如下:
事务1 处于等待状态,事务2被检测处死锁,语句执行失败,这是因为DM8在检测出死锁时会自动牺牲一个事务(事务2)
6、死锁状态被解除之后,我们可以通过v$lock、 v$trxwait、v$sessions、v$trx几个视图确定锁的状态。使用上方解除阻塞的方法即可解决问题:
想了解更多达梦数据库相关知识,欢迎访问达梦云适配中心https://eco.dameng.com/