死锁判定原理和具体场景

Posted by JimWang on 2021-02-23

死锁判定原理和具体场景

什么是死锁

数据库是一个多用户使用的共享资源,当多个用户 并发地存取数据的时候,在数据库中就会发生多个事务同时存取同一个数据的情况,加锁是进行数据库并发控制的一种非常重要的技术。在实际应用中,如果两个事务需要一组有冲突的锁,而不能继续进行下去,这时便发生了死锁。

死锁的原因

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

此类常见于两个用户分别首先访问A表和B表,并锁住当前表,但是双方都需要访问对方锁住的表,都在等待对方释放锁,
也就是经典的刀叉问题。像这样的死锁,基本上就是程序员的编程逻辑出现了问题,需要调整程序的逻辑。

2.并发修改同一记录

此类常见于用户A查询一条纪录,然后修改该条纪录;这时用户B修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。 这种死锁由于比较隐蔽,但在稍大点的项目中经常发生。像这样的解决办法就是使用乐观锁,在表的字段中增加version字段,更新时进行查询version是否为当前取到version。乐观锁可以避免长事务中的数据库加锁开销。最不推荐的是采用悲观锁进行控制,悲观锁会使排队等待的时间相当漫长,会发生灾难性的后果。

  • 独占锁获取了同步状态之后,其他独占和共享访问都会阻塞。 共享锁获取了同步状态之后,共享访问不会被阻塞,独占访问会被阻塞。

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

此类常见于在事务中执行了一条不满足的语句,执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行之后,就很容易产生死锁和阻塞。类似的情况还有当表中的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,最终发生阻塞或死锁。解决其办法有,SQL语句中不要使用太复杂的关联多表的查询;使用“执行计划”对SQL语句进行分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。

4.事务封锁范围大且相互等待
保持事务简短并在一个批处理中
在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁。

使用低隔离级别
确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间,从而降低了锁定争夺(比如这次的S NK和X IK 是InnoDB引擎Repeatable Read级别才有的)。

如何解决死锁问题

  • 查出死锁的线程杀死
  • 设置锁的超时时间
  • 指定获取锁的顺序

部分转载自:https://blog.csdn.net/xiaheshun/article/details/81393796