1. MVCC 解决了什么问题?
MVCC(多版本并发控制)是 MySQL InnoDB 存储引擎实现并发访问的核心机制,主要解决了读写冲突问题:
- 在传统锁机制中,读操作需要加共享锁,写操作需要加排他锁,会导致 “读阻塞写、写阻塞读”,严重影响并发性能。
- MVCC 通过维护数据的多个版本,允许读操作(非锁定读)无需加锁即可访问历史版本数据,同时写操作修改当前版本,实现了 “读不阻塞写,写不阻塞读”,大幅提升了数据库的并发处理能力。
2. MySQL 幻读解决了吗?
在 InnoDB 的可重复读(RR)隔离级别下,幻读被有效解决;在读已提交(RC)隔离级别下,幻读仍可能存在。
幻读的定义:同一事务内,连续两次执行相同范围的查询(如
SELECT * FROM t WHERE id > 10
),第二次查询可能返回第一次未出现的新数据(因其他事务插入了满足条件的记录)。InnoDB 在 RR 级别通过间隙锁(Gap Lock)和临键锁(Next-Key Lock) 防止其他事务插入新数据,从而避免幻读。
3. MVCC 的原理是什么?
MVCC 通过以下核心组件实现多版本控制:
-
隐藏列:InnoDB 为每个表添加 3 个隐藏列:
DB_TRX_ID
:记录最后一次修改该记录的事务 ID;DB_ROLL_PTR
:回滚指针,指向该记录的历史版本(存储在 undo log 中);DB_ROW_ID
:若表无主键,InnoDB 会自动生成该列作为行唯一标识。
-
undo log:存储数据的历史版本,用于事务回滚和 MVCC 的读操作。当修改记录时,旧版本数据会被写入 undo log,通过
DB_ROLL_PTR
串联形成版本链。 -
Read View(读视图):每个事务在启动时会生成一个 Read View,用于判断数据版本的可见性。Read View 包含当前活跃事务的 ID 列表,规则如下:
- 若记录的
DB_TRX_ID
小于 Read View 中的最小活跃事务 ID,说明该版本在事务启动前已提交,可见; - 若记录的
DB_TRX_ID
大于 Read View 中的最大活跃事务 ID,说明该版本在事务启动后生成,不可见; - 若
DB_TRX_ID
在活跃事务 ID 范围内,需判断是否为当前事务自身的修改:是则可见,否则不可见。
- 若记录的
通过以上机制,事务可以访问符合条件的历史版本,实现非阻塞读。
4. 间隙锁(Gap Lock)和临键锁(Next-Key Lock)解决了什么问题?怎么解决的?原理是什么?
解决的核心问题:
两者均用于防止幻读,本质是通过锁定 “数据间隙” 阻止其他事务插入新记录,确保同一事务内多次查询的范围一致性。
(1)间隙锁(Gap Lock)
- 定义:锁定索引记录之间的 “间隙”(不包含记录本身),防止其他事务在间隙中插入新数据。
- 原理:
t
的id
索引值为 10、20、30,当执行SELECT * FROM t WHERE id BETWEEN 10 AND 30 FOR UPDATE
时,InnoDB 会锁定以下间隙:- (负无穷,10)
- (10, 20)
- (20, 30)
- (30, 正无穷)
id=15
、id=25
等新记录,从而避免 “新增数据导致幻读”。
(2)临键锁(Next-Key Lock)
-
定义:InnoDB 默认的行级锁机制,是 “行锁(Record Lock)+ 间隙锁(Gap Lock)” 的组合,锁定索引记录本身及其前面的间隙。
-
原理:仍以
id
索引 10、20、30 为例,临键锁的锁定范围是左开右闭区间:- (-∞, 10]
- (10, 20]
- (20, 30]
- (30, +∞)
id=20
时,临键锁会同时锁定记录id=20
(行锁)和间隙 (10,20)(间隙锁),既防止修改id=20
的记录,也防止在 (10,20) 中插入新数据。
-
作用:临键锁是 InnoDB 在 RR 级别下默认使用的锁,通过同时锁定记录和间隙,彻底阻止 “修改已有记录” 和 “插入新记录” 两种可能导致幻读的操作。
总结
- MVCC 解决读写冲突,通过多版本实现非阻塞读;
- InnoDB 在 RR 级别通过间隙锁和临键锁解决幻读;
- 间隙锁锁定数据间隙,临键锁锁定记录 + 间隙,两者共同防止其他事务插入新数据,保障范围查询的一致性。