目录
- 说明
- MVCC的底层原理
- 隐藏字段
- undo log
- Read View
说明
在被面试官问面试题的时候,首先它问了Mysql的事务的隔离级别有几种?默认是哪种?他们分别解决了什么问题?
我在一顿回答“巴巴巴巴。。。。”之后,它又继续问题了我:
1、读已提交是怎么实现的呢?
2、可重复读能不能解决幻读?如果可以是怎么解决的?什么情况下又会出现幻读?
我顿时不知道怎么回答,从这里,其实过后我才知道它已经把我从MySQL事务的隔离级别引到了MVCC那边,我需要去搞懂MVCC是什么以及它的底层原理。
MVCC的底层原理
MVCC的实现主要依赖于记录中的三个隐藏字段,undo log和Read View来实现。
隐藏字段
数据库表中除了我们自定义的字段外,还有数据库隐式定义的字段,如DB_TRX_ID,DB_ROLL_PTR和DB_ROW_ID等字段。
DB_TRX_ID:表示最近修改事务的id,用于记录创建这条记录或者是最后一次修改这条记录的事务的id。
DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本,用于配合undo log。
DB_ROW_ID:隐藏的主键,如果数据表没有主键,那么innodb会自动生成一个6字节的row_id。
undo log
Undo Log被称为回滚日志,表示在进行inset、delete和update操作的时候阐释的方便回滚的日志。
当进行inser操作的时候,产生的undo log只在事务回滚的时候需要,并且在事务提交之后可以被丢弃。
当进行update和delete操作的时候,产生的undo log不仅仅在事务回滚的时候需要,在快照读的时候也需要,所以不能随便删除,只有在事务回滚或者快照读不涉及该日志是,对应的日志才会被purge线程统一清除。
为了便于理解,我们来举个小例子:假设有三个事务,事务1、2、3。
事务1向表中插入一条记录,如图
事务2修改该数据进行update。
事务2修改原本的记录之前,数据库会对该行加排它锁。
然后把原本的记录拷贝一份到undo log中,作为旧记录,既在undo log中有原本记录的拷贝副本。
当拷贝完成后,对数据进行update,并且修改隐藏字段最近修改的事务id,回滚指针指到undo log的拷贝副本中。
事务提交后,释放锁。
事务3也对该记录做了update。
事务3在堆原本的记录做update之前,数据库会对该行记录加排它锁。
然后把原本的记录拷贝一份到undo log中,作为旧记录,但是发现改行记录已经有undo log了,那么最小的旧数据作为链表的表头,插在该行记录undo log的最前面。
拷贝插入到表头之后,对数据进行update,并且修改隐藏字段最近修改事务的id,回滚指针指向刚刚拷贝的undo log的副本记录。
事务提交后,释放锁。
从这个图片可以看出,
不同事务或者相同事务的对同一记录的修改,会导致该记录的undo log生成一条记录版本链表,undo log的链首就是最新旧记录,链尾就是最早的旧记录。
Read View
为什么要有Read View的存在?
如果一条记录在undo log中有多个数据版本,并且这些又是由不同事务进行操作的,那么我们该如何选择?
Read View是事务进行快照读操作的时候产生的可读视图,在该事务执行读快照操作的那一刻,会生成一个数据系统当前的快照,记录并维护系统当前活跃事务的id,事务的id值是递增的。
Read View的最大作用是用来做可见性判断的,也就是说当某个事务在执行快照读的时候,对该记录创建一个Read View视图,把它当做条件去判断当前事务能够看到哪个版本的数据,有可能读取到的是最小的数据,也有可能读取的是当前行记录的undo log中某个版本的数据。