08 04 2021

什么是幻读?

事务A按照一定条件进行数据读取,期间事务B插入了相同搜索条件的新数据,事务A再次按照原先条件进行读取时,发现了事务B新插入的数据称之为幻读。

  1. CREATE TABLE `user` (
  2. `id` int(11) NOT NULL AUTO_INCREMENT,
  3. `name` varchar(255) DEFAULT NULL,
  4. `age` int(11) DEFAULT NULL,
  5. PRIMARY KEY (`id`)
  6. ) ENGINE=InnoDB ;
  7. INSERT into user VALUES (1,'1',20),(5,'5',20),(15,'15',30),(20,'20',30);

假设有如下业务场景:

时间 事务1 事务2
T0 begin;
T1 select * from user where age = 20; (2个结果) begin;
T2 insert into user values(25,’25’,20);commit;
T3 select * from user where age =20;(2个结果)
T4 update user set name=’00’ where age =20;(此时看到影响的行数为3)
T5 select * from user where age =20;(3个结果)  

执行流程如下:

1、T1时刻读取年龄为20的数据,事务1拿到了2条记录;

2、T2时刻另一个事务插入一条新的记录,年龄也是20;

3、T3时刻,事务1再次读取年龄为20的数据,发现还是2条记录,事务2插入的数据并没有影响到事务1的事务读取;

4、T4时刻,事务1修改年龄为20的数据,发现结果变成了三条,修改了三条数据;

5、T5时刻,事务1再次读取年龄为20的数据,发现结果有三条,第三条数据就是事务2插入的数据,此时就产生了幻读情况。

此时大家需要思考一个问题,在当下场景里,为什么没有解决幻读问题?

其实通过前面的分析,大家应该知道了快照读和当前读,一般情况下select from ….where …是快照读,不会加锁,而 for update,lock in share mode,update,delete都属于当前读,*如果事务中都是用快照读,那么不会产生幻读的问题,但是快照读和当前读一起使用的时候就会产生幻读

如何解决幻读问题?

如果都是当前读的话,如何解决幻读问题呢?

  1. truncate table user;
  2. INSERT into user VALUES (1,'1',20),(5,'5',20),(15,'15',30),(20,'20',30);
时间 事务1 事务2
T0 begin;
T1 select * from user where age =20 for update; begin;
T2 insert into user values(25,’25’,20);(此时会阻塞等待锁)
T3 select * from user where age =20 for update;  

此时,可以看到事务2被阻塞了,需要等待事务1提交事务之后才能完成,其实本质上来说采用的是间隙锁的机制解决幻读问题。

延伸阅读
  1. MySQL 5.7 详细安装步骤
  2. Linux下MySQL的彻底卸载
  3. 一文读懂 MySQL 事务
  4. MySQL 怎么解决幻读问题
  5. MySQL join的使用和原理
发表评论