1.背景

看过一些博客和文章,有的说 MySQL Innodb RR 级别下,解决了幻读问题,也有的说没有解决,也还有一些说解决了部分幻读问题,但是都没有想过实验去论证,据着严谨的我,特此实验一番记录一下

2.实验前提

1.使用 SELECT @@tx_isolation 查询当前的事务隔离级别,确保是 REPEATABLE-READ

2.执行以下表结构语句创建表

CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL,
  `pwd` varchar(20) NOT NULL,
  `is_deleted` tinyint(4) NOT NULL,
  `gmt_created` datetime NOT NULL,
  `gmt_modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `t_user_username_uindex` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin

3.这里需要了解快照读和当前读两个概念。

3.幻读场景1

序号事务A事务B现象当前读/快照读
1开始事务(start transaction)开始事务(start transaction)无异常
2查询表所有数据(select * from t_user)事务A执行结果为空快照读
3插入一条数据(insert into t_user(id, username, pwd, is_deleted, gmt_created, gmt_modified) values(1, ‘test’, ‘test’, 0, now(), now()))事务B执行成功当前读
4提交事务(commit)事务B事务提交成功
5再次查询表所有数据(select * from t_user)事务A执行结果和第2步的一致快照读
6再次查询表所有数据(select * from t_user for update)事务A执行结果和第2步的不一致当前读
7提交事务(commit)

这种场景在实际工作中极少出现,可以接着看场景2

4.幻读场景2

序号事务A事务B现象当前读/快照读
0username 字段建立唯一索引
1开始事务(start transaction)开始事务(start transaction)无异常
2查询表所有数据(select * from t_user where username = ‘aaa’)事务A执行结果为空快照读
3插入一条数据(insert into t_user(id, username, pwd, is_deleted, gmt_created, gmt_modified) values(2, ‘aaa’, ‘aaa’, 0, now(), now()))事务B执行成功当前读
4提交事务(commit)事务B事务提交成功
5插入一条数据(insert into t_user(id, username, pwd, is_deleted, gmt_created, gmt_modified) values(3, ‘aaa’, ‘aaa’, 0, now(), now()))事务A语句执行报错,说username = ‘aaa’ 已经存在当前读

5.总结

MySQL 的 Innodb 在 REPEATABLE-READ 的事务隔离级别下,会发生幻读现象。

6.原因分析

在一个事务中,先从快照读中读取数据,后从当前读读取数据,会出现数据不一致的情况,因此出现幻读现象。当一个事务中,全程都是快照读,则不会出现幻读现象。

Logo

更多推荐