要说幻读,⾸先要了解MVCC,MVCC叫做多版本并发控制,实际上就是保存了数据在某个时间节点的快照。
我们每⾏数实际上隐藏了两列,创建时间版本号,过期(删除)时间版本号,每开始⼀个新的事务,版本号都会⾃动递增。
还是拿user表举例⼦,假设我们插⼊两条数据,它们实际上应该⻓这样
这时候假设⼩明去执⾏查询,此时current_version=3
select * from user where id<=3;
同时,⼩红在这时候开启事务去修改id=1的记录,current_version=4
update user set name='张三三' where id=1;
执⾏成功后的结果是这样的
如果这时候还有⼩⿊在删除id=2的数据,current_version=5,执⾏后结果是这样的。
由于MVCC的原理是查找创建版本⼩于或等于当前事务版本,删除版本为空或者⼤于当前事务版本,⼩明的真实的查询应该是这样
select * from user where id<=3 and create_version<=3 and (delete_version>3 or delete_version is null);
所以⼩明最后查询到的id=1的名字还是'张三',并且id=2的记录也能查询到。这样做是为了保证事务读取的数据是在事务开始前就已经存在的,要么是事务⾃⼰插⼊或者修改的。
明⽩MVCC原理,我们来说什么是幻读就简单多了。举⼀个常⻅的场景,⽤户注册时,我们先查询⽤户名是否存在,不存在就插⼊,假定⽤户名是唯⼀索引。
1. ⼩明开启事务current_version=6查询名字为'王五'的记录,发现不存在。
2. ⼩红开启事务current_version=7插⼊⼀条数据,结果是这样:
3. ⼩明执⾏插⼊名字'王五'的记录,发现唯⼀索引冲突,⽆法插⼊,这就是幻读。
网友评论