幻读
在一个事务中查询多次会出现不一样数目的结果集。
结论
在快照读情况下,可重复读可以避免幻读(MVCC)
在当前读情况下,可重复读还是会出现幻读。
当前读和快照读是针对某一行记录来说的。一个范围查询中当前读和快照读可以同时存在的
解释
快照读
快照读又叫一致性读,读取的是快照数据。不加锁的简单的 SELECT 都属于快照读,即不加锁的非阻塞读;普通的select *。。。。
当前读
当前读读取的是记录的最新版本(最新数据,而不是历史版本的数据),读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。加锁的 SELECT,或者对数据进行增删改都会进行当前
读。比如:
SELECT * FROM student LOCK IN SHARE MODE; # 共享锁
SELECT * FROM student FOR UPDATE; # 排他锁
INSERT INTO student values ... # 排他锁
DELETE FROM student WHERE ... # 排他锁
UPDATE student SET ... # 排他锁
MVCC
MVCC (Multiversion Concurrency Control),多版本并发控制。顾名思义,MVCC 是通过数据行的多个版本管理来实现数据库的 并发控制 。这项技术使得在InnoDB的事务隔离级别下执行 一致性读 操作有了保证。
可重复读隔离级别下的快照读,一个事务中查询语句会产生一个快照ReadView。然后每次查询都查不到之后新增或者修改的数据。
验证结论
表结构
CREATE TABLE `course` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`course_id` int(11) NOT NULL,
`course_name` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=217 DEFAULT CHARSET=utf8;
事务演示
时刻 | 事务1 | 事务2 | 事务1结果 |
---|---|---|---|
t1 | BEGIN; | 没开始 | 无 |
t2 | select * FROM course; | BEGIN; | 查询出1条数据 |
t3 | select * FROM course; | INSERT INTO course (course_id,course_name) values(6,‘事务2’); | 查询出1条数据 |
t4 | COMMIT; | 无 | |
t5 | select * FROM course; | 查询出1条数据 | |
t6 | UPDATE course set course_name=‘sqqs’ where course_id =6 | 修改影响1行 | |
t7 | select * FROM course; | 查询出2条数据 |
事务1在【t5】时刻查询还是一条数据,证明在可重复读下快照读不会出现幻读。
事务1在【t6】时刻进行了修改操作使得【course_id=6】(事务2在t4时刻提交的数据)变成了当前读。所以在【t7】时刻再读取时就读取到了course_id=6的数据。
时刻结果图
t1-t2时刻事务1查数据
t2-t4时刻-事务2进行插入数据
t5时刻事务1查数据
在t1-t2同一个事务下查询,这里是选择执行了,如下图。
结果还是一条数据,没有出现幻读。
t6时刻
事务1执行了修改操作使得course_id=6的数据变成了当前读。
t7时刻
因为t6时刻把course_id=6变成了当前读导致读取最新数据,所以出现了两条数据,造成了幻读现象。
最后结论
不能认为可重复读就一定会出现幻读问题,必须要上串行化级别才能解决幻读问题。
在快照读情况下,可重复读可以避免幻读(MVCC)
在当前读情况下,可重复读还是会出现幻读,要串行化级别才能解决。