谈一谈数据库中的死锁问题

本篇文章是基于《MySQL45讲》来写的个人理解与感悟。

死锁是什么?

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

比如:两个线程A、B各自持有一个无法共享的资源,并且他们都需要获取对方现在持有的资源才能进行下一步,但是他们又必须等对方释放了才能去获取,于是A等待B,B也在等待A。如此这般,死锁就产生了。

而对于MySQL,如果只支持到表锁的引擎,同一张表上任何时刻只能有一个更新在执行,这就会影响到业务并发度。也就意味着如果我们对某一个表进行修改的时候这张表就会被锁住,从而影响效率。因此有了行锁。
而行锁是遵循两阶段提交协议的,这也就意味着虽然我们可以主动开启行锁,但是只有事务提交的时候行锁才会被释放。那么这就容易产生了死锁。

死锁的四个必要条件

互斥条件:指多个线程不能同时使用同一个资源。
在这里插入图片描述

请求与保持条件: 当线程 A 已经持有了资源 1,又想申请资源 2,而资源 2 已经被线程 C 持有了,所以线程 A 就会处于等待状态,但是线程 A 在等待资源 2 的同时并不会释放自己已经持有的资源 1。
在这里插入图片描述

不可剥夺条件:当线程已经持有了资源 ,在自己使用完之前不能被其他线程获取,线程 B 如果也想使用此资源,则只能在线程 A 使用完并释放后才能获取。
在这里插入图片描述

换路等待条件:在死锁发生的时候,两个线程获取资源的顺序构成了环形链。
在这里插入图片描述

图片来源:小林coding

那么我们接下来看一下死锁的例子:

比方说,此时有两个用户去电影院购票,然后购票其实是有三个步骤。

  1. 从顾客A、B账户余额中扣除电影票价;

  2. 给影院的账户余额增加这张电影票价;

  3. 记录一条交易日志。

因为要保证数据库完整性,所以会用到事务。假设此时我们两个用户都去购票,然后因为增加电影院的账户余额的这个动作两个用户要需要进行,这个时候会需要行锁去操作增加,然后又因为行锁其实是必须要在事务提交之后才会释放。

所以一旦某个操作阻塞住了,那么就会产生死锁。

而且有些电影院可以有预售秒杀的那种活动嘛,这样的话,一旦阻塞住,那么cpu会飙升,最后导致数据库挂了。

避免死锁的策略

  1. 超时等待:也就是等待一定时间,如果没有获取到锁那么线程就自动退出,但是等待的时间很不好把握,所以一般用第二种方法。

  2. 死锁检测:也就是说,发起死锁检测,然后查看是否发生死锁【是否出现循环等待】,一旦发生就会回滚这个死锁链条的某个事务,这样的话就让其他事务可以继续执行。

但是死锁检测也不是万能的,比如还是出现了上面一样的情况【对同一行记录进行更新】,假设同时有一千条并发线程同时去操作,那么死锁检测是O(N)的,所以就回去检查完1000个线程之后才检测完,而在这个期间会占用大量的CPU资源的,所以就会看到虽然CPU占用资源很高,但是处理的效率缺差强人意。

那么如何解决这种热点行更新操作导致的问题呢?
  1. 抓住矛盾的主要方面,也就是说如果你可以确定这个业务不会发生死锁问题,就可以把死锁检测关掉。

但是这种操作本身带有一定的风险,因为业务设计的时候一般不会把死锁当做一个严重错误,毕竟出现死锁了,就回滚,然后通过业务重试一般就没问题了,这是业务无损的。而关掉死锁检测意味着可能会出现大量的超时,这是业务有损的。

  2. 控制并发度
在这里插入图片描述

  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值