原始出处 http://oecpby.blog.51cto.com/2203338/457054

最近在项目中使用SqlServer的时候发现在高并发情况下,频繁更新和频繁查询引发死锁。通常我们知道如果两个事务同时对一个表进行插入或修改数据,会发生在请求对表的X锁时,已经被对方持有了。由于得不到锁,后面的Commit无法执行,这样双方开始死锁。但是select语句和update语句同时执行,怎么会发生死锁呢?看完下面的分析,你会明白的……

首先举个例子:
  CREATE PROC p1 @p1 int AS
      SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
   GO

CREATE PROC p2 @p1 int AS
         UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1
         UPDATE t1 SET c2 = c2-1 WHERE c1 = @p1
   GO

p1没有insert,没有delete,没有update,只是一个select,p2才是update。
 那么,什么导致了死锁?

需要从事件日志中,看sql的死锁信息:
   Spid X is running this query (line 2 of proc [p1], inputbuffer “… EXEC p1 4 …”):
   SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
   Spid Y is running this query (line 2 of proc [p2], inputbuffer “EXEC p2 4”):
   UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1
              
   The SELECT is waiting for a Shared KEY lock on index t1.cidx. The UPDATE holds a conflicting X lock.
   The UPDATE is waiting for an eXclusive KEY lock on index t1.idx1. The SELECT holds a conflicting S lock.

首先,我们看看p1的执行计划。怎么看呢?可以执行set statistics profile on,这句就可以了。下面是p1的执行计划
   SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
        |--Nested Loops(Inner Join, OUTER REFERENCES:([Uniq1002], [t1].[c1]))
               |--Index Seek(OBJECT:([t1].[idx1]), SEEK:([t1].[c2] >= [@p1] AND [t1].[c2] <= [@p1]+(1)) ORDERED FORWARD)
                     |--Clustered Index Seek(OBJECT:([t1].[cidx]), SEEK:([t1].[c1]=[t1].[c1] AND [Uniq1002]=[Uniq1002]) LOOKUP ORDERED FORWARD)

我们看到了一个nested loops,第一行,利用索引t1.c2来进行seek,seek出来的那个rowid,在第二行中,用来通过聚集索引来查找整行的数据。这是什么?就是bookmark lookup啊!为什么?因为我们需要的c2、c3不能完全的被索引t1.c1带出来,所以需要书签查找。

好,我们接着看p2的执行计划。
   UPDATE t1 SET c2 = c2+1 WHERE c1 = @p1
         |--Clustered Index Update(OBJECT:([t1].[cidx]), OBJECT:([t1].[idx1]), SET:([t1].[c2] = [Expr1004]))
               |--Compute Scalar(DEFINE:([Expr1013]=[Expr1013]))
                     |--Compute Scalar(DEFINE:([Expr1004]=[t1].[c2]+(1), [Expr1013]=CASE WHEN CASE WHEN ...
                           |--Top(ROWCOUNT est 0)
                                 |--Clustered Index Seek(OBJECT:([t1].[cidx]), SEEK:([t1].[c1]=[@p1]) ORDERED FORWARD)

通过聚集索引的seek找到了一行,然后开始更新。这里注意的是,update的时候,它会申请一个针对clustered index的X锁的。

实际上到这里,我们就明白了为什么update会对select产生死锁。update的时候,会申请一个针对clustered index的X锁,这样就阻塞住了(注意,不是死锁!)select里面最后的那个clustered index seek。死锁的另一半在哪里呢?注意我们的select语句,c2存在于索引idx1中,c1是一个聚集索引cidx。问题就在这里!我们在p2中更新了c2这个值,所以sqlserver会自动更新包含c2列的非聚集索引:idx1。而idx1在哪里?就在我们刚才的select语句中。而对这个索引列的更改,意味着索引集合的某个行或者某些行,需要重新排列,而重新排列,需要一个X锁。

SO………,问题就这样被发现了。

总结一下,就是说,某个query使用非聚集索引来select数据,那么它会在非聚集索引上持有一个S锁。当有一些select的列不在该索引上,它需要根据rowid找到对应的聚集索引的那行,然后找到其他数据。而此时,第二个的查询中,update正在聚集索引上忙乎:定位、加锁、修改等。但因为正在修改的某个列,是另外一个非聚集索引的某个列,所以此时,它需要同时更改那个非聚集索引的信息,这就需要在那个非聚集索引上,加第二个X锁。select开始等待update的X锁,update开始等待select的S锁,死锁,就这样发生鸟。

那么,为什么我们增加了一个非聚集索引,死锁就消失鸟?我们看一下,按照上文中自动增加的索引之后的执行计划:
   SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+1
      |--Index Seek(OBJECT:([deadlocktest].[dbo].[t1].[_dta_index_t1_7_2073058421__K2_K1_3]), SEEK:([deadlocktest].[dbo].[t1].[c2] >= [@p1] AND [deadlocktest].[dbo].[t1].[c2] <= [@p1]+(1)) ORDERED FORWARD)

哦,对于clustered index的需求没有了,因为增加的覆盖索引已经足够把所有的信息都select出来。就这么简单。

实际上,在sqlserver 2005中,如果用profiler来抓eventid:1222,那么会出现一个死锁的图,很直观的说。

下面的方法,有助于将死锁减至最少(详细情况,请看SQLServer联机帮助,搜索:将死锁减至最少即可。

·         按同一顺序访问对象。

·         避免事务中的用户交互。

·         保持事务简短并处于一个批处理中。

·         使用较低的隔离级别。

·         使用基于行版本控制的隔离级别。

-    将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON,使得已提交读事务使用行版本控制。

-    使用快照隔离。

·         使用绑定连接。

本文出自oecp博客,请务必保留此出处http://oecpby.blog.51cto.com/2203338/457054

sql server中同时执行select和update语句死锁问题的更多相关文章

  1. sql server中高并发情况下 同时执行select和update语句死锁问题 (二)

    SQL Server死锁使我们经常遇到的问题,数据库操作的死锁是不可避免的,本文并不打算讨论死锁如何产生,重点在于解决死锁.希望对您学习SQL Server死锁方面能有所帮助. 死锁对于DBA或是数据 ...

  2. c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程

    c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...

  3. 谈一谈SQL Server中的执行计划缓存(上)

    简介 我们平时所写的SQL语句本质只是获取数据的逻辑,而不是获取数据的物理路径.当我们写的SQL语句传到SQL Server的时候,查询分析器会将语句依次进行解析(Parse).绑定(Bind).查询 ...

  4. 浅析SQL Server中的执行计划缓存(上)

    简介 我们平时所写的SQL语句本质只是获取数据的逻辑,而不是获取数据的物理路径.当我们写的SQL语句传到SQL Server的时候,查询分析器会将语句依次进行解析(Parse).绑定(Bind).查询 ...

  5. SQL Server中的执行引擎入门

      简介 当查询优化器(Query Optimizer)将T-SQL语句解析后并从执行计划中选择最低消耗的执行计划后,具体的执行就会交由执行引擎(Execution Engine)来进行执行.本文旨在 ...

  6. SQL Server中 SET 和 SELECT 赋值有什么区别?

    SQL Server 中对已经定义的变量赋值的方式用两种,分别是 SET 和 SELECT.对于这两种方式的区别,SQL Server 联机丛书中已经有详细的说明,但很多时候我们并没有注意,其实这两种 ...

  7. 谈一谈SQL Server中的执行计划缓存(下)

    简介 在上篇文章中我们谈到了查询优化器和执行计划缓存的关系,以及其二者之间的冲突.本篇文章中,我们会主要阐述执行计划缓存常见的问题以及一些解决办法. 将执行缓存考虑在内时的流程 上篇文章中提到了查询优 ...

  8. sql server中Set与select的区别

    Set与select的区别 Set select 同时多个变量赋值 不支持 支持 表达式返回多个值时 出错 将返回的最后一个值赋给变量 表达式未返回值 变量被null赋值 变量保持原始值

  9. SQL SERVER中如何在声明游标的语句中,用变量做表名

    -- 因为定义游标所用的表名是变量,所以采用EXEC(定义语句) 的方式来声明游标set @StrSql='DECLARE Ba_Cursor CURSOR FOR (SELECT a.PhoneId ...

随机推荐

  1. 详解exif.js,应用于canvas照片倒转(海报H5)

    业务背景,苹果手机调用上传接口拍照没有问题但是上传到网页上照片倒转了解决方法利用exif.js读取图片参数并对图片进行元数据修改 window.btoa(str)转码 window.atob(base ...

  2. 动态时间规整DTW(Dynamic Time Warping )

    动态时间规整DTW(Dynamic Time Warping ) 原文:https://blog.csdn.net/raym0ndkwan/article/details/45614813 算法笔记- ...

  3. voip通话分析(含语音质量)

    SipAnalysis.exe使用python开发,通过抓取网卡通信包进行质量分析:1) 分析VOIP通话的发起方.挂机方及对应时间点2) 分析通话使用的媒体信息(方向.载荷.切换时间)3) 分析通话 ...

  4. bug缺陷级别定义

    缺陷定义: 出现以下缺陷测试定义为致命 bug l  系统无响应处于死机状态. l  点击某个菜单后出现“The page cannot be displayed”或者返回异常错误. l  进行某 ...

  5. Android 开发 命名规范(基础回顾)

    标识符命名法标识符命名法最要有四种: 1 驼峰(Camel)命名法:又称小驼峰命名法,除首单词外,其余所有单词的第一个字母大写. 2 帕斯卡(pascal)命名法:又称大驼峰命名法,所有单词的第一个字 ...

  6. Light Table 编辑器修改字体 更新

    view->command->use.behaviors 加上这一句  (:lt.objs.style/font-settings "Inconsolata" 14 1 ...

  7. git笔记四

    git log --graph --oneline 中的--oneline相当于 --pretty=oneline但是可以显示更短小的id git reset --hard HEAD^ git res ...

  8. unity 解决与永久解决行尾不一致报警

    虽然不影响使用,但一堆警告信息着实让人不爽,继续往下看. 用Notepad2的“查看->显示换行编码"查看发现通过Unity3D编辑器创建的脚本文件是以"LF"结尾 ...

  9. android 9 patch

  10. https Java SSLException protocol_version的问题解决方法

    我们的业务代码里有需要用爬虫爬取商品的图片地址,然后在转到我们的服务器里的过程,中间当然少不了下载图片的过程,最近目标网站有些改变,就是之前http前缀的图片地址部分改成了https,然后就造成了一个 ...