记录一下遇到的MySQL出现死锁的场景,持续记录
以下都是针对MySQL的Innodb的可重复读级别
第一种,先查(删、改)再插问题,先用当前读检查一条记录是否存在,如果不存在的话在插入一条记录。这种场景下,无论是用什么索引,只要用到了索引,而且这条记录是不存在的话,就会在查询的位置的下一条记录上加一个间隙锁,如果此时第二个事务也想在这个范围插入一条记录,那么就要检查它插入的这条记录存不存在,如果不存在,也会在同一个记录上加间隙锁,因为间隙锁是兼容的,所以两个事务同时对一个范围加间隙锁是可以的,然后第一个事务执行插入语句的时候,会因为第二个事务加了间隙锁而无法插入,第二个事务执行插入语句时,也会因为第一个事务加了间隙锁而无法插入,这时两个事务都在持有一个间隙锁,且都在等待对方释放对方的间隙锁,就会发生死锁
第二种,相互转账问题,先用当前读查询一个用户的余额是否足够,然后再修改这个用户和另一个用户的余额。这种场景下,当两个用户同时相互转账就有可能出现死锁问题,如果事务A当前读了用户A的余额,会给用户A这条记录加上x型记录锁,事务B当前读了用户B的余额,会给用户B这条记录加上x型记录锁,此时事务A修改完用户A的余额之后,再去修改用户B的余额,会因为事务B加的记录锁发生阻塞,事务B修改完用户B的余额之后,再去修改用户A的余额,也会发生阻塞,这个时候双方都持有记录锁,并等待对方释放对方的记录锁,就会发生死锁
第三种,共享读模式的先查后改问题,先用共享读查询一条记录,然后再修改这个记录。这种场景下,当事务A共享读了一条记录,事务B也共享读了同一条记录,两个事务都会对这条记录加上s型的记录锁,然后事务A去修改这条记录时会被事务B加的共享锁阻塞,事务B在修改这条记录时会被事务A加的共享锁阻塞,这个时候双方都在都持有这个记录的共享锁,都想对这条记录加排它锁,但是都在等待对方释放对方的共享锁
第四种,乱序批量更新问题,先后执行update语句,where中的条件是乱序的。这种场景下,当事务A以1,2的顺序更新数据库,事务B以2,1的顺序更新数据库,这时就容易出现死锁;事务A给1加上X型的记录锁,事务B给2加上X型的记录锁,然后事务A等待事务B释放2的记录锁,事务B等待事务A释放1的记录锁;这种情况可以通过给where的条件做排序来解决
第五种,插入出现唯一键冲突时回滚问题,三个事务同时插入相同的唯一键,第一个事务回滚了。这种场景下,已经把隐式锁转换为显示锁了,事务A会持有这个记录的X型锁,事务B和C会等待这个记录的S型锁,这个S型锁是什么锁取决于唯一键是不是主键,如果是主键,那么是记录锁,如果不是主键,那么是临键锁,此时A回滚了,事务B和事务C都会获得这个S型锁,S型锁之间不会冲突,然后分别执行插入操作,但是插入需要获得插入意向锁,因为这个记录上有对方加的S型锁,那么此时就出现了两个事务持有S型锁,都想获得插入意向锁,但是都在等待对方释放S型锁
第六种,插入出现唯一键冲突时提交问题,三个事务同时插入相同的唯一键,第一个事务回滚了,其余两个事务还要执行新的插入操作。这种死锁并不是必现的,要求唯一键不是主键。由于事务A提交,事务B和事务C都会报唯一键冲突的错,此时,因为发生唯一键冲突之后事务B和事务C在主键索引插入的记录要被删除,所以事务B和事务C在主键索引上的隐式锁会转换成显式锁,转移到下一条记录上,这是个X型的间隙锁(对于supremum是临键锁);此时如果事务B和事务C都要在这个区间内插入数据,那么出现它们都持有间隙锁,都想获得插入意向锁,但是都在等待对方释放间隙锁
例如记录的主键使用的是自增id,那么事务B和事务C在唯一键冲突之后,会对supremum加上X型临键锁(隐式锁转移),此时事务B和事务C又要继续使用自增id去插入新的记录,但是都要等待对方释放supermum的临键锁,那么就会死锁了
如果错误,请斧正
欢迎补充