MVCC(多版本并发控制)快照的实现原理
MVCC(Multi-Version Concurrency Control)是现代数据库实现事务隔离级别的核心技术,它通过数据多版本和快照机制来实现高效的并发控制。下面我将详细解析MVCC快照的实现机制。
一、MVCC核心组件
1. 版本链结构
MVCC通过以下隐藏字段维护数据版本:
- DB_TRX_ID(6字节):记录创建或最后一次修改该行的事务ID
- DB_ROLL_PTR(7字节):回滚指针,指向undo log记录
- DB_ROW_ID(6字节):隐含自增ID(无主键时生成)
- DELETE BIT(1位):标记该行是否被删除
2. undo log(回滚日志)
- 存储数据修改前的状态
- 组成版本链的关键结构
- 类型:
- INSERT undo log:事务回滚时需要删除
- UPDATE undo log:事务回滚时需要恢复旧值
3. ReadView(读视图)
决定事务能看到哪些版本的数据,包含:
m_ids
:生成ReadView时活跃的事务ID列表min_trx_id
:活跃事务中的最小IDmax_trx_id
:系统将分配的下一个事务IDcreator_trx_id
:创建该ReadView的事务ID
二、MVCC快照的生成过程
1. 事务启动时
- 分配唯一事务ID(单调递增)
- 根据隔离级别决定是否立即创建ReadView:
- 读已提交:每条语句执行前创建新ReadView
- 可重复读:事务第一次读操作时创建ReadView
2. 数据读取时的可见性判断
python
def is_visible(trx_id, read_view):
if trx_id < read_view.min_trx_id:
return True # 已提交的旧事务
elif trx_id == read_view.creator_trx_id:
return True # 自身修改可见
elif trx_id in read_view.m_ids:
return False # 活跃事务不可见
else:
return True # 已提交的新事务
3. 版本链遍历过程
- 从最新数据行开始检查
- 通过DB_ROLL_PTR找到undo log中的旧版本
- 对每个版本应用可见性判断
- 返回第一个可见的版本
三、不同隔离级别的实现差异
1. 读已提交(RC)
- 每次执行SELECT都新建ReadView
- 能看到其他事务已提交的修改
- 可能产生不可重复读现象
2. 可重复读(RR)
- 事务第一次读操作时创建ReadView
- 整个事务期间复用同一个ReadView
- 保证同一事务内读取一致性
- MySQL通过间隙锁避免幻读
四、MVCC的写入流程
1. 更新操作
- 对原数据行打删除标记
- 插入新行并更新DB_TRX_ID
- 原行DB_ROLL_PTR指向undo log
2. 删除操作
- 设置行的DELETE BIT为1
- 记录undo log
3. 插入操作
- 分配新行并设置DB_TRX_ID为当前事务ID
五、MVCC的清理机制
1. purge线程
- 定期清理不再需要的undo log
- 清理条件:
- 对应事务已提交
- 没有活跃事务需要访问该版本
2. 版本链修剪
- 当某版本对所有活跃事务都不可见时
- 可被安全回收
六、各数据库实现对比
特性 | MySQL(InnoDB) | PostgreSQL | Oracle |
---|---|---|---|
版本存储 | 主表+undo log | 主表+TOAST(大对象存储) | 回滚段 |
快照生成时机 | 语句/事务级别 | 事务级别 | SCN(System Change Number) |
清理机制 | purge线程 | VACUUM进程 | SMON后台进程 |
七、MVCC的优势与局限
优势:
- 读不阻塞写,写不阻塞读
- 避免大部分锁争用
- 实现非锁定一致性读
局限:
- 需要维护多版本数据,增加存储开销
- 长期事务可能导致版本堆积
- 需要复杂的垃圾回收机制
MVCC快照的实现是现代数据库高并发能力的基石,理解其原理有助于优化事务设计和排查并发问题。