欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

MySQL 的 Innodb 在 REPEATABLE-READ 的事务隔离级别下,幻读问题记录

程序员文章站 2022-06-14 16:37:06
...

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

实验前提:

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.这里需要了解快照读和当前读两个概念。

幻读场景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

幻读场景2

序号 事务A 事务B 现象 当前读/快照读
0 username 字段建立唯一索引
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’ 已经存在 当前读

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

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