死锁概念
死锁是两个事务都在等待对方持有的资源锁,要等对方释放持有的资源锁之后才能继续工作,他们互不相让,坚持到底,双方都要等到对方完成之后才能继续工作,而以目前这种状态,双方都完成不了,陷入死循环了。
死锁与阻塞的不同之处在于死锁包括两个或者多个已阻塞事务,它们之间形成了等待环,每个都等待其他事务释放锁。例如事务 1 给表 T1 上了排他锁,第2个事务给表 T2 上了排他锁,此时事务 1 请求 T2 的排他锁,就会处于等待状态,被阻塞。若此时 T2 再请求表 T1 的排他锁,则 T2 也处于阻塞状态。此时这两个事务发生死锁,DM 数据库会选择牺牲掉其中一个事务。
为了更好的认知死锁的概念,这里我们来具体用案例来模拟死锁。
死锁模拟
需要提起说明的是,这里通过达梦数据库的manage工具进行模拟,对于manage中的每个窗口可以理解为一个事务。
--创建测试表
drop table student;
create table student(stuid int primary key,stuname varchar(100),stusex varchar(4),stuage int);
insert into student values(1,'张三','男',19);
insert into student values(2,'李四','女',20);
insert into student values(3,'王五','男',21);
insert into student values(4,'赵六','女',22);
commit;
--接下来我们模拟锁阻塞
--窗口1模拟事务1,对stuid=1的更新,不提交
update student set stuage=21 where stuid=1;
--窗口2模拟事务2,对stuid=2的更新,不提交
update student set stuage=10 where stuid=2;
--窗口1模拟事务1,对stuid=2的更新,不提交,此时产生锁阻塞
update student set stuage=40 where stuid=2;
--继续模拟一个新的锁阻塞,与之前的锁阻塞形成死锁
--窗口2模拟事务2,对stuid=1的更新,此时产生锁阻塞,两个锁阻塞形成死锁事件
update student set stuage=40 where stuid=2;
对于应用程序来说,死锁的事务会自动回滚,这里我们手动对窗口2的事务2直接回滚,此时窗口1的事务1自动执行完成。
死锁排查
由于死锁会被数据库系统自动识别并终止,所以从会话视图中是感受不到的,程序会反馈一个死锁错误,通过死锁历史动态视图V$DEADLOCK_HISTORY,可以查询到发生过的死锁信息。
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;
死锁解决方案
数据库的机制是当发生有死锁时会牺牲掉其中的一个进程来让其它进程继续执行下去。
这种情况是应用程序BUG产生的,需要调整程序的逻辑结构。在对多表进行操作的时候,尽量按照相同的顺序进行处理,避免同时锁定两个资源,必须同时锁定两个资源的时候,要保证在任何时候都应该按照相同的顺序来锁定资源。对于具体应用,可以通过事务ID去sql日志中查找对应的事务,确定完整的事务信息。
根据完整的事务信息和死锁规则,去找其他影响的事务操作,确定业务功能设计,从而优化处理逻辑,消除或者降低死锁频率。