当某个事务a范围查询数据时,另一个事务b在该范围内插入了数据,当事务a再次范围查询时,会产生幻行(即多了事务b插入的那行数据)。
看看幻读的正确理解
时间点 | 事务A | 事务B |
---|---|---|
1 | 开启事务 | |
2 | 开启事务 | |
3 | 查询数据“张三”,不存在 | |
4 | 插入数据“张三” | |
5 | 提交事务 | |
6 | 查询数据“张三”,不存在 | |
7 | 插入数据“张三”,不成功 |
事务A查询“张三”,查询不到,插入又不成功,“张三”这条数据就像幻觉一样出现。这就是所谓的“幻读”。网上对“幻读”还是其他的解释,都是错误的。比如像“幻读”和“不可重复读”是一样,只不过“幻读”是针对数据的个数。这些理解都是错误的。
参考链接:MySQL中的幻读,你真的理解吗? - 云+社区 - 腾讯云
先说结论:【基本定义】的理解是对的,【网络上的其他定义】是不准确的。
1. 幻读是repeatble read隔离级别下的产物,但并不是repeatble read隔离级别应该有的现象。按照隔离级别的定义,重复读一定是相同的结果。
2. 那为啥会产生幻读呢?是mysql等数据库一开始在范围查询时,没有加范围锁。其他事务b可以在事务a的处理过程中在范围内插入新纪录。(数据库未能完全实现repeatble read隔离,导致了幻读现象)
3. 解决幻读的措施:众所周知的mvcc机制。
4. 解决幻读的另一个措施:next-key锁(范围加锁)。
参考链接:MySQL :: MySQL 8.0 Reference Manual :: 15.7.4 Phantom Rows
mysql的 mvcc实现,会隐含的给数据表加上两个版本控制列。
它的insert操作,插入数据的同时,会写入版本号。
它的update操作,其实是插入了一条新的数据,同时更新了新老数据的版本号。
所以会导致以下情况:
1. 事务a查询数据1(空)-》事务b插入数据1-》事务a查询数据1(空)-》事务a插入数据1(失败)。这非常正常!已经有数据1了,凭什么事务a要插入成功?它并不是幻读
2. 事务a查询数据1(空)-》事务b插入数据1-》事务a查询数据1(空)-》事务a更新数据1(成功)-》事务a查询数据1(有数据)。这也很正常,因为update其实是一个insert操作,它的版本号已经更新了,为啥不能读到。它也不是幻读。
总的来说,那些错误的定义,描述的是mysql解决幻读后出现的现象,并不是幻读本身。查找了很多博客,发现幻读真的是众说纷纭,最后找到的是mysql官网的解释,加上mysql的mvcc版本号机制,推测了部分错误定义的来源,也不一定对,仅供参考。