记录SQL Server中一次无法重现的死锁(2)

理论上,这句sql的执行,会走col2 上的索引进行查找,然后再进行删除(delete本来就是先查找再删除的过程),测试case也是预期地,走了col2 上的索引

记录SQL Server中一次无法重现的死锁

查看锁的申请与释放过程

可以发现

1,删除多条数据的时候,是一条一条加锁然后删除的

2,对于第一条记录(32a1976b7833),也即col2 = 'X000000000089'的记录,删除的加锁过程如下

  2.1 对(32a1976b7833),即col2 = 'X000000000089'的记录记录所在的page加共享排它锁,对(32a1976b7833)记录所在的行加U锁

  2.2  对(32a1976b7833)记录对应的主键所在的page加IX锁,主键行加RID级别的U锁

  2.3 对2.2 对(32a1976b7833)记录对应的RID所在的page加IX锁,主键行加RID级别的X锁

  2.4 对2.2 对(32a1976b7833)记录对应的主键所在的page加IX锁,主键行加RID级别的U锁

   2.5 对2.2 对(32a1976b7833)记录对应的主键所在的page加IX锁,主键行加KEY级别的X锁

  2.6 释放KEY与Page级别的X锁和IX锁

  2.7 重复2.1对(32a1976b7833)记录所在的page加共享排它锁,对(32a1976b7833)记录所在的行加U锁

  2.8 释放(32a1976b7833)以及其所在page的X锁和IX锁

  2.9 对(d12bea8cbd9f)这个记录,也即Col3字段上的索引依次加page上的IX锁,key上的X锁

  2.10(反向)依次释放Col3 key上的X锁,page上的IX锁

  2.11 依次释放上述其他的锁

简而言之,遵循两段锁协议(2PL),以行为基础,加锁与释放所过程独立,互不干扰。
因为走了Col2上的索引,这个过程大概是:先申请Col2上的U锁,找到其RID和主键索引,然后依次删除这RID和主键索引,然后再删除Col2上索引的key,最后删除对应的Col3上的索引key
最后释放所有上面申请的锁

记录SQL Server中一次无法重现的死锁

上述是删除多条数据其中一条数据的加锁以及释放锁的过程,很清楚的看到,Col2上的U锁只是在第一步申请的,Col3上根本没有申请U锁,而是直接申请的X锁,然后删除,然后再释放
因为死锁双方的数据是互不交叉的,U锁又是单独只在Col2索引上申请的,那么为什么会出现死锁双方相互等待Col2与Col3上的U锁,从而造成死锁?
之前没有想明白,是因为就存在一种想当然的推断过程,两个session的删除语句都走col2上的索引,当然不会出现两个session相互申请Col2与Col3上的U锁
一旦存在Session1走Col2上的索引,Session2走Col3上的索引,才有可能出现ession相互申请Col2与Col3上的U锁的可能性

对于Session1和Session2

1,session1 执行delete from TableA where col2 in (x,y,z);
2,session2 执行delete from TableA where col2 in (l,m,n);

理论上说,或者相当然地说,都会走col2上的索引,但是不能完全肯定一定都会走Col2上的索引,或许有可能走全表扫描,或者有可能走Col3上的索引扫描
比如如下的强制索引提示,走任何一种执行计划,都是可能的,尽管可能会在主观上认为某些执行计划是不好的,但是这个语句在没有任何索引提示的时候,不能臆测一定会走col2上的索引
否则不会出现session双方持有了Col2索引上的U索引,申请Col3索引上的U锁,否则这个死锁就解释不通。

记录SQL Server中一次无法重现的死锁

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/b06bb481d4c0a631c9549e1cd12c49b6.html