1.前言
Mysql的MVCC机制是Mysql中的重要的一环,其中也是经常在Mysql DBA面试中被问到,并且也是Innodb存储引擎(带有MVCC机制)和Myism存储引擎的一个很大的区别。
2. 什么是MVCC技术?
MVCC(Multi-Version Concurrency Control)即多版本并发控制,在高性能Mysql一书中介绍,MVCC的大多数事务型存储引擎实现都不是简单的行级锁,它们一般都实现了多版本并发控制,可以认为MVCC是行锁的一个变种,在很多情况下避免的加锁的操作,因此开销更低,实现对读的不阻塞,读不加锁,读写不冲突。缺点是每行记录需要额外的记录空间,需要做更多的维护工作和检查。
MVCC的实现是通过保存数据在某个时间点的快照来实现的,也就是说不管执行多长时间,每个事务看到的数据都是一致的,根据事务开始的时间点不同,每个事务对同一张表,同一时刻看到的数据都是不一样的,
3.MVCC具体实现原理?
MVCC具体实现原理主要是依赖记录中3个隐藏的字段、undo日志、以及Read View来实现的
3.1. 三个隐藏字段:在表中除了我们自定义的字段外,还包括DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID等字段
- DB_TRX_ID:6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
- DB_ROLL_PTR:7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
- DB_ROW_ID:6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
- 实际还有一个删除flag隐藏字段, 既记录被更新或删除并不代表真的删除,而是删除flag变了
如下图所示:
前面的两列是用户自己定义的,后面的三列是Mysql数据库默认的隐藏列,其中,DB_ROW_ID是数据库默认为该行记录生成的唯一隐式主键,DB_TRX_ID是当前操作该记录的事务ID,而DB_ROLL_PTR是一个回滚指针,用于配合undo日志,指向上一个旧版本
3.2 undo日志
undo日志主要分为两种:
- insert undo log : 代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃
- update undo log:事务在进行update或delete时产生的undo log; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除
对undo有帮助的主要是:update undo log日志,它实际存在于rollback segment中旧的记录链表中,
执行流程如下(很重要):
比如一个有个事务插入persion表插入了一条新记录,记录如下,name为LiuYiFei, age为20岁,隐式主键是1,事务ID和回滚指针,我们假设为NULL,
现在来了一个事务1对该记录的name做出了修改,改为Tom (update person set name=Tom where name=‘LiuYiFie‘)
- 在事务1对数据行进行修改时,首先数据库会对该数据行加一个排他锁
- 然后会把改行数据拷贝到undo log中,作为旧的记录,这样unlog 里面就会有了改行的副本
- 拷贝完毕之后,修改字段name有LiuYiFei-->Tom,并修改隐藏字段事务ID为当前事务为1的ID,这里默认从1开始,之后递增,回滚指针指向拷贝拷贝到undo log的副本记录,即表示我的上一个副本就是它。
- 事务提交,释放锁
如下图所示:
又来了个事务2修改person表的同一个记录,将age修改为30岁
- 在事务2对数据行进行修改时,首先数据库也会对该数据行加一个排他锁
- 然后把该行数据拷贝到undo log中,作为旧记录,发现该行记录已经有undo log了,那么最新的旧数据作为链表的表头,插在该行记录的undo log最前面
- 修改该行age为30岁,并且修改隐藏字段的事务ID为当前事务2的ID, 那就是2,回滚指针指向刚刚拷贝到undo log的副本记录
- 事务提交,释放锁
总结:
purge:
- 从前面的分析可以看出,为了实现InnoDB的MVCC机制,更新或者删除操作都只是设置一下老记录的deleted_bit,并不真正将过时的记录删除
-
为了节省磁盘空间,InnoDB有专门的purge线程来清理deleted_bit为true的记录。为了不影响MVCC的正常工作,purge线程自己也维护了一个read view(这个read view相当于系统中最老活跃事务的read view);如果某个记录的deleted_bit为true,并且DB_TRX_ID相对于purge线程的read view可见,那么这条记录一定是可以被安全清除的。
3.3 Read View(读视图)
什么是读视图?
这个视图数组把所有的trx_id分成了三种情况:
- 如果落在绿色的部分,表示这个版本是已提交的事务或者是当前事务自己生成的是可见的
- 如果落在红色的部分,表明这个版本是由将来事务生成的,是肯定不可见的
- 如果落在黄色的部分,那么就包括两种情况:
- 若row_trx_id在数组中,表示这个版本是由还没提交的事务生成的,不可见
- 若row_trx_id不在这个数组中,表示这个版本是已经提交的事务生成的,可见。
当前读:
快照读:
总结: