1、基本原理

所谓“死锁”,在操作系统的定义是:在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。

定义比较抽象,下图可以帮助你比较直观的理解死锁:

出现死锁需要满足几个必要条件:

a)互斥:进程独占资源,资源不共享;

b)请求与保持:已经得到资源的进程可以再次申请新资源;

c)不剥夺:已分配的资源不能被其它进程强制剥夺;

d)环路等待:几个进程组成环路,都在相互等待正被占用的资源;

对应到SQL Server中,在2个或多个任务中(insert、update、delete、select、alter table或Tran事务等等),如果每个任务锁定了其它任务想要锁定的资源,会造成这些任务永久阻塞,从而出现死锁。这些资源可能是:单行数据(RID、HEAP堆中的行)、索引中的键(KEY,行锁)、页(Page,8KB)、区(Extent,8个连续页)、堆或B树、表(Table,数据和索引)、文件(File,数据库文件)、整个数据库(DataBase)。

如果系统中的资源不足或者资源分配策略不当,会导致因进程间的资源争用产生死锁现象。但更多的可能是程序员的程序有问题。“锁”有多种方式,如意向锁、共享锁、排他锁等等。锁还有多种粒度,如行锁、表锁。

了解了死锁产生的原因,就可以最大可能的避免与预防死锁。只要上述4个必要条件中有1个不满足,就不会发生死锁。所以,在系统设计、实现阶段就可以在资源分配与占用、资源访问顺序等方面采取必要措施。

2、一个例子

直面死锁,来看一个例子:如图1所示,新建一个查询窗口,并利用事务的原子特性和update语句的排他锁特性把2个表中的记录锁住;如图2所示,再次新建一个查询窗口,2条很简单的SQL语句长时间仍没有执行完成。

3、检测与排查

3.1 通过Profile工具看死锁

Profile是SQL Server自带的跟踪分析工具,开启Profile来捕捉死锁信息可以更直观的看到相关信息。

3.2 通过系统存储过程看死锁

sp_who和sp_lock是SQL Server的2个系统存储过程,可以用它们来查询数据库中的锁情况。sp_who提供有关的数据库实例中当前用户、会话和进程的信息,如下图,我们看到spid=56的会话(UPDATE语句)被spid=54的会话阻塞。

sp_lock提供有关锁的信息,如下图。我们可以通过spid知道是哪个会话锁住了资源,可以通过ObjId知道被锁住的资源是什么。

执行如下SQL脚本获取被锁资源和资源所属的数据库:

SELECT OBJECT_NAME(421666738) AS LockedResource,DB_NAME(11) AS DBName;
--------------------------------------------------------------
LockedResource DBName
--------------------------------------------------------------
tb_TE_SizeInformation JYBGDB

执行如下脚本获取锁资源的会话正在执行的SQL脚本:

DBCC INPUTBUFFER(54);
---------------------------------------------------------------------------
EventInfo EventType Parameters
---------------------------------------------------------------------------
--根据事务的原子性实现个必要条件中 Language Event 0
--请求和等待 BEGIN TRAN
--update语句在数据行上加排他锁
--和其它所有锁不兼容
--实现个必要条件中的:互斥
UPDATE tb_TE_BrandInformation SET IsCompensate=0
UPDATE tb_TE_SizeInformation SET [Description]=''

4、处理方式

4.1 SQL Server自动处理

“无为而治”。当数据库产生死锁时,SQL Server通过一个叫“锁监视器”的东西捕获死锁信息,并根据一定的规则自动选择一个SQL作为锁的牺牲品,并返回如下报错信息:

服务器: 消息 1205,级别 13,状态 50,行 1
事务(进程 ID  xx)与另一个进程已被死锁在  lock 资源上,且该事务已被选作死锁牺牲品。请重新运行该事务。

如果你对数据库还不够了解,那建议你向其他有经验的人求助,在此之前不要轻易对数据库进行修改。

4.2 Kill会话

通过3.2中提到的系统存储过程可以获取到与死锁相关的信息。可以查询其中是哪个spid导致的死锁,并使用Kill spid的方法把它干掉。但是这只能是一种临时的解决方案,我们不可能一遇到死锁就在用户的生产环境里排查死锁、Kill sp。同样的道理,也不可能一遇到死锁就重启SQL Server服务,甚至重启数据库服务器。

SQL脚本:

Kill  54;        --此处54即分析后得到的spid值

4.3 设定锁请求超时

默认情况下,数据库没有锁定超时期限。也就是说一个会话在申请新的资源时,如果这个资源已经被其它进程锁定,那么本会话会一直处于等待状态。这样无疑是有问题的。我们可以通过SQL命令来设定锁请求超时。也可以访问全局变量 @@LOCK_TIMEOUT 来查看这个值。

SET LOCK_TIMEOUT  20000;     --单位是毫秒

当请求锁超过设定时间时,SQL Server将返回错误。我们的程序可以根据返回的错误来进行响应的处理,避免长时间的用户等待。

服务器: 消息 1222,级别 16,状态 50,行 1

已超过了锁请求超时时段。

当然,使用这种方式来处理所有的锁请求是不合适的,也是不负责任的。在多数情况下是我们的程序的设计、实现的问题导致了死锁。在处理过程中,我们既要治标,更要治本。

4.4 修改程序

在3.2的最后,我们通过系统存储过程和几个命令找到了锁定资源的SQL命令。以这次LL项目为例,我们发现是WEB管理系统上的一个统计报表(SELECT)在执行过程中长时间的那一个生产信息表锁定,导致现场各机台上位机系统想要插入新的生产记录(INSERT)时长时间等待。所以在现场项目组每次重新启动SQL Server服务或者重启数据库服务器2个小时以后,这个问题依然重复出现。

这个时候如果采用Kill掉这个统计报表请求的方式处理,结果和重启SQL Server服务、重启数据库服务器没有区别,2个小时后问题依旧。如果采用设定锁请求超时的方式处理,那么这个统计报表每次执行都不会获得想要的结果,而且每次执行也会锁定一定的时间导致现场上位机的等待。

这次我们的处理措施是:1)暂时禁用了WEB管理系统上的这个报表功能;2)重启了SQL Server服务;3)优化报表的SQL语句;4)启用报表功能。之后的一段时间没有再次出现这样的问题。

通过对这个报表的性能优化,这个问题算是解决的差不多了。但是经过事后了解,发现报表的性能问题并不在于开发人员的疏忽或水平不够。问题的根本在这个生产信息表的设计有问题。在一个数据量达到1000w级的表中,我们采用char(10)来保存日期值,虽然INSERT、UPDATE、DELETE时没有问题,但是在执行SELECT且这个日期值字段作为过滤条件时发生性能问题是必然的。经过测试,这个字段的数据类型改为datetime时的执行时间不到性能优化后的10%。

所以,不但是在开发阶段,早在设计阶段就已经有了性能隐患。

4.5 升级硬件

不赘述。

5、如何预防

首先要理解,在多并发的环境中死锁是不可避免的,只能通过合理的数据库设计、良好的索引、适当的查询语句以及隔离等级等措施尽量减少死锁。

最开始列出了死锁的4个必要条件,只要想办法破坏任意1个或多个条件就可以避免产生死锁。下列方法有助于最大限度的降低死锁:

a) 按同一顺序访问对象;

b)避免事务中的用户交互,也就是在事务执行过程中不要包含用户交互的步骤;

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

d)SELECT语句加WITH(NOLOCK)提示;

SELECT * FROM TABLE1 WITH(NOLOCK);

SELECT * FROM TABLE2 WITH(NOLOCK);

这种写法在执行中不对查询到的资源加锁,就允许2条SQL可以并发地访问同一资源。但是加WITH(NOLOCK)提示可能会导致脏读!!!

e)使用较低的隔离级别;

暂不需要了解,不赘述。

f)使用绑定连接;

处理程序端的死锁,非数据库端,不赘述。

6、结束语

项目实施过程中遇到死锁现象在所难免。通过前面的介绍,希望大家能够对它有一个比较简单的认识,在遇到异常情况的时候不至于束手无策。如果以上内容有什么技术上不对的问题或观点,欢迎大家直接向我提出来一起研究沟通,也欢迎大家在遇到其它数据库方面的问题时能和我一起探讨,共同提高。

SQL Server死锁的分析、处理与预防的更多相关文章

  1. Update导致SQL Server死锁的典型方法(转载)

    此文为转载文章,描述的很好,没有验证过. 最近遇到了一个看上去很奇怪,分析起来很有意思的死锁问题.这个死锁看上去难以理解.而分析过程中,又使用了很多分析SQL Server死锁的典型方法.记录下来整个 ...

  2. SQL Server死锁

    SQL Server死锁 多个事务之间互相等待对方的资源,导致这些事务永久等待 注意是永久等待,而非长事务 死锁的4个条件 互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程 ...

  3. SQL Server 在线进程分析处理

    SQL Server 在线进程分析处理 前言 数据库在线进程处理在很多时候需要人为干预已达到预期管理目标,下面整理一下常用的在线进程管理方法,便于后续工作使用. 一.查看目标数据库在线进程,并杀死指定 ...

  4. SQL Server死锁的解除方法

    如果想要查出SQL Server死锁的原因,下面就教您SQL Server死锁监控的语句写法,如果您对此方面感兴趣的话,不妨一看. 下面的SQL语句运行之后,便可以查找出SQLServer死锁和阻塞的 ...

  5. SQL Server 死锁概念和分析

    锁的概念 锁是什么 锁是数据库中在并发操作情形下保护资源的机制.通常(具体要看锁兼容性)只有锁的拥有者才能对被锁的资源进行操作,从而保证数据一致性. 锁的概念可分为几部分 锁资源(锁住什么) 锁模式( ...

  6. SQL Server 死锁的告警监控

    今天这篇文章总结一下如何监控SQL Server的死锁,其实以前写过MS SQL 监控错误日志的告警信息,这篇文章着重介绍如何监控数据库的死锁,当然这篇文章不分析死锁产生的原因.以及如何解决死锁.死锁 ...

  7. SQL Server死锁中的会话隔离级别为序列化(Serializable)实验测试

    最近在分析SQL Server的死锁时,发现一个比较有意思的现象,发现死锁当中一个会话的隔离级别为序列化(Serializable),这个是让人比较奇怪的地方,我们知道SQL Server数据库的默认 ...

  8. SQL Server死锁诊断--同一行数据在不同索引操作下引起的死锁

    死锁概述 对于数据库中出现的死锁,通俗地解释就是:不同Session(会话)持有一部分资源,并且同时相互排他性地申请对方持有的资源,然后双方都得不到自己想要的资源,从而造成的一种僵持的现象.当然,在任 ...

  9. SQL Server死锁产生原因及解决办法 .

    其实所有的死锁最深层的原因就是一个:资源竞争 表现一: 一个用户A 访问表A(锁住了表A),然后又访问表B,另一个用户B 访问表B(锁住了表B),然后企图访问表A,这时用户A由于用户B已经锁住表B,它 ...

随机推荐

  1. 二分查找实现(Jon Bentley:90%程序员无法正确实现)

    二分查找实现(Jon Bentley:90%程序员无法正确实现)作者:July出处:结构之法算法之道引言Jon Bentley:90%以上的程序员无法正确无误的写出二分查找代码.也许很多人都早已听说过 ...

  2. 用vim处理字符的大小写转换

    转载: http://blog.csdn.net/ruixj/article/details/3765385 http://www.linuxsong.org/2010/09/vim-convert- ...

  3. 类型推导:函数模板与auto

    1.从函数模板谈起 函数模板的类型推导机制是在c++98时代就有的,auto的类型推导机制与其基本一致,所以先理解函数模板类型推导. 函数模板可以用如下代码框架表示: #template<typ ...

  4. js技巧总结

    很早以前看到的代码,同时加上一些我在项目中用到的代码,感觉很实用,在这里记录下来,怕忘记了,有些代码忘记在哪看到的了,所以就不贴网址了,感谢各位大神的分享!如果有其他的好的方法,欢迎留言~ 1.取整的 ...

  5. C#中常用的排序算法的时间复杂度和空间复杂度

    常用的排序算法的时间复杂度和空间复杂度   常用的排序算法的时间复杂度和空间复杂度 排序法 最差时间分析 平均时间复杂度 稳定度 空间复杂度 冒泡排序 O(n2) O(n2) 稳定 O(1) 快速排序 ...

  6. 用Eclipse插件Bytecode Outline来查看Java字节码

    在遇到一些小问题的时候我们经常会使用Javap反编译取得字节码来分析,虽然Javap能完成这个工作,但是有两个缺点,一方面操作麻烦,需要很多步骤,一方面没有文档注释,对新手来说看起字节码来比较麻烦. ...

  7. Qt绘图控件qwt绘制等比例坐标图

    需要用到QwtPlotRescaler类,用法如下: QwtPlotRescaler *plotRescaler = new QwtPlotRescaler(canvas, yLeft, QwtPlo ...

  8. 自定义基本java类-StdDraw.java

    /************************************************************************* * Compilation: javac StdD ...

  9. Python练习册--PIL处理图片之加水印

    背景 最近在看到了Python 练习册,每天一个小程序 这个项目,非常有趣,也比较实用. 晚上看了这第000题,关于Python图片处理: 将你的 QQ 头像(或者微博头像)右上角加上红色的数字,类似 ...

  10. zoj 3057 博弈

    思路:对于TT来说,如果数量分别为a a b或 a b a,或 b a a的形式,那么TT必赢,因为TT可以使其成为 a a a的形式,那么不论DD 怎么拿,都是TT最后使其成为a a a 的形式,0 ...