数据库

位置:IT落伍者 >> 数据库 >> 浏览文章

如何减少SQLServer死锁发生


发布日期:2022年01月07日
 
如何减少SQLServer死锁发生

死锁是指在某组资源中两个或两个以上的线程在执行过程中在争夺某一资源时而造成互相等待的现象若无外力的作用下它们都将无法推进下去死时就可能会产生死锁这些永远在互相等待的进程称为死锁线程简单的说进程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 smallintbl smallint)IF @@ERROR<> RETURN

@@ERRORinsert into

#tmp_lock_who(spidbl) select

blockedfrom (select * from sysprocesses where

blocked> )

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

b where ablocked=spid)union select spidblocked 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 <= @intCountPropertiesbegin 取第一条记录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的锁

避免事务中的用户交互

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

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

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

使用低隔离级别

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

使用绑定连接

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

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

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

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

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

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

)对一些数据不需要及时读取更新值的表在写SQL的时候在表后台加上(nolock)Select * from tableA(nolock)

上一篇:使用动态SQL克隆数据库对象

下一篇:SQL入门:从多个表中选择数据