- MVCC
- 当前读与快照读
- 三个隐式字段
- Undo log与undo log版本链
- undo log
- 版本链
- Readview
MVCC 称 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,快照读为MySQL实现MVCC提供了一个非阻塞读功能。MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undolog日志、readView。
当前读与快照读当前读读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
快照读:简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
- Read Committed:每次select,都生成一个快照读。
- Repeatable Read:开启事务后第一个select语句才是快照读的地方。
- Serializable:快照读会退化为当前读。
当我们创建一张表的时候,表中除了会又创建时添加的字段外,InnoDB还会自动的给我们添加三个隐藏字段及其含义分别是:
| 隐藏字段 | 含义 |
|---|---|
| DB_TRX_ID | 最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。 |
| DB_ROLL_PTR | 回滚指针,指向这条记录的上一个版本,用于配合undolog,指向上一个版本。 |
| DB_ROW_ID | 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。 |
上述的前两个字段是肯定会添加的,是否添加最后一个字段DB_ROW_ID,得看当前表有没有主键,如果有主键,则不会添加该隐藏字段。
Undo log与undo log版本链 undo log回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。而update、delete的时候,产生的undo log日志不仅在回滚时需要,在快照读时也需要,不会立即被删除。
版本链有一张原始的数据表为
| id | age | name | DB_TRX_ID | DB_ROLL_PTR |
|---|---|---|---|---|
| 2 | 30 | A30 | 1 | null |
其中:
DB_TRX_ID:代表最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID,是
自增的。
DB_ROLL_PTR:由于这条数据是才插入的,没有被更新过,所以该字段值为null。
有四个并发事务同时访问这张表:
当执行完事务1,2,3中的对同一个数据的更改操作后,undo log会形成undo log版本链,记录数据变更之前的样子;然后更新记录,并且记录本次操作的事务ID,回滚指针,回滚指针用来指定如果发生回滚,回滚到哪一个版本。
不同事务或相同事务对同一条记录进行修改,会导致该记录的undo log生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
ReadviewReadView(读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
ReadView中包含了四个核心字段:
| 字段 | 含义 |
|---|---|
| m_ids | 当前活跃的事务ID集合 |
| min_trx_id | 最小活跃事务ID |
| max_trx_id | 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的) |
| creator_trx_id | ReadView创建者的事务ID |
而在readview中就规定了版本链数据的访问规则:
trx_id代表当前undolog版本链对应事务ID。
| 条件 | 是否可以访问 | 说明 |
|---|---|---|
| trx_id ==creator_trx_id | 可以访问该版本 | 成立,说明数据是当前这个事务更改的。 |
| trx_id < min_trx_id | 可以访问该版本 | 成立,说明数据已经提交了。 |
| trx_id > max_trx_id | 不可以访问该版本 | 成立,说明该事务是在ReadView生成后才开启。 |
| min_trx_id <= trx_id<= max_trx_id | 如果trx_id不在m_ids中,是可以访问该版本的 | 成立,说明数据已经提交。 |
不同的隔离级别,生成ReadView的时机不同:
- READ COMMITTED:在事务中每一次执行快照读时生成ReadView。
- REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
在READ COMMITTED中,每执行一次select语句就会执行快照读生成ReadView,然后数据库就会根据所生成的ReadView以及ReadView的版本链访问规则,到undo log版本链中匹配数据,最终决定此次快照读返回的数据。
RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。而RR是可重复读,在一个事务中,执行两次相同的select语句,查询到的结果是一样的。
MVCC与锁机制保证了数据库的隔离性。数据事务的其他性质及保证可以参考:
Mysql事务详解-[数据库的隔离级别、脏读、不可重复读、幻读以及ACID性质与redo log与undo log]



