幻读与间隙锁
# 幻读与间隙锁
幻读:同一个事务内,先后两次查询同一个范围内的数据,得到的结果出现不一致的现象。后一次查询看到了前一次查询看不到的数据。
①幻读只会出现在for update当前读的场景下。普通查询是快照读,读到的数据都是一致的,肯定不会出现幻读。
②幻读仅仅特指通过insert新插入的数据。对于for updaet读到的其它事务更新后的数据,不属于幻读。
# 1.for update表锁
for update:执行时在RR隔离级别中会锁住表中所有的行记录,commit提交时才会释放所有行记录的行锁。在RC隔离级别,语句执行前会给所有行记录加锁,执行完对应查询语句之后,InnoDB引擎会把不满足查询条件的行锁去掉。
假设数据库初始仅存在(id=5,d=5)这条数据。别看A和B看上去好像风马牛不相及,sessionB执行更新语句会被阻塞,直到sessionA提交后才会执行更新操作。

🔥for update的全表锁,解决了数据与日志不一致的问题。确保了保存日志的时序与所有更新操作执行的时序保持一致。
假设for update加的是行锁,那么sessionB会正常执行,最后sessionA执行提交。此时数据库中的数据(id=5,d=100),(id=3,d=5)。
而此时bin log日志:sessionB(id=3,d=5)——>sessionA更新所有d=5变为d=100。如果按照bin log日志进行恢复,最终得到的是(id=5,d=100),(id=3,d=100)。显然与当前数据是不一致的。
如果for update加的是表锁,那么整个执行和写binlog的流程为①A执行完所有更新操作②将A所有操作写入binlog③B执行更新操作④写B操作进binlog
# 2.幻读存在的问题
✨for update遗留问题:对于幻读插入的新的行记录,全表锁并不能进行覆盖和约束。也就是说在sessionB的同一时刻,sessionC新插入一条d=5的行记录,那么它的日志因为先于sessionA写入binlog,因此最终也会出现数据日志不一致的现象。
# 3.间隙锁
为了解决幻读导致数据与日志不一致的问题,InnoDB引入了间隙锁(Gap Lock)。
间隙锁:每个相邻两行记录之间(id)的间隙加锁,添加间隙锁后,这两行记录之间不准插入或是删除新的行记录。间隙锁是左开右闭区间,添加间隙锁时需要找到行记录落在的区间。
next-key lock:行锁+间隙锁。
多个线程可以加同一个间隙的间隙锁,但它们是分别持有的关系,只有当所有线程事务提交后,撤去其它线程各自的间隙锁,才能够对这个间隙进行插入和删除(当前事务添加的间隙锁不会阻塞当前事务的更新操作)。
🔥for update不仅会给表中所有记录添加行锁,同时还会在行与行之间添加间隙锁。
# 4.间隙锁存在的问题
✨间隙锁遗留问题:间隙锁虽然增加了锁的粒度和范围,但是在高并发场景下,容易造成死锁。

上面场景中B插入语句等待A释放间隙锁,而A也等待B释放间隙锁。从而造成死锁的情况。
解决方案:将隔离级别设置为RC读已提交关闭间隙锁,同时binlog_format=row。