前言
我们经常使用数据库,而在使用数据库中,必不可少的就是锁,所以很容易出现死锁问题
死锁产生的原因
死锁是由于不同线程对于资源访问导致阻塞的问题,比如A线程使用资源a,B线程使用资源b,这个时候,线程A,B已经请求到资源了,这个时候线程A又要使用资源b,线程b又要使用资源a,这样就形成了死锁,导致线程A等待线程B释放资源b,线程B等待线程A释放资源a
死锁产生的必要条件
死锁的产生的必要条件有四个,互斥原则
,不剥夺原则
,阻塞原则
,循环等待原则
- 互斥原则
对于每一个资源,一段时间内,只能有一个进程占用
- 不剥夺原则
对于每一个进程请求获取到的资源,在此进程未使用完毕,不进行释放
- 阻塞原则
对于进程请求的新资源,如果此资源被其他进程占用,该进程进入阻塞,但不会释放资源
- 循环等待原则
如果产生了死锁,必然形成了资源等待闭环
数据库中出现死锁及解决方案
- 事务之间对资源访问的顺序交替
出现原因
事务A访问表1,并且把表1锁住,然后又想要访问表2,事务B先访问表2,并且把表2锁住,然后又想要访问表1,这个时候,事务A等待事务B释放表2,事务B等待事务A释放表1
解决方案
这种死锁的出现主要是因为程序代码写的有问题,除了调整代码的逻辑之外并没有什么好的方案,对于一个方法要使用多张表,尽量保证同时锁住这多张表。
- 索引不当导致全表扫描
出现原因
一条sql语句的索引失效导致进行了全表扫描,同时让行级锁上升为表级锁,多个事务执行这样的Sql之后很容易发生死锁问题
解决方案
使用mysql的explain命令,来对sql语句进行分析,尽量不让sql语句去全表扫描,并适当的加索引
- 并发修改同一条记录
出现原因
有两个事务,事务A先查询一条记录然后对该记录进行修改,事务B对该记录进行修改,事务A查询记录会加上共享锁,然后此时事务B想要修改记录,就要加上排他锁,但是由于事务A加了共享锁,事务B必须要等待事务A释放共享锁,但是事务A又需要修改该记录,企图将共享锁上升为排他锁,但是A由于B的排他锁,就不能去释放共享锁,这样导致了死锁
解决方案
- 乐观锁机制
乐观锁机制大多数基于版本(Version)记录来实现,也就是给表中新增一个字段version
,每次读取的时候将version一并读出,如果要修改,就对version+1,然后更新的时候,判断如果当前version大于我们加1的version,那么就证明为过期数据,直接丢弃,如果当前version小于我们加1的version,那么久证明不是过期数据,直接进行修改
- 悲观锁机制
悲观锁机制大多数基于数据库锁来实现,例如,使用Mysql的for update
保证锁的排他性,但是使用悲观锁机制,会让我们的数据库性能大幅度下降。