记一次系列再更新,这次是死锁~
数据库死锁这个东西,一般碰到这个都会觉得头皮发麻,不知道怎么处理,
然后看了几篇文章以及sqlserver的官方文档后往往都会很蒙蔽,根本不知道在说什么
但是在最近的一次死锁中,我逐渐明白了一些,在这里分享下
死锁原因:
不仅是数据库死锁,任何多线程都会死锁,而原因其实都是一个,那就是两方或两方以上资源竞争僵持不下,就一直相互等待,没错,2方以上也可以,这里拿现实点的比喻,
有2个盲盒A和B,每个盒子只有一个口子允许拿手进去摸
这里设置个条件,伸手去摸的盒子没法看,正在看的盒子不能摸,
摸盒子都是先看(add S)再摸(release S,add IX,add U -> X),最后在放手(release X,release IX)
同时
小张摸了A盒准备去摸B,看向了B,
小王摸了B盒准备去摸A,看向了A,
如果2人都一根筋都必须摸另一个盒子,那是不是就僵持了
因为
小张控制着A,准备去看B发现B没法看因为被小王控制着,然后小张一直等待着B被释放
小王控制着B,想去看A,发现A也没法看,因为被小张控制着,然后小王也等待着A被释放
然后这2个人,就一直等了(deadlock)
死锁分析:
上面的例子其实就是数据库中出现的最标志性,最明显的死锁
小王和小张可以理解成2个请求
A和B就是2张表,这里涉及了加锁和解锁
涉及2个比较重要的锁,其他类型锁具体的可以看sqlserver的官方文档
共享锁S
这个锁就是查数据时候加的锁,加上这个锁以后,锁的资源能查,但不能修改,查询完释放S锁
排他锁X
这个锁是在数据进行insert,update,delete时候加的锁,加上这个锁以后,因为与其他锁不兼容,无法给这个资源加上别的锁,除非nolock或未提交读隔离级别
是不是可以理解成上面的条件(伸手去摸的盒子没法看,正在看的盒子不能摸)
下面说下我这次碰到的死锁
是在执行一条delete语句的时候触发的死锁,
死锁的另一条语句是一条insert语句,都是操作的同一张表,说实话一开始真的奇怪,这都能死锁。
先看了下死锁的2个锁一个是U锁,另一个是表面看是IX锁?,这个很奇怪,
然后查了下这2条语句的执行计划,发现死锁的表是有索引的,insert语句是正常的,delete语句发现delete筛选的字段有的有索引,有的没索引,导致bookmark lookup
https://blog.csdn.net/u012752975/article/details/22412915 和这篇里面的一个很像
但还是想不通为什么会死锁,并且把每条语句单独执行,通过SQL Server Profiler查了下加锁过程,似乎很难发现什么
后面干脆写了个多线程代码,再加上jmeter看看能不能复现出这个死锁
经过多次测试和踩坑,最后还是没有复现出了这个死锁,但是复现出了别的死锁,然后通过保存死锁图形 (SQL Server Profiler)
复现出的死锁是锁了索引导致的,是多条delete语句导致的,基本都是一条deleteX锁了某个索引,然后准备U锁另一个索引,然后另一条delete语句反过来就导致了死锁,有的甚至出现了3方死锁,如下图这样的
然后我又仔细看了那天的死锁日志,把里面的每个信息仔细地又看了遍,
发现这个也是多方的死锁,但是堵塞的对象里面发现了exchangeEvent,这个是啥?
经过多次搜索后
https://dba.stackexchange.com/questions/282959/what-does-exchangevent-indicate-in-sql-deadlock-graph
https://support.microsoft.com/en-us/topic/kb4089473-better-intra-query-parallelism-deadlocks-troubleshooting-in-sql-server-2017-and-2016-c0cc8ac0-ae4f-966a-b6ed-29dfcd17972a
https://blog.csdn.net/weixin_34015336/article/details/89758416
发现Intra-Query Parallel Thread Deadlocks
解决方案都在帖子的里面,我这边是后面优化了bookmark lookup的sql语句的索引最后解决了这个问题