跳到主要内容

前言

我们经常使用数据库,而在使用数据库中,必不可少的就是锁,所以很容易出现死锁问题

死锁产生的原因

死锁是由于不同线程对于资源访问导致阻塞的问题,比如A线程使用资源a,B线程使用资源b,这个时候,线程A,B已经请求到资源了,这个时候线程A又要使用资源b,线程b又要使用资源a,这样就形成了死锁,导致线程A等待线程B释放资源b,线程B等待线程A释放资源a

image.png

image.png

image.png

死锁产生的必要条件

死锁的产生的必要条件有四个,互斥原则不剥夺原则阻塞原则循环等待原则

  • 互斥原则

对于每一个资源,一段时间内,只能有一个进程占用

  • 不剥夺原则

对于每一个进程请求获取到的资源,在此进程未使用完毕,不进行释放

  • 阻塞原则

对于进程请求的新资源,如果此资源被其他进程占用,该进程进入阻塞,但不会释放资源

  • 循环等待原则

如果产生了死锁,必然形成了资源等待闭环

数据库中出现死锁及解决方案

  1. 事务之间对资源访问的顺序交替

出现原因

事务A访问表1,并且把表1锁住,然后又想要访问表2,事务B先访问表2,并且把表2锁住,然后又想要访问表1,这个时候,事务A等待事务B释放表2,事务B等待事务A释放表1

解决方案

这种死锁的出现主要是因为程序代码写的有问题,除了调整代码的逻辑之外并没有什么好的方案,对于一个方法要使用多张表,尽量保证同时锁住这多张表。

  1. 索引不当导致全表扫描

出现原因

一条sql语句的索引失效导致进行了全表扫描,同时让行级锁上升为表级锁,多个事务执行这样的Sql之后很容易发生死锁问题

解决方案

使用mysql的explain命令,来对sql语句进行分析,尽量不让sql语句去全表扫描,并适当的加索引

  1. 并发修改同一条记录

出现原因

有两个事务,事务A先查询一条记录然后对该记录进行修改,事务B对该记录进行修改,事务A查询记录会加上共享锁,然后此时事务B想要修改记录,就要加上排他锁,但是由于事务A加了共享锁,事务B必须要等待事务A释放共享锁,但是事务A又需要修改该记录,企图将共享锁上升为排他锁,但是A由于B的排他锁,就不能去释放共享锁,这样导致了死锁

解决方案

  • 乐观锁机制

乐观锁机制大多数基于版本(Version)记录来实现,也就是给表中新增一个字段version,每次读取的时候将version一并读出,如果要修改,就对version+1,然后更新的时候,判断如果当前version大于我们加1的version,那么就证明为过期数据,直接丢弃,如果当前version小于我们加1的version,那么久证明不是过期数据,直接进行修改

  • 悲观锁机制

悲观锁机制大多数基于数据库锁来实现,例如,使用Mysql的for update保证锁的排他性,但是使用悲观锁机制,会让我们的数据库性能大幅度下降。