MVCC,全称Multi-Version Concurrency Control,即多版本并发控制,为多个版本的数据实现并发控制的技术。其基本思想是为每一次事务生成一个新版本的数据,在读取数据时可以选择不同版本的数据即实现的事务结果的完整性读取。(基于Mysql的回滚机制为达到并发场景下的读操作不需要锁定)
实现原理:主要是依赖记录中的三个隐式字段、undo日志、ReadView
三个隐式字段:
DB_TRX_ID
6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
DB_ROLL_PTR
7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
DB_ROW_ID
6byte,隐含的自增ID(隐藏主键),如果数据表没有主键并且没有唯一键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
下面看一下MVCC的整个的运作的流程
一、 比如一个有个事务插入persion表插入了一条新记录,记录如下,name为Jerry, age为24岁,隐式主键是1,事务ID和回滚指针,我们假设为NULL
二、 现在来了一个事务1对该记录的name做出了修改,改为Tom
- 在事务1修改该行(记录)数据时,数据库会先对该行加排他锁
- 然后把该行数据拷贝到undo log中,作为旧记录,既在undo log中有当前行的拷贝副本
- 拷贝完毕后,修改该行name为Tom,并且修改隐藏字段的事务ID为当前事务1的ID, 我们默认从1开始,之后递增,回滚指针指向拷贝到undo log的副本记录,既表示我的上一个版本就是它
-
事务提交后,释放锁
image.png
三、 又来了个事务2修改person表的同一个记录,将age修改为30岁
- 在事务2修改该行数据时,数据库也先为该行加锁
- 然后把该行数据拷贝到undo log中,作为旧记录,发现该行记录已经有undo log了,那么最新的旧数据作为链表的表头,插在该行记录的undo log最前面
- 修改该行age为30岁,并且修改隐藏字段的事务ID为当前事务2的ID, 那就是2,回滚指针指向刚刚拷贝到undo log的副本记录
- 事务提交,释放锁
从上面,我们就可以看出,不同事务或者相同事务的对同一记录的修改,会导致该记录的undo log成为一条记录版本线性表,既链表,undo log的链首就是最新的旧记录,链尾就是最早的旧记录。
查询时会读出ReadView;[未提交的事务id]数组 + 最大事务id,并根据ReadView从undo log日志中最新记录依次往下找
- 如果当前记录:当前事务id < 未提交事务的最小id,则可读
- 如果当前记录:未提交事务的最小id <= 当前事务id <= 事务的最大id,则判断事务id是否存在ReadView数组中,若存在则不可读。
如果当前记录:当前事务id > 事务最大id,则不可读
本篇文章参考 https://www.jianshu.com/p/8845ddca3b23
B站还有个程序猿up讲的这个也很不错 https://www.bilibili.com/video/BV1Vk4y1k7KQ?from=search&seid=12530364264673342397