幻读指的是一个事务开启之后,执行了两次相同的 SELECT 查询某一范围内的数据,但是第二次查询返回了第一次未返回的行,也就是读取到了幻行,这就是幻读问题。
MySQL 官方也将这个问题叫做幻象问题,读取到的行叫做幻行。
地址:https://dev.mysql.com/doc/refman/8.0/en/innodb-next-key-locking.html
如何解决幻读问题?
在默认的可重复读(RR)隔离级别下:
针对快照读,InnoDB 通过 MVCC 的方式解决幻读问题,原理就是在事务开始之前就会生成一个 ReadView ,后续的查询都是基于这个 ReadView 进行的。
注意:如果在快照读期间执行了当前读,则 ReadView 会失效,进而产生幻读问题。
针对当前读,InnoDB 通过 Next-key Lock 解决幻读问题,原理就是 InnoDB 不止锁定查询中涉及的行,还会对索引结构中的间隙进行锁定,防止幻行被插入。
快照读:读取某一时刻生成的最新快照数据,即普通的 SELECT 语句,例如 SELECT * FROM t WHERE xx = yy
当前读:读取最新提交的数据,即 UPDATE、INSERT 、DELETE 以及 SELECT ... FOR UPDATE 的语句。
在串行化(SERIALIZABLE)隔离级别下:该隔离级别通过强制事务按序执行,使得不同事务之间不可能产生冲突,从而解决了幻读问题。
为什么可重复读隔离级别下的 MVCC没法彻底的解决幻读问题?
MVCC 解决幻读主要是针对快照读而言的,但是如果在快照读期间执行了当前读,比如 UPDATE、INSERT、DELETE、SELECT ... FOR UPDATE
等操作,那么快照读的 ReadView 就会失效,此时当前读会去读取最新的数据,进而产生幻读问题。
案例一:
当前表 tp 的数据如下所示:
事务 A 的操作
BEGIN;
#执行快照读
SELECT * FROM tp WHERE id = 5; #没数据
...执行事务B
#执行当前读,尝试更新id=5的数据
UPDATE tp SET age = 500 WHERE id = 5;
#执行快照读,再次查询id=5的数据
SELECT * FROM tp WHERE id = 5; #查询到了数据,此时事务还没提交哦!两次查询的结果不一样,发生了幻读。
.........
COMMIT;
事务 B 的操作
BEGIN;
#插入一条数据
INSERT INTO tp VALUE(5,50)
COMMIT;
案例二:
当前表 tp 的数据如下所示:
事务 A 的操作
BEGIN;
#执行快照读,查询年龄大于10的所有数据,查询到20,30,40三条数据
SELECT * FROM tp WHERE age > 10;
....事务B执行
#执行当前读,依旧查询年龄大于10的所有数据
SELECT * FROM tp WHERE age > 10 FOR UPDATE;
竟然把事务B刚插入的数据(5,50)也查询到了,发生了幻读。
....
COMMIT;
事务 B 的操作
BEGIN;
#插入数据
INSERT INTO tp VALUE(5,50);
COMMIT;
标签:事务,快照,幻读,tp,查询,问题,MySQL,SELECT
From: https://www.cnblogs.com/wangjiawu/p/17991705