欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术的推送!
在我后台回复 「资料」 可领取
编程高频电子书
!
在我后台回复「面试」可领取硬核面试笔记
!文章导读地址:点击查看文章导读!
感谢你的关注!
在可重复读隔离级别中,通过临键锁
在一定程度上缓解了幻读的问题,但是在特殊情况下,还是会出现幻读
以下两种情况下,会出现 幻读
,大家可以先看一下如何出现的幻读, 思考一下为什么会出现幻读
,答案会写在后边!
- 情况1:事务 A 通过更新操作获取最新视图之后,可以读取到事务 B 提交的数据,出现幻读现象
对于下图中的执行顺序,会出现幻读现象,可以看到在事务 A 执行到第 7 行发现查询到了事务 B 新提交的数据了
这里都假设使用的 InnoDB 存储引擎,事务隔离级别默认都是 可重复读
在可重复读隔离级别下,使用了 MVCC 机制,select 操作并不会更新版本号,是快照读(历史版本),执行 insert、update 和 delete 时会更新版本号,是当前读(当前版本),因此在事务 A 执行了第 6 行的 update 操作之后,更新了版本号,读到了 id = 5 这一行数据的最新版本,因此出现了幻读!
- 情况2:事务 A 在步骤 2 执行的读操作并不会生成间隙锁,因此事务 B 会在事务 A 的查询范围内插入行
对于下边这种情况也会出现幻读,在第 6 行使用 select ... for update
进行查询,这个查询语句是当前读(查询的最新版本),因此查询到了事务 B 新提交的数据,出现了幻读!
那么对于以上两种情况来说,为什么会出现幻读呢?
对于事务 A 出现了幻读,原因就是,事务 A 执行的第 2 行是普通的 select 查询,这个普通的 select 查询是快照读,不会生成临键锁(具体生成临键锁、记录锁还是间隙锁根据 where 条件的不同来选择),因此就 不会锁住这个快照读所覆盖的记录行以及区间
那么事务 B 去执行插入操作,发现并没有生成临键锁,因此直接可以插入成功
重要:那么我们从代码层面尽量去避免幻读问题呢?
在一个事务开始的时候,尽量先去执行 select ... for update
,执行这个当前读的操作,会先去生成临键锁,锁住查询记录的区间,不会让其他事务插入新的数据,因此就不会产生幻读
这里我也画了一张图如下,你也可以去启动两个会话窗口,连接上 mysql 执行一下试试,就可以发现,当事务 A 执行 select ... for update
操作之后,就会加上临键锁(由于 where 后的条件是 id=5,因此这个临键锁其实会退化为记录锁,将 id=5 这一行的数据锁起来),那么事务 B 再去插入 id=5 这条数据,就会因为有锁的存在,阻塞插入语句