MySQL MVCC多版本并发控制
MVCC(Multi-Version Concurrency Control)通过保存数据多个版本,实现读写不阻塞。
MVCC 核心概念
| 组件 | 说明 |
|---|---|
| Undo Log | 存储数据旧版本,形成版本链 |
| Read View | 读视图,决定能看到哪个版本 |
| 事务ID | 每个事务有唯一递增ID |
Undo Log 版本链
SQL
-- 每条记录包含隐藏字段
-- DB_TRX_ID: 最后修改的事务ID
-- DB_ROLL_PTR: 回滚指针,指向Undo Log中的旧版本
-- DB_ROW_ID: 行ID(无主键时生成)
-- 示例:数据修改过程
INSERT INTO users VALUES (1, '张三'); -- trx_id = 100
UPDATE users SET name = '李四' WHERE id = 1; -- trx_id = 101
UPDATE users SET name = '王五' WHERE id = 1; -- trx_id = 102
-- 版本链结构(从新到旧)
-- 当前数据: name='王五', trx_id=102, roll_ptr→undo1
-- undo1: name='李四', trx_id=101, roll_ptr→undo2
-- undo2: name='张三', trx_id=100, roll_ptr→NULL
Read View 结构
SQL
Read View 包含:
- m_ids: 创建 Read View 时活跃事务ID列表
- min_trx_id: m_ids 中最小值
- max_trx_id: 下一个待分配事务ID
- creator_trx_id: 创建该 Read View 的事务ID
版本可见性判断
SQL
数据版本 trx_id:
1. trx_id < min_trx_id → 可见(已提交)
2. trx_id >= max_trx_id → 不可见(之后新事务)
3. trx_id 在 m_ids 中 → 不可见(活跃未提交)
4. trx_id 不在 m_ids 中且 trx_id < max_trx_id → 可见(已提交)
5. trx_id == creator_trx_id → 可见(自己修改)
RC 与 RR 的 Read View 差异
SQL
-- READ COMMITTED: 每次 SELECT 创建新 Read View
-- 事务A 事务B
START TRANSACTION; START TRANSACTION;
SELECT * FROM users; UPDATE users SET name='x';
-- Read View 1 COMMIT;
SELECT * FROM users;
-- Read View 2,能看到事务B的修改
-- REPEATABLE READ: 第一次 SELECT 创建 Read View,后续复用
-- 事务A 事务B
START TRANSACTION; START TRANSACTION;
SELECT * FROM users; UPDATE users SET name='x';
-- Read View 1 COMMIT;
SELECT * FROM users;
-- 仍用 Read View 1,看不到事务B的修改
COMMIT;
SELECT * FROM users; -- 新 Read View,能看到修改
MVCC 工作流程
text
-- 查询流程
SELECT name FROM users WHERE id = 1;
-- 1. 获取当前事务的 Read View
-- 2. 获取最新版本数据
-- 3. 判断 trx_id 是否可见
-- 4. 不可见则通过 roll_ptr 找上一版本
-- 5. 重复判断直到找到可见版本或到链尾
MVCC 与锁的关系
| 操作 | MVCC | 锁 |
|---|---|---|
| 普通读 | 使用 MVCC | 无锁 |
| 锁定读 | 不使用 | 加行锁 |
| 写操作 | 不使用 | 加行锁 |
text
-- 普通读(快照读):使用 MVCC
SELECT * FROM users WHERE id = 1;
-- 锁定读(当前读):不使用 MVCC,加锁
SELECT * FROM users WHERE id = 1 FOR UPDATE;
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
MVCC 解决的问题
- 读写不阻塞:读操作不加锁,写操作不阻塞读
- 解决脏读:只看到已提交版本
- 解决不可重复读:RR 级级 Read View 复用,版本不变
MVCC 不解决幻读,但 InnoDB 通过间隙锁+临键锁在 RR 级别解决幻读。
要点总结
- MVCC 通过 Undo Log 版本链实现多版本数据
- Read View 决定事务能看到哪个版本
- RC 每次 SELECT 新建 Read View
- RR 复用首次 SELECT 的 Read View
- 快照读用 MVCC,当前读加锁
- MVCC 解决脏读和不可重复读,不解决幻读
📝 发现内容有误?点击此处直接编辑