MySQL事务原理深度剖析:MVCC与Read View机制详解
MySQL事务原理深度剖析:MVCC与Read View机制详解
MySQL的事务隔离机制是数据库系统中的核心概念之一,其中MVCC(多版本并发控制)和read view机制在实现高并发读写操作中扮演着重要角色。本文将深入探讨MVCC的工作原理,以及在不同隔离级别(Read Committed和Repeatable Read)下read view的具体表现,帮助读者理解这些复杂的数据库概念。
事务隔离级别的回顾
在深入讲解MVCC和read view之前,我们先回顾一下事务隔离性的四种级别:
- 脏读(Dirty Read):一个事务读到另一个事务未提交的数据。
- 不可重复读(Non-repeatable Read):一个事务两次读取同一数据,但两次结果不同,因为有其他事务修改并提交了该数据。
- 幻读(Phantom Read):一个事务两次读取同一范围的数据,但两次结果不同,因为有其他事务插入了新的数据。
- 隔离级别:
- 读未提交(Read Uncommitted):允许脏读、不可重复读和幻读。
- 读已提交(Read Committed):避免脏读,但可能有不可重复读和幻读。
- 可重复读(Repeatable Read):避免脏读和不可重复读,某些数据库可能有幻读问题,但MySQL解决了幻读问题。
- 串行化(Serializable):完全避免所有并发问题,但性能最低。
一致性
事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库只包含事务成功提交的结果时,数据库处于一种一致性状态。如果系统运行发生中断,某个事务尚未完成而被迫中断,而改变未完成的事务对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确的状态。因此一致性是通过原子性保证的。
三种并发方式的安全隐患
- 读读并发:不存在任何问题,不需要并发控制。
- 读写并发:可能造成事务隔离性问题,如脏读、幻读、不可重复读。
- 写写并发:可能会存在更新丢失问题,如第一类更新丢失、第二类更新丢失。
三个隐藏字段
在InnoDB存储引擎中,每条记录都会自动添加三个隐藏字段:
- DB_TRX_ID:6字节,记录最后修改这条数据的事务ID。
- DB_ROLL_PTR:指向undo日志的位置,用于回滚操作。
- DB_ROW_ID:隐藏的自增ID,如果没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引。
Undo日志
Undo日志是MySQL中的一段内存缓冲区,用于记录事务的回滚操作。当需要修改数据时,MySQL会先将数据保存一份到undo日志中,然后再进行修改。这样即使事务回滚,也可以从undo日志中恢复数据。
例如,假设有一条数据:
当事务10想要修改这条数据时,会先将数据加锁,然后复制一份到undo日志中,再进行修改:
事务10提交后,释放锁。如果事务11也想要修改这条数据,会重复相同的过程:
这样就形成了一个版本链,如果需要回滚,可以从undo日志中恢复历史版本。
MVCC多版本控制
MVCC(Multi-Version Concurrency Control)是一种无锁的并发控制机制,通过维护数据的多个版本来实现并发控制。在MVCC中,每个事务都有自己的事务ID,通过事务ID来判断事务的先后顺序。
- Undo log:管理事务内形成的版本链条。一个事务多次修改会形成版本链,commit时undo log会被清空。
- Insert:虽然没有版本链,但insert对应的数据也要放到undo log里面,commit后版本链可以被清空。
- Update和Delete:可能有其他事务在访问,不一定能立即清空。
Read View
Read View是事务进行快照读操作时生成的读视图,记录系统当前活跃事务的ID。Read View是一个类,与事务的关系类似于进程地址空间和PCB。
class Readview
{
private:
trx_id_t m_low_limit_id; // 高水位,大于等于这个ID的事务均不可见。
trx_id_t m_up_limit_id; // 低水位,小于这个ID的事务均可见。
trx_id_t m_creator_trx_id; // 创建该Read view的事务ID。
ids_t m_ids; // 创建视图时的活跃事务id列表。
trx_id_t m_low_limit_no;
bool m_closed; // 视图是否关闭
//....省略
};
Read View的判断规则:
- 如果版本链中的事务ID与m_ids中的事务ID相同,说明事务还在活跃状态,不能看到这个版本。
- 如果版本链中的事务ID小于m_up_limit_id,说明事务启动得更早,可以看见。
- 如果版本链中的事务ID大于m_low_limit_id,说明事务启动得更晚,不能看见。
RC与RR的本质区别
- RC(Read Committed):每次快照读都生成新的Read View,low_limit_id总是当前系统最大的事务ID,只要其他事务提交,就能看到其修改。
- RR(Repeatable Read):只在第一次快照读时生成Read View,之后的修改都基于这个Read View判断可见性,因此在事务持续期间,读到的数据不会发生变化。
通过实验可以验证:
- 在RR隔离级别下,如果一个事务在另一个事务commit后才进行快照读,那么它可以看到前一个事务的修改。
- 在RC隔离级别下,每次快照读都会生成新的Read View,因此能立即看到其他事务的提交结果。