SQL Server死锁的分析、处理与预防

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);

--------------------------------------------------------------

EventType     Parameters EventInfo

--------------------------------------------------------------

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、 结束语

项目实施过程中遇到死锁现象在所难免。通过前面的介绍,希望大家能够对它

有一个比较简单的认识,在遇到异常情况的时候不至于束手无策。如果以上内容有什么技术上不对的问题或观点,欢迎大家直接向我提出来一起研究沟通,也欢迎大家在遇到其它数据库方面的问题时能和我一起探讨,共同提高。

时间: 2024-10-06 18:06:29

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

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

此文为转载文章,描述的很好,没有验证过. 最近遇到了一个看上去很奇怪,分析起来很有意思的死锁问题.这个死锁看上去难以理解.而分析过程中,又使用了很多分析SQL Server死锁的典型方法.记录下来整个分析过程还是很有意义的. 问题重现步骤: 经过提炼,问题重现的步骤非常简单,在SQL 2008上可以很容易地重现. 1.         首先,创建一张表格,上面有一个clustered index,两个non-clustered index. create table tt(id int iden

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

SQL Server死锁产生原因及解决办法 2006-07-18 05:12:10 分类: SQL Server 其实所有的死锁最深层的原因就是一个:资源竞争 表现一: 一个用户A 访问表A(锁住了表A),然后又访问表B,另一个用户B 访问表B(锁住了表B),然后企图访问表A,这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B,才能继续,好了他老人家就只好老老实实在这等了,同样用户B要等用户A释放表A才能继续这就死锁了. 解决方法: 这种死锁是由于你的程序的BUG产生的,除了调整你的程序

SQL Server 死锁的告警监控

原文:SQL Server 死锁的告警监控 今天这篇文章总结一下如何监控SQL Server的死锁,其实以前写过MS SQL 监控错误日志的告警信息,这篇文章着重介绍如何监控数据库的死锁,当然这篇文章不分析死锁产生的原因.以及如何解决死锁.死锁(Dead Lock)的错误信息在sys.messages中的message_id为1205,可以使用下面SQL查看. SELECT * FROM sys.messages WHERE message_id=1205 那么接下来,我们来设置一下死锁(Dea

SQL Server事务日志分析

SQL Server事务日志分析 fn_dblog()和fn_dump_dblog()函数介绍 SQL Server有两个未公开的函数fn_dblog()和fn_dump_dblog()非常有用并且提供的信息量很大.你可以使用这些函数来获取100多列大量的有用信息. fn_dblog()用于分析数据库当前的事务日志文件,它需要两个参数,分别为事务开始LSN和结束LSN,默认为NULL,表示返回事务日志文件的所有日志记录. 例如: SELECT * FROM fn_dblog(null,null)

SQL Server死锁排查

1. 死锁原理 根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态. 死锁的四个必要条件:互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用.请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源.非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺.循环等待条件(Circular wait):系统中若干进程组成

SQL Server 死锁 (Page锁)诊断

在数据库中打开死锁监测可以收集到数据库发生的死锁情况.打开的方式有2种: 1 打开1222监控 执行SQL语句: Dbcc traceon(1222,-1); 然后在系统日志里查看死锁的信息. 2 启动SQL Profiler(建议使用): 下面就是一个发生死锁的实例图: 下面提供对这个死锁分析思路,如有不当之处,还望大家批评指正. 一共3个问题,下面逐个回答. 第一个问题:被锁定的资源是什么? 上面写的很清楚,是一个Page 锁, 那么Page 锁是什么呢? 通常死锁是你操作A表,然后又要操作

SQL Server死锁总结

1. 死锁原理 根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态. 死锁的四个必要条件:互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用.请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源.非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺.循环等待条件(Circular wait):系统中若干进程组成

Sql server 死锁排查

记得以前客户在使用软件时,有偶发出现死锁问题,因为发生的时间不确定,不好做问题的重现,当时解决问题有点棘手了. 现总结下查看死锁的常用二种方式: 第一种是图形化监听: sqlserver -->工具--> sql server profiler   登录后在跟踪属性中选择如下图: 监听到的死锁图形如下图 这里的描述大致是:有二个进程 一个进程ID是96, 另一个ID是348.   系统自动kill 掉了进程ID:96,保留了进程ID:348 的事务Commit 原文地址:https://www

sql server 死锁自动释放

在高并发时数据库发生会死锁,发生死锁后,数据库会自动释放 原文: When a transaction is chosen as a deadlock victim, SQL Server will rollback the victim's transaction which releases locks held by the transaction. This will allow other transactions to proceed. Unlike blocking, deadlo