如何减少SQLServer死锁发生

如何减少SQLServer死锁发生,第1张

死锁是指在某组资源中 两个或两个以上的线程在执行过程中 在争夺某一资源时而造成互相等待的现象 若无外力的作用下 它们都将无法推进下去 死时就可能会产生死锁 这些永远在互相等待的进程称为死锁线程 简单的说 进程A等待进程B释放他的资源 B又等待A释放他的资源 这样互相等待就形成死锁

如在数据库中 如果需要对一条数据进行修改 首先数据库管理系统会在上面加锁 以保证在同一时间只有一个事务能进行修改 *** 作 如事务 的线程 T 具有表A上的排它锁 事务 的线程T 具有表B上的排它锁 并且之后需要表A上的锁 事务 无法获得这一锁 因为事务 已拥有它 事务 被阻塞 等待事务 然后 事务 需要表B的锁 但无法获得锁 因为事务 将它锁定了 事务在提交或回滚之前不能释放持有的锁 因为事务需要对方控制的锁才能继续 *** 作 所以它们不能提交或回滚 这样数据库就会发生死锁了

如在编写存储过程的时候 由于有些存储过程事务性的 *** 作比较频繁 如果先锁住表A 再锁住表B 那么在所有的存储过程中都要按照这个顺序来锁定它们 如果无意中某个存储过程中先锁定表B 再锁定表A 这可能就会导致一个死锁 而且死锁一般是不太容易被发现的

如果服务器上经常出现这种死锁情况 就会降低服务器的性能 所以应用程序在使用的时候 我们就需要对其进行跟踪 使用sp_who和sp_who 来确定可能是哪些用户阻塞了其他用户 我们还可以用下面的存储过程来跟踪具体的死锁执行的影响

create  procedure sp_who_lock

as

begin

declare @spid int @bl int @intTransactionCountOnEntry

int @intRowcount

int @intCountProperties

int @intCounter

int create table

#tmp_lock_who

(id int identity( ) spid *** allint bl *** allint)IF @@ERROR<>RETURN

@@ERRORinsert into

#tmp_lock_who(spid bl) select

blockedfrom (select * from sysprocesses where

blocked>)

a where not exists(select * from (select * from sysprocesses where  blocked>)

b where a blocked=spid)union select spid blocked from sysprocesses where

blocked>IF

@@ERROR<>RETURN @@ERROR 找到临时表的记录数select

@intCountProperties = Count(*) @intCounter = from #tmp_lock_whoIF

@@ERROR<>RETURN @@ERROR if @intCountProperties= select

现在没有阻塞和死锁信息

as message 循环开始while @intCounter <= @intCountPropertie *** egin 取第一条记录select

@spid = spid @bl = blfrom #tmp_lock_who where id = @intCounter beginif @spid = select

引起数据库死锁的是: + CAST(@bl AS VARCHAR( )) + 进程号

其执行的SQL语法如下 elseselect

进程号SPID + CAST(@spid AS VARCHAR( ))+ 被 +

进程号SPID + CAST(@bl AS VARCHAR( )) + 阻塞

当前进程执行的SQL语法如下 DBCC INPUTBUFFER (@bl )end

循环指针下移set @intCounter = @intCounter + enddrop table #tmp_lock_who

return

我们只需要通过在查询分析器里面执行sp_who_lock 就可以具体捕捉到执行的堵塞进程 这时我们就可以对对应的SQL语句或者存储过程进行性能上面的改进及设计

所以我们在数据库设计的时候 虽然不能完全避免死锁 但可以使死锁的数量尽量减少 增加事务的吞吐量并减少系统开销 因为只有很少的事务 所以就得遵循下面的原则

按同一顺序访问对象

如果所有并发事务按同一顺序访问对象 则发生死锁的可能性会降低 在写SQL语句或存储过程的时候 就需要按照顺序在两个并发事务中先获得表A上的锁 然后获得表B上的锁 当第一个事务完成之前 另一个事务被阻塞在表A上 第一个事务提交或回滚后 第二个事务继续进行 而不能在语句里面写先获得表B上的锁 然后再获得表A的锁

避免事务中的用户交互

避免编写包含用户交互的事务 因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度 例如答复应用程序请求参数的提示 例如 如果事务正在等待用户输入 而用户就去做别的事了 则用户将此事务挂起使之不能完成 这样将降低系统的吞吐量 因为事务持有的任何锁只有在事务提交或回滚时才会释放 即使不出现死锁的情况 访问同一资源的其它事务也会被阻塞 等待该事务完成

保持事务简短并在一个批处理中

在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁 事务运行时间越长 其持有排它锁或更新锁的时间也就越长 从而堵塞了其它活动并可能导致死锁 保持事务在一个批处理中 可以最小化事务的网络通信往返量 减少完成事务可能的延迟并释放锁

使用低隔离级别

确定事务是否能在更低的隔离级别上运行 执行提交读允许事务读取另一个事务已读取(未修改)的数据 而不必等待第一个事务完成 使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间 从而降低了锁定争夺

使用绑定连接

使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作 次级连接所获得的任何锁可以象由主连接获得的锁那样持有 反之亦然 因此不会相互阻塞

下面有一些对死锁发生的一些建议

)对于频繁使用的表使用集簇化的索引

)设法避免一次性影响大量记录的T SQL语句 特别是INSERT和UPDATE语句

)设法让UPDATE和DELETE语句使用索引

)使用嵌套事务时 避免提交和回退冲突

lishixinzhi/Article/program/SQLServer/201311/22240

死锁主要表现以下几种情况:

表现一:

一个用户A 访问表A(锁住了表A),然后又访问表B

另一个用户B 访问表B(锁住了表B),然后企图访问表A

这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,才能继续,好了他老人家就只好老老实实在这等了

同样用户B要等用户A释放表A才能继续这就死锁了

解决方法:

这种死锁是由于你的程序的BUG产生的,除了调整你的程序的逻辑别无他法

仔细分析你程序的逻辑,

1:尽量避免同时锁定两个资源

2: 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源.

表现二:

用户A读一条纪录,然后修改该条纪录

这是用户B修改该条纪录

这里用户A的事务里锁的性质由共享锁企图上升到独占锁(for update),而用户B里的独占锁由于A有共享锁存在所以必须等A释

放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。

这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。

解决方法:

让用户A的事务(即先读后写类型的 *** 作),在select 时就是用Update lock

语法如下:

select * from table1 with(updlock) where ....

如果真的table被锁住了,可以通过下面的方法来解锁:

Sql server企业管理器->对应的数据库->管理->当前活动->锁/进程ID

将对应的被锁住的进程关闭。

还有一种方法,就是在你不知道究竟是哪张表被锁,由何种原因被锁,可以重新启动数据库来解决,但不保证下次又被锁住,因为还没有找到问题的根本原因。

要避免锁表,在 *** 作数据库最好不要用独占方式。

1、要求每个事务一次就将所有要使用的数据全部加锁,否则不能执行。

2、采用按序加锁法.预先规定一个封锁顺序,所有的事务都必须按这个顺序对数据执行封锁。

3、不采取任何措施来预防死锁的发生,而是周期性地检查系统中是否有死锁.如果发现死锁就设法解除。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/sjk/9986545.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-04
下一篇2023-05-04

发表评论

登录后才能评论

评论列表(0条)

    保存