MySQL的MVCC总结

MySQL的MVCC总结

@author:Jingdai
@date:2021.04.18

最近学习了一下MVCC的实现,现总结一下。

概念

在介绍之前,先介绍几个概念,后面会用到。

  • RCREAD COMMITTED

    只能读取到其他事务提交的数据,可以解决脏读问题。

  • RRREPEATABLE READ

    在一次事务中,读取到的数据不会改变,可以解决脏读和不可重复读的问题。

  • 快照读

    普通的select语句,根据MVCC机制读。

  • 当前读

    加了 in share modefor update 的 select 语句,或者insert、update、delete语句,读取数据库中最新的数据。

  • MVCCMulti-Version Concurrency Control

    多版本并发控制,在一般的并发场景中,对同一个数据进行并发读写操作,读-读是可以同时进行的,而读-写、写-写是不能同时进行的,需要加锁。而MVCC就是一种机制,可以提供读-写的并发操作,而不用加锁,原理后面会介绍。

MVCC实现

MVCC实现依靠3个方面,隐藏字段、Read View和Undo日志。

隐藏字段

在数据库的表中,除了我们自己定义的字段外,InnoDB引擎还会在每行后面加3个隐藏字段。

  • DB_TRX_ID:最近一次对本行记录修改的事务ID。这里的修改包括更新和删除,因为删除并非真的从表中删除该行,只会修改一个删除标志位。
  • DB_ROLL_PTR:回滚指针,指向Undo日志。
  • DB_ROW_ID:随新行插入而递增的行ID,如果一个表没有主键或唯一非空索引时,InnoDB会使用这个ID产生聚簇索引。(和MVCC关系不大,聚簇索引主要用来提高查找性能)
Read View

当前事务的快照,注意这里的快照和平时理解的快照不一样。平时理解的快照是对原始数据的一个拷贝,而这里的快照则是对当前相关活跃事务列表的一个拷贝,它包含多个字段,这里仅仅记录与MVCC相关的字段。

  • low_limit_id:目前出现过的最大的事务ID+1,即下一个将被分配的事务ID。
  • trx_ids:Read View 创建时其他未提交的活跃事务ID列表。(不包括当前事务自己)
  • up_limit_id:活跃事务列表trx_ids中最小的事务ID,如果trx_ids为空,则up_limit_id 为 low_limit_id。
  • creator_trx_id:当前创建事务的ID,这是一个递增的编号。
Undo日志

Undo日志就是一个用链表结构组织的历史版本数据,当查看当前行的事务发现该行数据对自己不可见时,可以顺着 Undo日志去查看下一个,如果可见就查看,如果不可见就继续查看下一个。

在这里插入图片描述

如图,当需要修改记录时,假设现在最新的记录数据是93,修改这个记录的事务ID是1001,当事务ID为1003的事务想要把这个记录修改为99时,它首先将这行记录加排他锁,然后将当前行数据拷贝到Undo日志中,然后修改记录的数据为99,并将DB_TRX_ID 字段修改为1003,把DB_ROLL_PTR 字段修改为 指向Undo日志的地址。最后提交事务并释放排他锁。修改后的结果如下图。

在这里插入图片描述

算法流程

当处于 RR 隔离级别时,事务开启后,在执行第一条普通的 select 语句时,会创建一个快照Read View,记录目前的活跃事务的信息,一直到事务结束,都一直使用这一个Read View,不会改变。

当处于 RC 隔离级别时,事务开启后,每次执行普通的 select 语句时,都会重新创建一个新的快照 Read View。

而这个算法就是为了知道当前记录是否对当前事务可见,如果可见就直接读,如果不可见就去Undo日志中找到拥有可见性的记录去读。

首先前面说过当前行有一个 DB_TRX_ID 的隐藏字段,这里用 trx_id 代表它的值。

具体如下:

  1. 若 trx_id < up_limit_id,说明在创建Read View时,当前行已经修改并提交了(若未提交就会在trx_ids 列表中存在),则该记录对当前事务可见,跳到步骤5。
  2. 若 trx_id >= low_limit_id,说明在创建Read View后,有新的事务对该行进行了修改,这行数据对当前事务不可见,跳到步骤4。
  3. 若 up_limit_id <= trx_id < low_limit_id,又分两种情况:
    • 如果 trx_ids 列表中没有找到了 trx_id,和步骤1类似,说明在创建Read View时,当前行已经修改并提交了,该记录对当前事务可见,跳到步骤5。
    • 如果 trx_ids 列表中找到了 trx_id,就有可能是ID为trx_id的事务在当前事务创建Read View 前就修改了记录,但是没有提交,此时该记录对当前事务不可见,跳到步骤4。还有可能是 ID 为 trx_id 的事务在当前事务创建 Read View 后修改了该行记录(应该只有RR隔离级别会发生这种情况),这时不管是否提交都对当前事务不可见,同样跳到步骤4。
  4. 根据 DB_ROLL_PTR 回滚指针指向的记录,找到Undo日志里的下一条记录,将该行记录的DB_TRX_ID字段的值赋给trx_id,回到步骤1。
  5. 将该行的值返回。

参考

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值