间隙锁(Gap Lock):
锁加在不存在的空闲空间,加在两个索引之间的锁,也可能是第一个索引记录之前或最后一个索引,使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。间隙锁只阻止其他事务插入到间隙中,他们不阻止其他事务在同一个间隙上获得间隙锁。
项目上运维发现我保存面单的表有大量的死锁,然后发现是插入的时候出现了间隙锁的情况,因为业务场景是多个队列同时进行的同一个操作,并发的情况不可避免。间隙锁是innodb中行锁的一种, 但是这种锁锁住的却不止一行数据,他锁住的是多行,是一个数据范围。
间隙锁的主要作用是为了防止出现幻读,但是它会把锁定范围扩大,有时候也会给我们带来麻烦。
在数据库参数中, 控制间隙锁的参数是:innodb_locks_unsafe_for_binlog,这个参数默认值是OFF, 也就是启用间隙锁, 他是一个bool值, 当值为true时表示disable间隙锁。
那为了防止间隙锁是不是直接将innodb_locaks_unsafe_for_binlog设置为true就可以了呢? 不一定!而且这个参数会影响到主从复制及灾难恢复, 这个方法还尚待商量。
出现的主要原因
间隙锁的出现主要集中在同一个事务中先delete后 insert的情况下,我遇到的就是这种情况,先把旧面单删除然后保存了新面单。 当我们通过一个参数去删除一条记录的时候, 如果参数在数据库中存在,那么这个时候产生的是普通行锁,锁住这个记录, 然后删除, 然后释放锁。如果这条记录不存在,问题就来了, 数据库会扫描索引,发现这个记录不存在, 这个时候的delete语句获取到的就是一个间隙锁,然后数据库会向左扫描扫到第一个比给定参数小的值,向右扫描扫描到第一个比给定参数大的值, 然后以此为界,构建一个区间, 锁住整个区间内的数据, 一个特别容易出现死锁的间隙锁诞生了。
解决办法:
- 通过修改数据库的参数innodb_locaks_unsafe_for_binlog来取消间隙锁从而达到避免这种情况的死锁的方式尚待商量。
- 记录存在才删除,尽量不去删除不存在的记录,要删除之前先确认是否存在。
参考:https://www.cnblogs.com/jing99/p/11489510.html