博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SQL SERVER中的两种常见死锁及解决思路
阅读量:5367 次
发布时间:2019-06-15

本文共 3867 字,大约阅读时间需要 12 分钟。

sql server中,死锁都与一种锁有关,那就是排它锁(x锁)。由于在同一时间对同一个资源只能有一个进程可以拥有排它锁。因此,一旦多个进程都需要获取某个或者同一个资源的排它访问权,而又被对方所阻止的时候,死锁就会出现。

 

第一种就是最经典的race condition思路,两个进程,ab,则a进程中修改数据表t1(假设id=100),再修改数据表t2(假设id=200);而在进程b中修改数据表t2id=200),然后再修改表t1id=100),当两个进程在并发的情况下,就会出现a尝试获取t2的排他锁或意向排他锁,b尝试获取t1的排他锁或意向排他锁的情况,由于a已经占有了t1的排他锁,b占有了t2的排他锁,因此,进程a和进程b一直处于僵持地步,从而造成了死锁。

 

脚本演示:

 

进程1:begin tranupdate customer set name='test' where id=2waitfor delay '00:00:20';update bill set remark=remark+':test' where id=2;commit tran进程2:begin tranupdate bill set remark=remark+':test' where id=2;waitfor delay '00:00:20';update customer set name='test' where id=2;commit tran

 

两个进程同时执行,发现存在死锁,选择一个牺牲品(victim),并直接将其kill掉:

msg 1205, level 13, state 51, line 6

transaction (process id xx) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. rerun the transaction.

打开1222 traceerror log,我们发现服务记录了此次死锁的最为详细的信息。

2011-01-28 23:12:59.82 spid16s       resource-list

2011-01-28 23:12:59.82 spid16s      

  keylock hobtid=72057594042515456 dbid=6 objectname=test.dbo.customer indexname=pk_customer id=lock4203a80 mode=x associatedobjectid=72057594042515456

2011-01-28 23:12:59.82 spid16s         owner-list

2011-01-28 23:12:59.82 spid16s          owner id=process398d48 mode=x

2011-01-28 23:12:59.82 spid16s         waiter-list

2011-01-28 23:12:59.82 spid16s          waiter id=process399108 mode=x requesttype=wait

 

2011-01-28 23:12:59.82 spid16s       

 keylock hobtid=72057594043236352 dbid=6 objectname=test.dbo.bill indexname=pk_bill id=lock4205200 mode=x associatedobjectid=72057594043236352

2011-01-28 23:12:59.82 spid16s         owner-list

2011-01-28 23:12:59.82 spid16s          owner id=process399108 mode=x

2011-01-28 23:12:59.82 spid16s         waiter-list

2011-01-28 23:12:59.82 spid16s          waiter id=process398d48 mode=x requesttype=wait

 

 

从这一段日志中我们可以看到,资源列表中有两个资源,每个资源都处于排它锁状态,同时每个进程的请求类型都为等待,也就是等待对方释放对自己所需资源的排它锁。

 

第二种死锁是由底层在锁的转换时出现僵持情况造成的。例如,两个进程在各自的事务中都获取了表t中某行(id=300)的共享锁,而都需要对该行做修改,那么两个进程都要获取该行的意向排他锁,由于两个进程都拥有该行的共享锁,因此两个进程出现争端,从而产生死锁。对于这种死锁,选择一个牺牲品并终止它,从而来解决死锁问题。

 

脚本演示

--两个或多个进程同时执行如下脚本:begin transelect * from customer where id=2;waitfor delay '00:00:05';update customer set name=name+'a' where id=2;commit tran

这样两个进程就出现了死锁,提供仲裁,选择一个牺牲品来自动解除死锁:

msg 1205, level 13, state 51, line 8

transaction (process id xx) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. rerun the transaction.

  打开1222 traceerror log,我仍旧摘取最后一段:

  

2011-01-28 23:52:48.52 spid14s       resource-list

2011-01-28 23:52:48.52 spid14s     

   keylock hobtid=72057594042515456 dbid=6 objectname=test.dbo.customer indexname=pk_customer id=lock420de80 mode=s associatedobjectid=72057594042515456

2011-01-28 23:52:48.52 spid14s         owner-list

2011-01-28 23:52:48.52 spid14s          owner id=process38ad48 mode=s

2011-01-28 23:52:48.52 spid14s          owner id=process398f28 mode=s

2011-01-28 23:52:48.52 spid14s         waiter-list

2011-01-28 23:52:48.52 spid14s          waiter id=process38ad48 mode=x requesttype=convert

2011-01-28 23:52:48.52 spid14s          waiter id=process398f28 mode=x requesttype=convert

 

 

 

 第一种死锁的requesttypewait,而第二种死锁的requesttypeconvert

 

因为两种死锁产生的情形是不同的,第一种死锁是相互锁定对方需要的资源、阻止对方获取所需资源的排他访问权所造成的。第二种死锁是共同拥有同一资源的共享访问权,都在要求获取排它访问权而造成的。

 

 

从第一种死锁产生的情况看,在比较复杂的业务逻辑中,访问的顺序一定要协调好,如果出现混乱,那么极有可能出现这种不必要的麻烦。

 

而第二种死锁似乎我们对此无能为力,因为在处理从共享锁到排它锁转换的过程由来操纵。而最讨厌的是,经常会从event view中能够看到此类死锁的身影,查遍了很多地方都找不到原因。

 

 

我们仔细观察我在模拟这种死锁的sql脚本中,我在select语句之后增加了waitfor语句等待5秒钟,这是最为关键的地方,如果我去掉等待,那么我用手动是几乎不可能模拟出由共享锁升级到排它锁的死锁的,如果我再去掉select语句,那么就绝对不会有此类死锁了,一次更新就是一个更新锁(u锁),对同一个资源,sql server是不会允许多于一个进程在申请同一个资源时存在交叉。那么同样的道理,之所以出现这种死锁,就是由于让多于一个进程拥有了同一个资源的共享锁所导致的。我不能说我的这种理解非常恰当,但是从这种死锁所产生的场景来看,只要避免共享锁过早被占,就能够解决此类死锁。

避免共享锁过早被占,其实还可以解决另外一个问题,那就是不可重复读的问题。因为共享锁过早被占,因为这在不同的进程中,资源被过早的检索出来,这样就会导致不同进程的操作被覆盖,而不是累加。

 

 

那么在开发中,如何做来避免这种死锁呢?通过上面的分析,我的建议是对于要求比较高而且操作比较频繁、复杂的资源上,使用sqltransaction(isolation level=serializable),它采用的是悲观的锁定策略,在不同的进程中可以确保进程等待,而不会出现共享锁提前被占用的情况。

 

 

参考另一篇:

 

 

 

 

转载于:https://www.cnblogs.com/sdadx/p/9098987.html

你可能感兴趣的文章
如何辨别一个程序员的水平高低?是靠发量吗?
查看>>
linux的子进程调用exec( )系列函数
查看>>
zju 2744 回文字符 hdu 1544
查看>>
【luogu P2298 Mzc和男家丁的游戏】 题解
查看>>
前端笔记-bom
查看>>
上海淮海中路上苹果旗舰店门口欲砸一台IMAC电脑维权
查看>>
给mysql数据库字段值拼接前缀或后缀。 concat()函数
查看>>
迷宫问题
查看>>
【FZSZ2017暑假提高组Day9】猜数游戏(number)
查看>>
练习10-1 使用递归函数计算1到n之和(10 分
查看>>
Oracle MySQL yaSSL 不明细节缓冲区溢出漏洞2
查看>>
Code Snippet
查看>>
zoj 1232 Adventure of Super Mario
查看>>
组合数学 UVa 11538 Chess Queen
查看>>
Redis常用命令
查看>>
[转载]电脑小绝技
查看>>
thinkphp如何实现伪静态
查看>>
BZOJ 1925: [Sdoi2010]地精部落( dp )
查看>>
Week03-面向对象入门
查看>>
一个控制台程序,模拟机器人对话
查看>>