网站首页 服务项目 客户案例 我们优势 主机域名 关于黑羽 联系黑羽

资讯中心
了解网站建设资讯引领网站建设开发方向
谁还没经历过死锁呢

本文转载自微信公众号「小林coding」,作者小林coding。转载本文请联系小林coding公众号。
大家好,我是小林。
说个很早之前自己遇到过数据库死锁问题。
有个业务主要逻辑就是新增订单、修改订单、查询订单等操作。然后因为订单是不能重复的,所以当时在新增订单的时候做了幂等性校验,做法就是在新增订单记录之前,先通过 select ... for update 语句查询订单是否存在,如果不存在才插入订单记录。
而正是因为这样的操作,当业务量很大的时候,就可能会出现死锁。
接下来跟大家聊下为什么会发生死锁,以及怎么避免死锁。
本次案例使用存储引擎 Innodb,隔离级别不可重复读(RR)。
接下来,我用实战的方式来带大家看看死锁是怎么发生的。
我建了一张订单表,其中 id 字段为主键索引,order_no 字段普通索引,也就是非唯一索引:
然后,先 t_order 表里现在已经有了 6 条记录:

假设这时有两事务,一个事务要插入订单 1007 ,另外一个事务要插入订单 1008,因为需要对订单做幂等性校验,所以两个事务先要查询该订单是否存在,不存在才插入记录,过程如下:

可以看到,两个事务都陷入了等待状态(前提没有打开死锁检测),也就是发生了死锁,因为都在相互等待对方释放锁。
这里在查询记录是否存在的时候,使用了 select ... for update 语句,目的为了防止事务执行的过程中,有其他事务插入了记录,而出现幻读的问题。
如果没有使用 select ... for update 语句,而使用了单纯的 select 语句,如果是两个订单号一样的请求同时进来,就会出现两个重复的订单,有可能出现幻读,如下图:

可重复读隔离级别下,是存在幻读的问题。
Innodb 引擎为了解决「可重复读」隔离级别下的幻读问题,就引出了 next-key 锁,它是记录锁和间隙锁的组合。
普通的 select 语句是不会对记录加锁的,因为它是通过 MVCC 的机制实现的快照读,如果要在查询时对记录加行锁,可以使用下面这两个方式:
行锁的释放时机是在事务提交(commit)后,锁就会被释放,并不是一条语句执行完就释放行锁。
比如,下面事务 A 查询语句会锁住(2, +∞]范围的记录,然后期间如果有其他事务在这个锁住的范围插入数据就会被阻塞。

next-key 锁的加锁规则其实挺复杂的,在一些场景下会退化成记录锁或间隙锁,我之前也写一篇加锁规则,详细可以看这篇「我做了一天的实验!」
需要注意的是,next-key lock 锁的是索引,而不是数据本身,所以如果 update 语句的 where 条件没有用到索引列,那么就会全表扫描,在一行行扫描的过程中,不仅给行加上了行锁,还给行两边的空隙也加上了间隙锁,相当于锁住整个表,然后直到事务结束才会释放锁。
所以在线上千万不要执行没有带索引条件的 update 语句,不然会造成业务停滞,我有个读者就因为干了这个事情,然后被老板教育了一波,详细可以看这篇「完蛋,公司被一条 update 语句干趴了!」
回到前面死锁的例子,在执行下面这条语句的时候:
因为 order_no 不是唯一索引,所以行锁的类型是间隙锁,于是间隙锁的范围是(1006, +∞)。那么,当事务 B 往间隙锁里插入 id = 1008 的记录就会被锁住。
因为当我们执行以下插入语句时,会在插入间隙上再次获取插入意向锁。
插入意向锁与间隙锁是冲突的,所以当其它事务持有该间隙的间隙锁时,需要等待其它事务释放间隙锁之后,才能获取到插入意向锁。而间隙锁与间隙锁之间是兼容的,所以所以两个事务中 select ... for update 语句并不会相互影响。
案例中的事务 A 和事务 B 在执行完后 select ... for update 语句后都持有范围为(1006,+∞)的间隙锁,而接下来的插入操作为了获取到插入意向锁,都在等待对方事务的间隙锁释放,于是就造成了循环等待,导致死锁。
死锁的四个必要条件:互斥、占有且等待、不可强占用、循环等待。只要系统发生死锁,这些条件必然成立,但是只要破坏任意一个条件就死锁就不会成立。
在数据库层面,有两种策略通过「打破循环等待条件」来解除死锁状态:
当发生超时后,就出现下面这个提示:

当检测到死锁后,就会出现下面这个提示:

上面这个两种策略是「当有死锁发生时」的避免方式。
我们可以回归业务的角度来预防死锁,对订单做幂等性校验的目的是为了保证不会出现重复的订单,那我们可以直接将 order_no 字段设置为唯一索引列,利用它的唯一下来保证订单表不会出现重复的订单,不过有一点不好的地方就是在我们插入一个已经存在的订单记录时就会抛出异常。
网页制作的服务
网站开发服务
自贡黑羽网络品牌建站
自贡本地做网站就选黑羽网络
Tel:0813-5104030 15348110304 QQ或微信:22232591
地址:四川省自贡市自流井区丹佳大街泰丰大厦写字楼19楼10号
Copyright © 2018-2058 自贡黑羽网络科技有限公司 All rights reserved. ICP备案号 : 蜀ICP备12014994号-1 技术支持:黑羽网络
自贡网站建设,网页设计制作与开发,自贡网络公司推荐品牌,关键词: 自贡网站制作 自贡做网站 自贡网络公司 自贡网页制作 自贡做网站 网站地图xml 网站地图html 网站地图txt