幻读 : 针对update和delete操作里面的where条件查找满足条件的记录,InnoDB引擎会返回的所有满足条件的数据。
实际场景
【幻读】则关注: 行数量是否发生变化。【不可重复读】关注: 行内容是否发生变化。
能解决幻读问题的有两种:
一个事务能读到的数据视图有 : a) MVCC快照读: 初始看到的是该事务第一次查询获取到的已提交数据的快照版本。如果没有发生“当前读”,一直都如此。 b) 事务“当前读” :被其他事务更新且提交后的数据。(事务自己更新未提交的也能看到)
【插入、更新、删除】操作到的数据为当前的最新值版本,称为当前读。
直接在另外事务执行commit | rollback 后再次查询也可以读到最新版数据。
加锁是实现在索引上的,如果没有索引则会全表加锁,不同场景下的锁是不一样的。
准备操作:
set session transaction isolation level REPEATABLE READ; -- 设置RR级别 select @@tx_isolation; SET AUTOCOMMIT= 0; -- 设置手动处理事务 show session variables like 'autocommit'; start TRANSACTION
通过2个事务的交替执行进行验证。
1、A、B 事务初始查询看到的数据视图
2、B事务更新数据但未提交时:
A看到的还是事务开始时初始查询的数据视图;===> 这个是肯定的,除非隔离级别是 "读未提交"。
B能看到更新后的视图,虽然此时未提交。
3、B事务提交时:
A依然是事务开始时初始查询的数据视图,B看到的是自己修改后的数据视图; 若此时有新的事务C来查看则能看到commited的数据。 ====> RR: 快照读,解决不可重复读问题; RC: 每次读最新事务提交的结果。
4、此时A事务指定相同字段过滤更新:
结果: 更新0条 。 ===> 虽然A事务select时看到的是初始视图,是有符合该过滤条件的,但在最新的commited数据集里都不符合条件,所以无法更新成功。
important! : 此时A事务直接执行commit ,会看到最新commited后的数据!
5、此时A事务进行全量更新但未提交:
结果: 能更新这2条记录。===> 事务能看到自己更新后的视图,即使未提交。
6、此时B事务接着插入一条新的数据,id值比现有的都要大。
结果: 因为A事务还没有commit, 会造成锁等待阻塞。 ===> "当前读"进行了加锁操作。 (按当前测试的sql来说,因为没有使用到索引,此时加的是全表行锁和含上下界的Gap锁)
7、A事务进行commit操作:
结果:新的C事务(RR + autoCommit)数据真实视图已经更改。
但B事务查看到的是:自己事务中提交更新后的视图。
8、B事务新增加一条数据,并提交
结果:
C事务(RR+ autoCommit)真实视图已经更改 , B事务看到也是如此
A事务看到没有变化,没有新加的这个“9”。
9、A事务进行全局更新提交:update test set token = "ccccccccccc"; commit; (是否有过滤条件不影响最终结论,只影响加锁的类型)。
(需要注意的是: 这里仅单独执行commit也能达到效果,不提交也是看不到多的那条数据的)
此时A事务再来select下:
结果: 多了一条数据值!也能正确commit。 这样就说明解决了幻读问题
插入一个已经存在的主键时,insert时先加的是共享读锁S锁
来判定唯一约束 。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。