热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SQLServer死锁的分析、处理与预防毓明在线

1、基本原理所谓“死锁”,在操作系统的定义是:在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种

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

1、基本原理 所谓“死锁”,在操作系统的定义是:在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。 定义比较抽象,下图可以帮助你比较直观的理解死锁:

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

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


推荐阅读
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
author-avatar
黑焰2502887807
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有