天天看點

MySQL 的 Innodb 引擎在 REPEATABLE-READ(RR) 的事務隔離級别下,幻讀問題記錄1.背景2.實驗前提3.幻讀場景14.幻讀場景25.總結6.原因分析

目录

  • 1.背景
  • 2.实验前提
  • 3.幻读场景1
  • 4.幻读场景2
  • 5.总结
  • 6.原因分析

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 现象 当前读/快照读
在

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’ 已经存在 当前读

5.总结

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

6.原因分析

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

繼續閱讀