热门标签 | 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 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 本文介绍了如何利用Shell脚本高效地部署MHA(MySQL High Availability)高可用集群。通过详细的脚本编写和配置示例,展示了自动化部署过程中的关键步骤和注意事项。该方法不仅简化了集群的部署流程,还提高了系统的稳定性和可用性。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 第二十五天接口、多态
    1.java是面向对象的语言。设计模式:接口接口类是从java里衍生出来的,不是python原生支持的主要用于继承里多继承抽象类是python原生支持的主要用于继承里的单继承但是接 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • 本指南详细介绍了如何利用华为云对象存储服务构建视频点播(VoD)平台。通过结合开源技术如Ceph、WordPress、PHP和Nginx,用户可以高效地实现数据存储、内容管理和网站搭建。主要内容涵盖华为云对象存储系统的配置步骤、性能优化及安全设置,为开发者提供全面的技术支持。 ... [详细]
  • PTArchiver工作原理详解与应用分析
    PTArchiver工作原理及其应用分析本文详细解析了PTArchiver的工作机制,探讨了其在数据归档和管理中的应用。PTArchiver通过高效的压缩算法和灵活的存储策略,实现了对大规模数据的高效管理和长期保存。文章还介绍了其在企业级数据备份、历史数据迁移等场景中的实际应用案例,为用户提供了实用的操作建议和技术支持。 ... [详细]
  • 在Linux系统中避免安装MySQL的简易指南
    在Linux系统中避免安装MySQL的简易指南 ... [详细]
  • DVWA学习笔记系列:深入理解CSRF攻击机制
    DVWA学习笔记系列:深入理解CSRF攻击机制 ... [详细]
  • Delphi XE Rtti单元深入解析:TRttiContext的应用与实践
    Delphi XE Rtti单元深入解析:TRttiContext的应用与实践 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 在处理数据库中所有用户表的彻底清除时,目前尚未发现单一命令能够实现这一目标。因此,需要采用一种较为繁琐的方法来逐个删除相关表及其结构。具体操作可以通过编写PL/SQL脚本来实现,该脚本将动态生成并执行删除表的SQL语句。尽管这种方法相对复杂,但在缺乏更简便手段的情况下,仍是一种有效的解决方案。未来或许可以通过数据库管理工具或更高版本的数据库系统提供更简洁的处理方式。 ... [详细]
  • 在使用达梦数据库时,管理员可能会遇到连接频繁中断或特定SQL语句语法错误的问题。这些问题通常源于开发人员在创建对象时的不规范操作。为了解决这些问题,建议对数据库配置进行优化,并确保所有SQL语句符合达梦数据库的标准语法。此外,定期检查和维护数据库连接参数,以及对异常日志进行详细分析,也有助于及时发现并解决问题。 ... [详细]
  • 本文深入解析了WCF Binding模型中的绑定元素,详细介绍了信道、信道管理器、信道监听器和信道工厂的概念与作用。从对象创建的角度来看,信道管理器负责信道的生成。具体而言,客户端的信道通过信道工厂进行实例化,而服务端则通过信道监听器来接收请求。文章还探讨了这些组件之间的交互机制及其在WCF通信中的重要性。 ... [详细]
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社区 版权所有