MySQL 是如何解决幻读的

本文介绍MySQL如何通过多版本并发控制(MVCC)和next-key锁解决幻读问题,确保事务隔离性和一致性。

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

MySQL 是如何解决幻读的

一、什么是幻读

在一次事务里面,多次查询之后,结果集的个数不一致的情况叫做幻读。

而多出来或者少的哪一行被叫做 幻行

二、为什么要解决幻读

在高并发数据库系统中,需要保证事务与事务之间的隔离性,还有事务本身的一致性。

三、MySQL 是如何解决幻读的

如果你看到了这篇文章,那么我会默认你了解了 脏读不可重复读可重复读

1. 多版本并发控制(MVCC)(快照读/一致性读)

多数数据库都实现了多版本并发控制,并且都是靠保存数据快照来实现的。

InnoDB 为例,每一行中都冗余了两个字断。一个是行的创建版本,一个是行的删除(过期)版本。

具体的版本号(trx_id)存在 information_schema.INNODB_TRX 表中。

版本号(trx_id)随着每次事务的开启自增。

事务每次取数据的时候都会取创建版本小于当前事务版本的数据,以及过期版本大于当前版本的数据。

普通的 select 就是快照读。

select * from T where number = 1;

原理:将历史数据存一份快照,所以其他事务增加与删除数据,对于当前事务来说是不可见的。

2. next-key 锁 (当前读)

next-key 锁包含两部分

  • 记录锁(行锁)
  • 间隙锁

记录锁是加在索引上的锁,间隙锁是加在索引之间的。(思考:如果列上没有索引会发生什么?)

select * from T where number = 1 for update;
select * from T where number = 1 lock in share mode;
insert
update
delete

原理:将当前数据行与上一条数据和下一条数据之间的间隙锁定,保证此范围内读取的数据是一致的。

其他:MySQL InnoDB 引擎 RR 隔离级别是否解决了幻读

引用一个 github 上面的评论 地址

Mysql官方给出的幻读解释是:只要在一个事务中,第二次select多出了row就算幻读。
a事务先select,b事务insert确实会加一个gap锁,但是如果b事务commit,这个gap锁就会释放(释放后a事务可以随意dml操作),a事务再select出来的结果在MVCC下还和第一次select一样,接着a事务不加条件地update,这个update会作用在所有行上(包括b事务新加的),a事务再次select就会出现b事务中的新行,并且这个新行已经被update修改了,实测在RR级别下确实如此。

如果这样理解的话,Mysql的RR级别确实防不住幻读

有道友回复 地址

在快照读读情况下,mysql通过mvcc来避免幻读。
在当前读读情况下,mysql通过next-key来避免幻读。
select * from t where a=1;属于快照读
select * from t where a=1 lock in share mode;属于当前读

不能把快照读和当前读得到的结果不一样这种情况认为是幻读,这是两种不同的使用。所以我认为mysql的rr级别是解决了幻读的。

先说结论,MySQL 存储引擎 InnoDB 隔离级别 RR 解决了幻读问题。

如引用一问题所说,T1 select 之后 update,会将 T2 中 insert 的数据一起更新,那么认为多出来一行,所以防不住幻读。看着说法无懈可击,但是其实是错误的,InnoDB 中设置了 快照读 和 当前读 两种模式,如果只有快照读,那么自然没有幻读问题,但是如果将语句提升到当前读,那么 T1 在 select 的时候需要用如下语法: select * from t for update (lock in share mode) 进入当前读,那么自然没有 T2 可以插入数据这一回事儿了。

注意

  1. next-key 固然很好的解决了幻读问题,但是还是遵循一般的定律,隔离级别越高,并发越低。

转载于:https://www.cnblogs.com/wdy1184/p/10655180.html

### MySQL解决问题的方法及实现机制 #### 的定义与产生原因 是指在一个事务中多次查询同一条件下的数据集合时,由于其他事务的操作导致结果集不一致的现象。这种现象通常发生在插入新记录的情况下[^4]。 #### 解决的主要方法 ##### 方法一:提高隔离级别至串行化 当事务隔离级别被设置为 **Serializable** 时,MySQL 使用表级锁来防止任何并发写入操作,从而彻底杜绝的可能性。然而,这种方式会显著降低系统的并发性能[^2]。 ##### 方法二:利用 MVCC 和 Next-Key Lock 实现解决方案 在 InnoDB 存储引擎中,默认的事务隔离级别是 **Repeatable Read (RR)**。虽然 RR 能够通过多版本并发控制(MVCC)解决和不可重复的问题,但它无法完全避免。为了进一步处理这一问题,InnoDB 引入了 **Next-Key Lock** 技术[^3]。 - **MVCC 工作原理** MVCC 是一种通过保存数据的历史版本来支持高并发的技术。对于 SELECT 查询而言,InnoDB 只会返回那些符合当前事务可见性的历史版本数据,而不会看到未提交或者由其他事务更新后的数据版本[^1]。 - **Next-Key Lock 的作用** Next-Key Lock 是 Record Lock 和 Gap Lock 的组合形式,在执行某些特定类型的查询(如 `SELECT ... FOR UPDATE` 或者索引扫描中的 DML 操作)时会被自动应用。它不仅锁定具体的行记录本身,还会封锁这些行之间以及之前可能存在的间隙区域,阻止其他事务在此范围内插入新的记录,以此达到预防的效果。 - **当前的概念** 当前指的是取到的是最新的实际物理存储上的数据副本,并且会对所访问的数据施加相应的排他锁(X 锁)。这意味着如果某个事务进行了当前,则在同一时刻其他试图修改相同资源的事务将会受到阻碍直到前者完成其工作为止。 #### 总结 综上所述,MySQL 主要依靠提升隔离等级至 Serializable 来绝对消除风险;而在更常见的 Repeatable Read 下则是借助于 MVCC 结合 Next-Key Locks 方案共同协作达成目标——即一方面维持旧有视图供一致性快照阅之需,另一方面则通过对涉及区间实施严格管控以防范新增干扰项出现引发逻辑错误情形发生。 ```sql -- 示例代码展示如何使用 SELECT...FOR UPDATE 进行当前 START TRANSACTION; SELECT * FROM table_name WHERE id = ? FOR UPDATE; -- 加锁并获取最新版本数据 COMMIT; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值