MVCC可重复读
一、MVCC 实现可重复读的关键机制
- 隐藏字段与 Undo 日志
- 每个 InnoDB 行记录包含两个隐藏字段:
- DB_TRX_ID:记录最后一次修改该行的事务 ID。
- DB_ROLL_PTR:指向 Undo 日志中旧版本数据的指针。
- Undo 日志:保存数据的历史版本,用于构建事务可见的旧数据快照。
- Read View(读视图)
- 事务首次执行 SELECT 时会生成一个 Read View,包含以下信息:
- 活跃事务列表(trx_ids):当前未提交的事务 ID 集合。
- 最小活跃事务 ID(min_trx_id):活跃事务列表中的最小 ID。
- 最大事务 ID(max_trx_id):下一个即将分配的事务 ID。
- 创建者事务 ID(creator_trx_id):当前事务自身的 ID。
- 通过 Read View 判断数据版本的可见性规则:
- 如果行记录的 DB_TRX_ID < min_trx_id:说明该版本在事务开始前已提交,可见。
- 如果 DB_TRX_ID >= max_trx_id:说明该版本在事务开始后生成,不可见。
- 如果 min_trx_id ≤ DB_TRX_ID < max_trx_id:
- DB_TRX_ID 在 trx_ids 列表中:说明该版本由未提交事务生成,不可见。
- DB_TRX_ID 不在 trx_ids 列表中:说明该版本已提交,可见。
- 事务首次执行 SELECT 时会生成一个 Read View,包含以下信息:
- 快照读(Snapshot Read) 在可重复读级别下,事务的 SELECT 操作基于第一次生成的 Read View,后续读取始终使用同一快照,确保多次读取的数据版本一致。 —
二、数据读写顺序的处理
-
读操作(SELECT)
- 流程:
- 访问数据页中的最新行记录。
- 检查 DB_TRX_ID 是否符合 Read View 的可见性规则。
- 若不可见,通过 DB_ROLL_PTR 找到 Undo 日志中的旧版本,逐级回溯直到找到可见版本。
- 示例
-- 事务 A 第一次读取(生成 Read View) SELECT * FROM table WHERE id = 1; -- 事务 B 修改并提交数据 UPDATE table SET value = 2 WHERE id = 1; -- 事务 A 再次读取(仍看到旧值) SELECT * FROM table WHERE id = 1; -- 结果与第一次一致
- 流程:
- 写操作(UPDATE/INSERT/DELETE)
- 流程:
- 当前读(Current Read):写操作直接读取最新提交的数据版本(而非快照),并加锁(如行锁、间隙锁)。
- 修改数据时生成新版本,更新 DB_TRX_ID 和 DB_ROLL_PTR,旧版本存入 Undo 日志。
-
示例: ```sql
– 事务 A 更新数据(触发当前读) UPDATE table SET value = 3 WHERE id = 1; – 会读取事务 B 提交的最新值 2,修改为 3
```
- 流程:
- 事务提交与清理
- 提交时,InnoDB 将修改持久化到磁盘。
- Undo 日志中的旧版本数据在无活跃事务依赖时,由后台 Purge 线程清理。
三、可重复读如何避免幻读?
- 快照读:通过 Read View 保证事务内读取的数据版本固定,避免看到其他事务新增/删除的数据(幻读)。
- 当前读 + 间隙锁:
- 执行 UPDATE、DELETE 或 SELECT … FOR UPDATE 时,InnoDB 对扫描的范围加间隙锁,阻止其他事务插入新数据,从而避免幻读。
四、与读已提交(READ COMMITTED)的区别
一、隔离级别总览
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 锁机制 | 实现原理 |
|————————-|——|————|——|———————————————————————–|————————————————————————–|
| READ UNCOMMITTED | ✔️ | ✔️ | ✔️ | 无锁控制(仅行锁) | 直接读取最新数据版本 |
| READ COMMITTED | ✖️ | ✔️ | ✔️ | 行锁 + 短间隙锁 | MVCC + 每次 SELECT 生成新快照 |
| REPEATABLE READ | ✖️ | ✖️ | △️ | 行锁 + 间隙锁(Gap Lock) + 临键锁(Next-Key Lock) | MVCC + 事务首次 SELECT 生成快照 + 间隙锁防插入 |
| SERIALIZABLE | ✖️ | ✖️ | ✖️ | 范围锁(所有操作串行化) | 所有 SELECT 隐式转换为 SELECT ... FOR UPDATE
|
△️ 表示:快照读可避免幻读,当前读可能触发(需显式加锁)
1. 脏读(Dirty Read)
总结
- MVCC 核心:通过 Undo 日志保存多版本数据,结合 Read View 实现事务隔离。
- 可重复读:事务内使用固定快照,确保多次读取一致性;写操作通过当前读和锁机制保证数据正确性。
- 适用场景:需要高度数据一致性的业务(如财务系统),但对并发性能有一定影响。
喜欢文章请关注我
程序领域
点击关注+转发,私信发送【面试】或者【资料】可以收获更多资源