阿里二面:了解 MySQL 事务底层原理吗(二)

发布于 2022-5-27 17:42
浏览
0收藏

 

MVCC 机制


MVCC(全称 Multi-Version Concurrency Control),即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;

 

我们本文的重点是事务的隔离级别的底层原理,但是似乎说到现在也并没有发现关于事务原理的影子(想发水文?)。

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
实际上要了解事务的底层原理,根本没法上来就开鲁,我相信那样的文章写出来不仅没人看,更是看不懂。所以为了让大家由浅入深的慢慢掌握。我必须要做很多铺垫,将相关的知识点进行抛砖引玉,然后一层一层剖析到原理。这里还请大家明白。

 

说到这里,我们又要提到一个新的概念了。就是数据在磁盘存储的时候,每一条存储的记录都会有事务 ID 和回滚指针(其他的是什么本文不需要关注,学习抓住脉路即可,否则必定走火入魔)。

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
这两个到底是干嘛的?我们还是先从概念说起

 

事务 ID:就是每个事务的唯一标识
回滚指针:该事务之前的记录的引用(指针)。换句话说就是相对现在时间节点的老数据

 

假设你需要操作某条记录,首先该条记录一定是先被加载到 Buffer Pool 中的,并且有这样的一条 undo log 记录。

 

画外音:undo log 就是修改前的记录。用于回滚的。

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
假设现在事务 A 开启了事务,将值改为 A

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
事务 A 还在活跃中,这个时候事务 B 开启了,将值改为 B

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
此时事务 A 和事务 B 都还在活跃中,这个时候事务 C 开启了,并将值改为 C

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
看到这里是不是稍微有一点感觉了。这上面的图有一个专有名词:MVCC 版本控制链。同时这里又涉及到一个新的名词:ReadView。就是每个事务在开启的时候都会创建一个 ReadView 视图。那具体什么叫 ReadView ,我们这里还不能一带而过,相反需要我们来详细的讨论分析下。

 

ReadView


ReadView 可能是你理解事务底层原理的核心部分,那么什么是 ReadView 呢

 

在每个事务开启的时候都会创建一个 ReadView 视图,作用就是用来记录每个事务中的操作的一些 Undo Log 记录。


他里面涉及到几个字段。分别是:m_ids、min_trx_id、max_trx_id、creator_trx_id。他们的具体含义如下:

 

  1. m_ids:用于记录活跃中的事务的 ID;
  2. min_trx_id:当前活跃的事务中的最小的事务 ID;
  3. max_trx_id:下一个即将要生成的事务 ID。注意这里并不是指的最大的事务 ID,这个事务一定是当前的 m_ids 中不存在的。(事务 ID 的生成是递增的);
  4. creator_trx_id:当前活跃事务的 ID;
    不要慌,光说概念一定是在耍流氓。下面我会通过图文并茂的形式来一一说明解释。假设有一个记录现在是这样存放的

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
看到这里大家应该知道的是,这条记录一定是原来的某一个事务修改后的结果。也就是说这是一条原本的已经存在的记录。

 

现在假设有 A、B、C 三个事务,他们分别先后开启,假设他们的事务 ID 依次为:4、5、6。

 

先来看事务 A,此时的m_ids 为:[4、5、6],min_trx_id 为:4,max_trx_id 为 7(下图第三行为max_trx_id),creator_trx_id 为 4。

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
事务 A 首先执行了一次查询操作,他此时是这么执行的:

 

首先他会顺着 MVCC 的版本控制链往下找。找啥?找该条记录的以前操作它的事务 ID,他发现找到的这个 undolog 日志的对应的事务 ID 为 3,比自己的 4 要小,所以可以肯定这条记录不是自己修改的,而又因为 m_ids 中的事务 ID 为 4、5、6,3 是比他们都要小的,所以可推断出查找到的这条记录是在本次事务开启之前就已经存在的。所以事务 A 查询到的值为 C。

 

此时事务 B 同样开始查询这条记录了。以此类推事务 B 此时的执行流程大概是这样子的,首先事务 B 会以同样的方式查询数据(PS:这些操作都是在内存中的)同样查询到的结果是 C,经过上面的的对于事务 A 的分析,相信这里已经不是问题了。但是假如现在事务 B 将该值改成了 B,也就是下面的这张图的样子。

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
此时事务 A 又开始活跃了,还是执行查询操作,这个时候结果该是多少呢?

 

首先事务 A 发现同样会顺着该条记录的 MVCC 版本控制链往下找,发现事务 ID 为 5 ,比 m_ids 中的最小的事务 ID 4 要大,那么可以且是存在于该集合中的,此时就可以断定事务 ID 为 5 的事务是正在进行中的事务,所以事务 A 是不会取该条 undo log 的值的。

 

然后继续往下找,找到了事务 ID 为 3 的 undo log 记录,对比后发现 3 不在 m_ids 中,且比 m_ids 中的最小的事务 ID 都要小。下面的判断就和刚开始的查询判断一样了。

 

假设此时事务 A 将该条记录的值改成 A ,然后事务 A 再查询这条记录,那么请问这个时候事务 A 查询是怎么样子的(这一步非常重要)?现在这些事务以及数据在我们的脑袋中应该是这样子的:

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
那么事务 A 到底是怎么查询的?查出来的结果到底是 A 还是 B?先来看下这张图,然后根据图一步一步来分析

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
事务 A 开始查询,返现此时的 undo log 日志针对于该条记录的 undo log 链(MVCC 版本链的另一种叫法)的第一条记录的事务 ID 为 4 ,一对比发现不就是自己修改的值吗?那么查询的结果就是 A。

 

那么此时如果是事务 B 来执行查询呢?结果你能否分析一下?那就是首先 B 发现最新的事务 ID 为 4 ,且在 m_ids 中,可以断定这是一条正在执行中的事务,且不和自己的一样,所以是不会取该值的。

 

然后继续顺着 undo log 日志链往下找,找到了事务 ID 为 5  的记录,发现和自己的一样,那这个不就是需要查找的结果吗?也就是说 事务 B 查找到的结果是 B。

以上是关于 ReadView 的相关的介绍,总体内容不算难,但是是需要认真思考的,这里先来一个小总结

 

  1. ReadView 其实使用版本链机制
  2. 他里面的核心属性为:
    m_ids: 一个列表, 存储当前系统活跃的事务 id (重点)
    min_trx_id: 当前 m_ids 活动事务中的最小的事务 ID
    max_trx_id: 下一个即将被分配出来的事务 ID
    creator_trx_id: 当前的事务的 ID
  3. ReadView 记录的是:每个事务中的 Undo log 日志
    说到这里,下面继续来分析本文的主题知识点:事务的底层原理(其实上面多多少少都说到了)。其实事务的底层就是基于 ReadView 来设计的。关于事务的底层原理,我们以 RC(Read Commit)和 RR(Repeatable read)来分析

 

1. Read commit


Read Commit 是事务隔离级别的其中一种,含义是:读取已经提交的记录。举个例子来说,假设有事务 A 和事务 B 都在活动中,事务 B 提交的记录是能够被事务 A 读取到的。

 

具体我们开始一步一步来分析。首先需要大家知道的是在 RC 隔离级别下,一个事务的每次查询操作,数据库都会为其创建一个新的 ReadView,这就是 RC 的核心思想。

 

假设有事务 A 和事务 B ,事务 ID 分别为 10 和 11,事务 A 还没开始活跃,事务 B 就将某条记录的值改为 B(假设原来的值为 X),但是还未提交,现在你可以想象一下下面这张图:

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
此时事务 A 开始活跃了,他首先执行了一次查询操作。按照上面的核心思想,此时数据库会重新创建一个 ReadView  里面的几个属性的值分别为:

  • m_ids:[10,11]
  • min_trx_id:10
  • max_trx_id: 12
  • creator_trx_id:10
    接着就是就是和上面说过的一样的查询过程了,首先 A 查询到的最近的一个事务 ID 为 11,发现在 m_ids 中,但是又和自己的事务 ID 不相等,所以就会顺着 undo log 链继续查找,然后找到了事务 ID 为 3 的记录,发现不在 m_ids  中且,比最小的事务 ID 10 还要小,所以可以断定出事务 ID 为 3 的这个记录是原本就存在的记录,所以查询到的结果就是 X。

 

接着事务 B 又开始活跃了,事务 B 直接提交了事务,然后事务 A 又发起了一起查询操作。现在这个时候就是 RC 的核心了:这个时候数据库会再次为事务 A 创建一个新的 ReadView 里面的四个属性分别为:

  • m_ids:[10]
  • min_trx_id:10
  • max_trx_id: 12
  • creator_trx_id:10
    然后 A 按照正常的流程去查询,首先查询到的是事务 ID 为 11 的记录,结果发现不在 m_ids 中,那这个时候就可以断定的是:这个是最近已经提交的记录,所以是能够查询到 B 这个值的,也就是说这次查询得到的结果就是 B 。

 

这就是 RC,是不是如果看懂了 ReadView 原理,这些再看起来就非常简单了?

 

2. Repeatable read


Repeatable read 是 MySQL 默认的隔离级别,既然是默认的,那一定是很厉害咯?其实你看完会发现 just so so ?

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
RR 的核心思想是:ReadView 创建以后直到事务提交,都不会再次重新生成。

 

首先还是有事务 A 和事务 B,事务 ID 分别为 10 和 11 ,事务 B 首先将值改为 B (假设原来值为 X),然后 事务 A 发起了一次查询的操作:

 阿里二面:了解 MySQL 事务底层原理吗(二)-开源基础软件社区
查询过程和前面的一模一样。我就不再赘述了。

 

接着事务 B 又开始活跃了,直接提交了事务,然后事务 A 又发起了一次查询。这个时候奇迹就出现了。因为我们刚刚说了:ReadView 创建以后直到事务提交,都不会再次重新生成。因为事务 A 在创建 ReadView 的时候 m_ids 是 10 和 11,所以现在查询的时候里面仍然是这个值,现在的查询是这样子的:事务 A 首先查询到的事务 ID 为 11 ,结果发现在 m_ids 中,也就不会取该值,会继续查找,当查找到事务 ID 为 3 的时候,发现不在 m_ids 中,所以查询到的就是 X。

现在你知道为什么这个隔离级别下的事务不会互相干扰了吧?这就是原理

 

本文小结


本文为了说明事务的底层原理,做了大量的铺垫,相信大家看完不光对不同隔离级别下事务的实现会有更深刻地理解,也同时明白了 undo log 记录的作用,所以多探索一下底层你会发现各种知识点是如何串在一起工作的,这种通透的感觉确实很奇妙^_^
··············  END  ··············

 

文章转自公众号:码海

分类
标签
已于2022-5-27 17:42:03修改
收藏
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐