概述
定义:
也就是同样的select读取sql语句读到的数据,后一次比前一次读到更多的数据行(事务在执行两句select语句中间时间,有其他事务往数据库中加入了数据行)
事务执行过程中单独给每一行加写锁并不能阻止幻读,因为事务不能给当前没有记录的行加锁,所以其他事务可以加入新的数据,那么前后两次select就可能查到多出的数据行。如下图所示:
幻读的解决办法:
使用next-key lock(间隙锁 Gap key + 行锁的合称),间隙所就是锁住相邻两行记录之间的空行(前开后闭区间)。比如:(0, 10],(10, 15], (15, +∞]。
next-key lock两阶段形成过程:先在(0,9]上加间隙锁,然后合并行锁10,得到(0,10]
间隙锁存在的问题是会引入死锁:
因为间隙锁不同于行锁,行锁是只有一个事务可以对该行加锁,但是无数据的间隙行上可以被多个事务加间隙锁。之后间隙锁和行锁会形成next-key lock,那么当一行空数据的间隙行同时在两个事务中形成了next-key lock的时候,两个事务都试图在该行插入数据,就会进入死锁。
根本原因:间隙锁共享加锁过程,但是独占更新过程。也就是说同一行可以被多个事务加间隙锁,但是间隙锁会和行锁进行结合形成next-key lock那么该行就只能被一个事务进行更新操作。由于间隙锁是两阶段形成的,所以如果第一阶段已经形成了间隙锁,就算没有融合行锁成为next-key lock,其他事务也不能对该事务上加间隙锁的行进行更新,就会形成事务之间的锁竞争,如下图所示:
间隙锁出现死锁的解决方法:
- MySQL的死锁检测会立即触发,回滚其中一个事务,但会影响并发度。
- 此外间隙锁只有在事务为可重复读的隔离级别下生效,如果使用已提交读则不存在间隙锁,当然也不会有他造成的死锁,问题是会出现幻读。
最后总结一下:
- MySQL只会对查询过程中扫描到的行加锁,锁的基本单位为next-key lock,满足优化条件则会降低锁粒度(可重复读);
- 无论是不是唯一索引,MySQL都需要扫描到下一行不满足查询条件的记录才停止。(也就是说最后一条扫描记录肯定不满足查询条件);
- 等值查询时MySQL对间隙锁的优化(降低锁粒度):当用唯一索引进行查询时,next-key lock会退化为行锁,不加间隙锁;当不是唯一索引时会退化为间隙锁,不包含查询到的行记录上的行锁。
参考文章:
https://time.1geekbang.org/column/article/75173
最后
以上就是单薄小懒猪为你收集整理的MySQL的幻读的全部内容,希望文章能够帮你解决MySQL的幻读所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复