暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

深入理解MVCC

小青菜的技术博客 2021-04-12
516


1. MVCC的基本概念

1.1 三种数据库并发场景

  • 读-读:不存在任何问题,也不需要并发控制

  • 读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读

  • 写-写:有线程安全问题,可能会存在更新丢失问题

1.2 什么是MVCC

  • MVCC,全称Multi-Version Concurrency Control,即多版本并发控制,是乐观锁的一种实现方式,可以做到读不加锁,读写不冲突,解决读-写冲突的无锁并发控制。

  • 注意

    • MVCC 只在 Read Commited 和 Repeatable Read 两种隔离级别下工作。

1.3 快照读与当前读的解释

  • 快照读:

    • 简单的select操作,不需要加锁,基于MVCC和undo log来实现的,读取的是记录的可见版本(有可能是历史版本)。

  • 当前读:

    • 特殊的读操作,需要加锁,是悲观锁的实现,读取的是记录的最新版本,并且当前读返回的记录,都会加锁,保证其他事务不会再并发修改这条记录。 

    • insert/update/delete

    • select ...for update

    • select ... lock in share mode

2. MVCC的底层实现

  • MVCC的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 3个隐式字段,undo log ,Read View 来实现的。

  • InnoDB MVCC的实现基于undo log,通过回滚指针来构建需要的版本记录。通过ReadView来判断哪些版本的数据可见。同时Purge线程是通过ReadView来清理旧版本数据。

2.1 三个隐式字段

  • DB_TRX_ID

    • 6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID

  • DB_ROLL_PTR

    • 7byte,回滚指针,用于配合undo log,指向这条记录的上一个版本

  • DB_ROW_ID

    • 6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引

  • 注意

    • 实际上每条记录的头信息(record header)里都有一个专门的bit(deleted flag)来表示当前记录是否已经被删除


2.2 undo log

2.2.1 基本概念

  • undo log主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。

  • 作用:

    • 用于事务的回滚

      • undo日志用于存放数据修改被修改前的值,如果这个修改出现异常,可以使用undo日志来实现回滚操作,保证事务的一致性。

      • undo日志,只将数据库逻辑地恢复到原来的样子,在回滚的时候,它实际上是做的相反的工作

    • 用于MVCC

  • undo log的类型主要分为:

    • insert undo log

    • update undo log

2.2.2 insert undo log

  • insert undo log是指在insert 操作中产生的undo log,因为insert操作的记录,只对事务本身可见,对其他事务不可见。故该undo log可以在事务提交后直接删除,不需要进行purge操作。

2.2.3 update undo log

  • update undo log记录的是对delete 和update操作产生的undo log,该undo log可能需要提供MVCC机制,因此不能再事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。

  • 具体工作原理需要分以下情况讨论

2.2.3.1 更新主键

  • 聚簇索引和二级索引都无法进行in place update,都会产生两个版本

  • update分两步执行,先删除该行,再插入一行目标行

2.2.3.2 更新非主键

  • 聚簇索引可以in place update,二级索引产生两个版本

  • 聚簇索引记录undo log,二级索引不记录undo log

  • 更新二级索引,同时需要判断是否修改索引页面的MAX_TRX_ID

2.2.3.3 删除操作

  • 删除操作实际上不会直接删除,而只是标记为删除,最终的删除操作是purge线程完成的

2.2.4 purge线程两个主要作用是:

  • 清理undo log

  • 清除page里面带有Delete_Bit标识的数据行。在InnoDB中,事务中的Delete操作实际上并不是真正的删除掉数据行,而是一种Delete Mark操作,在记录上标识删除,真正的删除工作需要后台purge线程去完成。

2.3 Read View(读视图)

2.3.1 什么是Read View

  • Read View就是事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以最新的事务,ID值越大

2.3.2 作用

  • Read View主要是用来做可见性判断的, 即当我们某个事务执行快照读的时候,对该记录创建一个Read View读视图,把它用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。

2.3.3 核心算法(可见性算法)

  • Read View的三个属性

    • trx_ids

      • 一个数值列表,用来维护Read View生成时刻系统正活跃的事务ID

    • up_limit_id

      • 记录trx_ids列表中事务ID最小的ID

    • low_limit_id

      • ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1

  • 可见性判断的流程

    • 遍历DB_TRX_ID执行以下步骤,直到找到当前事务可见的最新数据

      • 遍历方法:如果当前DB_TRX_ID这条记录不满足当前事务的可见性,可通过这条记录的DB_ROLL_PTR回滚指针去取出undo log中前一个版本的DB_TRX_ID

    • step1:比较DB_TRX_ID 小于 up_limit_id

      • 如果小于,则当前事务能看到DB_TRX_ID 所在的记录;即该记录是可见的最新的记录

      • 如果大于等于进入step2

    • step2:判断 DB_TRX_ID 大于等于 low_limit_id

      • 如果大于等于则代表DB_TRX_ID 所在的记录在Read View生成后才出现的,那对当前事务肯定不可见,继续遍历下一个DB_TRX_ID

      • 如果小于则进入step3

    • step3:判断DB_TRX_ID 是否在活跃事务之中

      • 如果在,则代表当前事务的Read View生成时刻,DB_TRX_ID这个事务还在活跃,还没有Commit,DB_TRX_ID这个事务修改的数据,当前事务也是看不见的;即对当前事务不可见,继续遍历下一个DB_TRX_ID

      • 如果不在,则说明,DB_TRX_ID这个事务在当前事务的Read View生成之前就已经Commit了,DB_TRX_ID这个事务修改的结果,对于当前事务是可见的

3. MVCC的工作原理

3.1 MVCC查询的工作流程

3.1.1 查询主键索引

  • 生成Read View读视图

  • 通过主键查找记录,根据记录里的DB_TRX_ID与Read View读视图进行可见性判断

  • 配合DB_ROLL_PTR回滚指针和undo log来找到当前事务可见的数据记录

3.1.2 查询二级索引

  • 生成Read View读视图

  • 比较读视图的up_limit_id与MAX_TRX_ID大小

    • 如果MAX_TRX_ID  小于 本次Read View的up_limit_id,则全部可见,过滤记录中的有效记录

    • 否则,无法通过二级索引判断可见性,需要一次遍历每条记录,反查到聚簇索引记录,通过聚簇索引记录来判断可见性

3.2 MVCC与隔离级别

  • MVCC 只在 Read Commited 和 Repeatable Read 两种隔离级别下工作。

  • 在RC隔离级别下,是每个快照读都会生成并获取最新的Read View

    • 这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因

  • 在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。

    • 即RR级别下,快照读生成Read View时,Read View会记录此时所有其他活动事务的快照,这些事务的修改对于当前事务都是不可见的。而早于Read View创建的事务所做的修改均是可见

4. 参考

https://www.cnblogs.com/AlmostWasteTime/p/11466520.html

https://www.jianshu.com/p/8845ddca3b23

https://www.zhihu.com/question/27674363/answer/38034982

https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html

https://www.cnblogs.com/xibuhaohao/p/11947041.html

https://blog.csdn.net/qiuyepiaoling/article/details/8054346

https://blog.csdn.net/shaochenshuo/article/details/76137652

https://www.cnblogs.com/stevenczp/p/8018986.html

https://www.cnblogs.com/rongdi/p/13378892.html

https://www.jianshu.com/p/336e4995b9b8

http://mysql.taobao.org/monthly/2015/04/01/

https://www.pianshen.com/article/50271826706/#Innodb__2

http://mysql.taobao.org/monthly/2018/11/04/

https://www.jianshu.com/p/8845ddca3b23

https://www.cnblogs.com/micrari/p/8144339.html



文章转载自小青菜的技术博客,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论