InnoDB 在可重复读(RR) 隔离级别下,通过两大核心机制确保数据一致性:一致性读视图(Read View) 和 Next-Key Lock 锁机制。前者解决 "不可重复读" 问题,后者解决 "幻读" 问题,两者协同保障事务内的数据视图稳定和并发操作的安全性。
一、一致性读视图(Read View):解决 "不可重复读"
"不可重复读" 指事务内多次读取同一行数据时,因其他事务修改并提交,导致前后读取结果不一致。InnoDB 通过Read View(读视图) 机制避免这一问题,核心是为每个事务创建一个 "数据快照",事务内的所有读取操作都基于这个快照,不受其他已提交事务的影响。
Read View 的工作原理:
-
生成时机 :事务启动时(执行第一个
SELECT语句时),InnoDB 会生成一个 Read View,记录当前数据库中 "活跃事务" 的 ID 列表(即未提交的事务 ID)。- 每个事务启动时会分配一个唯一的递增事务 ID(
trx_id)。 - Read View 包含三个关键信息:
m_low_limit_id(当前最大事务 ID+1)、m_up_limit_id(活跃事务中最小的 ID)、m_ids(活跃事务 ID 列表)。
- 每个事务启动时会分配一个唯一的递增事务 ID(
-
可见性判断规则 :当事务读取某行数据时,InnoDB 会对比该行的
trx_id(最后修改该行的事务 ID)与 Read View 中的信息,决定数据是否可见:- 若
trx_id < m_up_limit_id:说明修改该行的事务已提交,数据可见。 - 若
trx_id >= m_low_limit_id:说明修改该行的事务在当前事务启动后才开始,数据不可见。 - 若
m_up_limit_id <= trx_id < m_low_limit_id:需检查trx_id是否在m_ids中。若在,说明事务未提交,数据不可见;若不在,说明事务已提交,数据可见。
- 若
-
快照的 "稳定性" :RR 级别下,Read View 一旦生成,在整个事务期间不会更新。因此,无论其他事务如何修改并提交数据,当前事务始终只能看到启动时的快照,确保 "可重复读"。
举例说明:
- 事务 A(
trx_id=100)启动,生成 Read View(此时活跃事务只有 A,m_up_limit_id=100,m_low_limit_id=101)。 - 事务 A 第一次读取
stock=10(该行trx_id=50,早于 A 启动,可见)。 - 事务 B(
trx_id=200)启动,修改stock=9并提交(trx_id=200)。 - 事务 A 再次读取
stock:由于200 >= m_low_limit_id=101,事务 B 的修改对 A 不可见,A 仍读到stock=10。
关于
二、Next-Key Lock:解决 "幻读"
"幻读" 指事务内两次查询同一范围的数据时,因其他事务插入新行并提交,导致第二次查询多出 "新行"。InnoDB 通过Next-Key Lock(行锁 + 间隙锁)机制防止幻读,核心是锁定 "记录本身" 和 "可能插入新记录的间隙"。
Next-Key Lock 的组成:
-
行锁(Record Lock) :锁定表中具体某一行记录,防止其他事务修改或删除该行。
-
间隙锁(Gap Lock) :锁定不存在记录的区间(间隙),防止其他事务在该区间插入新行。
Next-Key Lock = 行锁 + 间隙锁,它的锁定范围是 "左开右闭" 的区间。例如,对id=10的行加 Next-Key Lock,会锁定(上一个存在的id, 10]的区间(如(5,10],若上一个 id 是 5)。
如何防止幻读:
当事务执行带范围条件的写操作 (如UPDATE ... WHERE、DELETE ... WHERE)或悲观读 (SELECT ... FOR UPDATE)时,InnoDB 会自动加 Next-Key Lock,既锁定符合条件的行,也锁定这些行前后的间隙,阻止新行插入。
举例说明:
-
表
products中存在id=5,10,15的行,库存字段stock>0。 -
事务 A 执行:
SELECT * FROM products WHERE id BETWEEN 5 AND 15 FOR UPDATE(悲观读,锁定范围)。- InnoDB 会对
id=5,10,15加行锁,同时对间隙(负无穷,5]、(5,10]、(10,15]、(15,正无穷)加间隙锁。
- InnoDB 会对
-
事务 B 尝试插入
id=7(属于(5,10]间隙):会被间隙锁阻塞,直到事务 A 提交释放锁。 -
因此,事务 A 再次执行相同查询时,不会出现 "新插入的 id=7" 这行,避免幻读。
三、锁的持有与释放:确保事务原子性
在 RR 级别下,InnoDB 的行锁和间隙锁会持有至事务提交或回滚后才释放,而非操作完成后立即释放(RC 级别会提前释放)。这种机制保证了事务内的一系列操作(如 "查库存→扣库存→创建订单")是一个原子性的整体,中间不会被其他事务的修改打断,进一步强化了数据一致性。
总结
InnoDB 在 RR 级别下通过两大机制确保一致性:
- Read View:为事务创建启动时的数据快照,确保事务内多次读取结果一致,解决 "不可重复读"。
- Next-Key Lock:结合行锁和间隙锁,锁定记录及间隙,防止其他事务插入新行,解决 "幻读"。
这两种机制协同工作,既保证了事务隔离性,又在并发性能与数据一致性之间取得了平衡,使 RR 成为 InnoDB 的默认隔离级别,适配多数业务场景(如电商交易、金融支付)对数据准确性的高要求。