一、什么是MVCC
全称 Multi-Version Concurrency Control,多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突。
事务5查询的记录是哪个事务版本的记录呢?MVCC的具体实现,主要依赖于数据库记录中的隐式字段、undo log日志、readView。
二、MVCC实现原理
1.隐藏字段
除了自己自定义的字段外,还有隐藏的3个字段。
DB_TRX_ID:修改事务时,就会自增+1。
DB_ROLL_PTR:就是指向上一个版本。
DB_ROW_ID:当字段有主键时,这个隐藏字段就没有意义了。
2.undo log
(1)定义
1. 回滚日志,在insert、update、delete的时候产生的便于数据回滚的日志。
2. 当insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除。
3. 而update、delete的时候,产生的undo log日志不仅在回滚时需要,mvcc版本访问也需要,不会立即被删除。
(2)undo log版本链
不同事务或相同事务对同一条记录进行修改,会导致该记录的undo log生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
3.readView
(1)定义
ReadView(读视图)是 快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
当前读:读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如:select ... lock in share mode(共享锁),select ... for update、update、insert、delete(排他锁)都是一种当前读。
快照读:简单的select(不加锁)就是快照读,快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读。
不同隔离级别也不一样:
Read Committed:每次select,都生成一个快照读。
Repeatable Read:开启事务后第一个select语句才是快照读的地方。
(2)四个核心字段
用下图解释上面字段:因为事务5开始创建视图时,事务2已经结束了。
m_ids就是345事务的id集合。 min_trx_id就是事务3的id。
max_trx_id就是事务5的id+1。 creator_trx_id就是事务5的id。
(3)版本链数据的访问规则
(4)隔离级别
1. RC隔离级别下,在事务中每一次执行快照读时生成ReadView。
结合前面的undolog和访问规则:可以得出事务5查询的第一次记录是事务2提交后的记录,事务5第二次查询的是事务3提交后的记录
2. RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
三、面试的回答
面试官:事务中的隔离性是如何保证的呢?(你解释一下MVCC)
候选人:事务的隔离性是由锁和mvcc实现的。
其中mvcc的意思是多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,它的底层实现主要是分为了三个部分,第一个是隐藏字段,第二个是undo log日志,第三个是readView读视图
隐藏字段是指:在mysql中给每个表都设置了隐藏字段,有一个是trx_id(事务id),记录每一次操作的事务id,是自增的;另一个字段是roll_pointer(回滚指针),指向上一个版本的事务版本记录地址
undo log主要的作用是记录回滚日志,存储老版本数据,在内部会形成一个版本链,在多个事务并行操作某一行记录,记录不同事务修改数据的版本,通过roll_pointer指针形成一个链表
readView解决的是一个事务查询选择版本的问题,在内部定义了一些匹配规则和当前的一些事务id判断该访问那个版本的数据,不同的隔离级别快照读是不一样的,最终的访问的结果不一样。如果是rc隔离级别,每一次执行快照读时生成ReadView,如果是rr隔离级别仅在事务中第一次执行快照读时生成ReadView,后续复用。