MySQL可重复读-问题实践

本文通过实例演示了MySQL InnoDB引擎在可重复读隔离级别下如何避免脏读和不可重复读问题。在幻读问题上,虽然大部分情况下可重复读能防止幻读,但当事务修改了其他事务插入的数据后,仍会出现幻读现象。总结表明,可重复读在不修改其他事务插入的数据时可避免幻读,而修改后则会出现幻读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MySQL可重复读之幻读问题

MySQL事务存储引擎InnoDB的默认隔离级别为可重复读,在该隔离级别下,可以很大程度上避免幻读的问题,完全避免脏读和不可重复读问题,接下来通过实际的测试看看这些场景是否真的能够被完全避免

模拟均基于下表:

CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `username` varchar(20) NOT NULL COMMENT '用户名',
  `password` varchar(20) NOT NULL COMMENT '密码',
  `name` varchar(20) DEFAULT NULL COMMENT '真实姓名',
  `age` tinyint DEFAULT NULL COMMENT '用户年龄',
  `address` varchar(30) DEFAULT NULL COMMENT '现居地址',
  PRIMARY KEY (`id`),
  KEY `idx_name_age` (`name`,`age`),
  KEY `idx_username` (`username`)
) ENGINE=InnoDB

表中具有如下数据:

在这里插入图片描述

不可重复读问题

我们首先通过如下示例看看MySQL的InnoDB究竟是否能避免不可重复读问题

以下测试基于MySQL8

首先查看MySQL的默认隔离级别:

# 查看当前会话下的隔离级别
select @@transaction_isolation;
# 查看全局隔离级别
select @@global.transaction_isolation;

在这里插入图片描述

可以发现,当前会话的隔离级别为REPEATABLE-READ,可重复读

首先,我们来看看可重复读的情况下是否会出现不可重复读的问题

什么是不可重复读: 在一个事务中,前后相同的两次查询,结果应当是一样的,但是查询返回的结果确不同。也就是说,同一份数据在同一个事务中不能被重复读。出现这种情况的可能原因是,当一个事务执行某个查询后,数据被另一个事务更新,当前事务再次执行该查询会得到更新后的数据。它违背了数据库设计的ACID原则

模拟不可重复读的场景:

1、开启两个MySQL客户端连接,并且分别开启事务(分别为事务1和事务2):

开启事务的语句如下:

begin;

在这里插入图片描述

2、在事务1中通过如下SQL语句查询数据库的数据:

select * from user where id = 4;

结果如下:

在这里插入图片描述

3、在事务2中对user表中id为4的数据进行修改,并查看修改结果:

update user set password = '4' where id = 4;
select * from user where id = 4;

结果如下:
在这里插入图片描述

可以发现在当前事务修改已生效,在另一个事务中进行查询

4、在事务1中查询user表中id为4的数据:

select * from user where id = 4;

结果:可以发现这里password还是5,事务2的未提交的修改并未影响事务1的结果,这里可以证明可重复读避免了脏读的问题

在这里插入图片描述

5、事务2提交修改:

commit;

6、事务1继续进行查询:

select * from user where id = 4;

结果如下:可以发现即使事务2提交了,事务1在读取的过程中,数据也没有改变,这里可以证明可重复读避免了不可重复的问题,一个事务的修改即使提交了,也不会影响另一个事务的读取

在这里插入图片描述

7、事务1提交后再读取数据:

commit;
select * from user where id = 4;

结果如下:

在这里插入图片描述

幻读问题

MySQL可重复读隔离级别下会不会出现幻读问题,接下来在模拟中看看实际效果

什么是幻读:假设有两个事务,分别是事务A和事务B,事务A首先以某一where条件读取数据集合;随后事务B向数据库中插入满足事务A查询where条件的数据,并且提交;事务A再次以相同的where条件查询数据时,读取到的数据集合与之前读取的数据集合不同(多了一条数据或少了一条数据),这种情况就是幻读问题

模拟幻读问题:

1、开启两个客户端连接,并分别开启事务(分别为事务1和事务2):

begin;

2、事务1通过如下查询语句查询数据:

select * from user where id > 1 and id < 6;

结果如下:

在这里插入图片描述

3、事务2向user表中插入id为5的数据:

insert into user(id, username, password, name, age, address) values(5, '5', '5', '王五', 12, 'hubei');
select * from user;

结果如下:

在这里插入图片描述

可以发现当前事务已经存在id为5的数据了,然后我们直接提交事务2

commit;

再次查询:

select * from user;

结果如下:可以发现数据已经插入数据库中

在这里插入图片描述

4、事务1再以相同条件查询数据:

select * from user where id > 1 and id < 6;

结果如下:可以发现并未查询到id为5的数据,这里可以证明可重复读隔离级别避免了幻读问题

在这里插入图片描述

5、在事务1中对id为5的数据进行修改,然后再次以相同的条件进行查询:

update user set password = '6' where id = 5;
select * from user where id > 1 and id < 6;

结果如下:可以发现这次查询又查到了id为5的数据,这里就出现了幻读的问题

在这里插入图片描述

从上面的模拟中可以发现,MySQL可重复读隔离级别在大部分场景下是可以避免幻读问题的,但是在第5中场景下是无法避免幻读问题,也就是:

  • 当事务2向数据库中插入满足事务1查询where条件的数据后,并且commit,此时事务1以相同的where条件查询时是不会读取到新插入的数据的,这就避免了幻读问题
  • 当事务1修改了事务2插入的数据之后,也就是update语句修改了事务2插入的数据,再次以相同的条件查询数据库时,会发现查询出来事务2插入的数据,出现了幻读问题

总结:MySQL可重复读隔离级别在当前事务不修改其他事务插入的数据时,是可以避免幻读的;但是当前事务修改了其他事务插入的数据之后,还是会出现幻读的问题

本篇文章从实践的角度讲解了MySQL可重复读隔离级别下避免脏读、不可重复读、幻读的场景,也把可能出现的幻读问题进行模拟

接下来会从MVCC原理的角度来讲解MySQL的可重复读是如何实现的,敬请期待

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值