【面试题】MVCC多版本并发控制

多版本并发控制指的就是维护一个数据的多个版本,,是得读写操作没有冲突;MVCC锁(排他锁)也是事务隔离性的保证

就好比以下的例子,我们查询id为30的记录到底是查询的是哪个事务所有修改的数据呢?这个就是MVCC的特点了,MVCC可保证我们读写操作没有冲突

MVCC的具体实现,主要是依赖于数据库记录中的隐式字段、undo log日志、readView。下面就来介绍一下这几个组成作用都是什么

隐式字段

隐藏字段是指:在mysql中给每个表都设置了隐藏字段其中包括以下三种:

  • DB_Trx_Id:最近修改事务的ID,记录插入这条记录或最后一次修改记录的事务ID
  • DB_Roll_Ptr:回滚指针,只想这条记录的上一个版本,用于配合undo log,指向上一个版本
  • DB_Row_Id:隐藏主键,如果表结构没有指定主键,那么将会自动生成一个隐藏字段

Undo Log

回滚日志,在insertupdatedelete的时候产生一个与之相反的sql(如insert的命令则在日志中加一个delete语句),便于数据回滚的日志

insert的时候,产生的undo log日志只在回滚时需要,在事务提交后,可被立即删除

updatedelete的时候,产生的undo log日志不仅在回滚时需要,MVCC版本访问也需要,不会立即被删除

Undo log版本链

undo 日志版本链是指一行数据被多个事务依次修改过后,MySQL 会为每个事务保留修改前的数据 undo 回滚日志,并通过两个隐藏字段trx_id(表示更新本行数据的事务 id)和roll_pointer(表示回滚指针,它指向该行数据上一个版本的 undo 日志)将这些 undo 日志串联起来形成的一个历史记录版本链

以下就是上述例子中的undo log版本链:

 不同事务或相同事务对同一条记录进行修改,会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录

ReadView

ReadView(读视图)是快照读Sql执行时MVCC提取提取数据的依据,记录并且维护当前活跃的事务(未提交的)id

当前读:读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。对于我们日常的操作,如select ... lock in share mode(共享锁)select ... for updateupdateinsertdelete(排他锁)都是一种当前读

 快照读:简单的select(不加锁)就是快照读,读取的是记录数据的可见版本,有可能是历史数据,不加锁,是非阻塞读,在不同的隔离级别之下,生成快照读的时机是不同的

  • Read Committed:每次select都会生成一个快照读
  • Repeatable Read:开启事务后第一个select语句才是快照读的地方,后序会复用该快照读

在ReadView中包含四个核心字段:

字段含义
m_ids当前活跃的事务ID集合
min_trx_id最小活跃事务ID
max_trx_id预分配事务ID,当前最大事务ID+1(因为事务ID是自增的)
creator_trx_idReadView创建者的事务ID

以下是关于MVCC版本链数据访问规则:

  • 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中是可以访问该版本
    成立,说明数据已经提交。

下面会以下述的事务执行顺序表来进行说明不同隔离级别的快照读

在RC的隔离级别下,每次select都会生成一个快照读,生成的快照如下:

根据版本链访问规则可知,第一次生成的快照读访问的是事务2,第二次生成的快照读访问的是事务3: 

由此可知在RC级别下,可知每次快照读都是访问已经提交的事务

在RR的隔离级别下,只有事务第一次执行的时候会生成一个快照读,生成的快照如下:

 可知两次select读取的都是事务2

总结

其中mvcc的意思是多版本并发控制。指维护一个数据的多个版本,使得读写操作没有冲突,它的底层实现主要是分为了三个部分,第一个是隐藏字段, 第二个是undo log日志,第三个是readView读视图

隐藏字段是指:在mysql中给每个表都设置了隐藏字段,有一个是trx_id(事 务id),记录每一次操作的事务id,是自增的;另一个字段是roll_pointer(回 滚指针),指向上一个版本的事务版本记录地址

undo log主要的作用是记录回滚日志,存储老版本数据,在内部会形成一个版本链,在多个事务并行操作某一行记录,记录不同事务修改数据的版本, 通过roll_pointer指针形成一个链表

readView解决的是一个事务查询选择版本的问题,在内部定义了一些匹配规 则和当前的一些事务id判断该访问那个版本的数据,不同的隔离级别快照读 是不一样的,最终的访问的结果不一样。如果是rc隔离级别,每一次执行快 照读时生成ReadView,如果是rr隔离级别仅在事务中第一次执行快照读时生 成ReadView,后续复用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lose_rose777

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值