我的编程空间,编程开发者的网络收藏夹
学习永远不晚

MySQL多版本并发控制MVCC底层原理解析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

MySQL多版本并发控制MVCC底层原理解析

1 事务并发中遇到的问题

1.1 脏读

当一个事务读取到了另外一个事务修改但未提交的数据,被称为脏读。

1.2 不可重复读

当事务内相同的记录被检索两次,且两次得到的结果不同时,此现象称为不可重复读。

1.3 幻读

当一个事务同样的查询条件查询两次(多次),查出的条数不一致称为幻读。

2 隔离级别

我们上边介绍了几种并发事务执行过程中可能遇到的一些问题,这些问题也有轻重缓急之分,我们给这些问题按照严重性来排一下序:

脏读 > 不可重复读 > 幻读

SQL 标准中规定,针对不同的隔离级别,并发事务可以发生不同严重程度的

问题,具体情况如下:

  • READ UNCOMMITTED:未提交读。
  • READ COMMITTED:已提交读。
  • REPEATABLE READ:可重复读。
  • SERIALIZABLE:可串行化

SQL 标准中规定,针对不同的隔离级别,并发事务可以发生不同严重程度的问题,具体情况如下:

  • READ UNCOMMITTED 隔离级别下,可能发生脏读、不可重复读和幻读问题。
  • READ COMMITTED 隔离级别下,可能发生不可重复读和幻读问题,但是不可以发生脏读问题。
  • REPEATABLE READ 隔离级别下,可能发生幻读问题,但是不可以发生脏读和不可重复读的问题。
  • SERIALIZABLE 隔离级别下,各种问题都不可以发生。

3 版本链

我们知道,对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列(row_id 并不是必要的,我们创建的表中有主键或者非 NULL的 UNIQUE 键时都不会包含 row_id 列):

trx_id: 每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务 id 赋值给 trx_id 隐藏列。

roll_pointer: 每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo 日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

假设插入该记录的事务 id 为 80的记录,那么此刻该条记录的示意图如下所示:

假设之后两个事务 id 分别为 100、200 的事务对这条记录进行 UPDATE 操作,操作流程如下:

Trx 100:


UPDATE t_people SET name = '关羽' WHERE number = 1;
UPDATE t_people SET name = '张飞' WHERE number = 1;

Trx 200:


UPDATE t_people SET name = '赵云' WHERE number = 1;
UPDATE t_people SET name = '诸葛亮' WHERE number = 1;

每次对记录进行改动,都会记录一条 undo 日志,每条 undo 日志也都有一个 roll_pointer 属性(INSERT 操作对应的 undo 日志没有该属性,因为该记录并没有更早的版本),可以将这些 undo 日志都连起来,串成一个链表,所以现在的情况就像下图一样:

对该记录每次更新后,都会将旧值放到一条 undo 日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被 roll_pointer 属性连接成一个链表,我们把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。另外,每个版本中还包含生成该版本时对应的事务 id。于是可以利用这个记录的版本链来控制并发事务访问相同记录的行为,那么这种机制就被称之为多版本并发控制(Mulit-Version Concurrency Control MVCC)

4 ReadView

4.1 ReadView 定义

InnoDB 提出了一个 ReadView 的概念,这个 ReadView 中主要包含 4个比较重要的内容:

  • (1) m_ids:表示在生成 ReadView 时当前系统中 活跃 的读写事务的事务 id 列表。
  • (2) min_trx_id: 表示在生成 ReadView 时当前系统中活跃的读写事务中最小的事务 id,也就是 m_ids 中的最小值。
  • (3) max_trx_id:表示生成 ReadView 时系统中应该分配给下一个事务的 id 值。max_trx_id 并不是 m_ids 中的最大值,事务 id 是递增分配的。比方说现在有 id 为 1,2,3 这三个事务,之后 id 为 3 的事务提交了。那么一个新的读事务在生成 ReadView 时,m_ids 就包括 1 和 2,min_trx_id 的值就是 1,max_trx_id的值就是 4。
  • (4) creator_trx_id:表示生成该 ReadView 的事务的事务 id。

4.2 访问控制

有了这个 ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:

  • (1) 如果被访问版本的 trx_id 属性值与 ReadView 中的 creator_trx_id 值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  • (2) 如果被访问版本的 trx_id 属性值小于 ReadView 中的 min_trx_id 值,表明生成该版本的事务在当前事务生成 ReadView 前已经提交,所以该版本可以被当前事务访问。
  • (3) 如果被访问版本的 trx_id 属性值大于或等于 ReadView 中的 max_trx_id值,表明生成该版本的事务在当前事务生成 ReadView 后才开启,所以该版本不可以被当前事务访问。
  • (4) 如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id之间(min_trx_id < trx_id < max_trx_id),那就需要判断一下 trx_id 属性值是不是在m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。
  • (5) 如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本。如果最后一个版本也不可见的话,那么就意味着该条记录对该事务完全不可见,查询结果就不包含该记录。

4.3 再谈隔离

对于使用 READ UNCOMMITTED 隔离级别的事务来说,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了。

对于使用 SERIALIZABLE 隔离级别的事务来说,InnoDB 使用加锁的方式来访问记录。

在 MySQL 中,READ COMMITTED 和 REPEATABLE READ 隔离级别的的一个非常大的区别就是它们生成 ReadView 的时机不同。

4.3.1 READ COMMITTED(读已提交)

读已提交,每次读取数据前都生成一个 ReadView。

假设现在有一个使用 READ COMMITTED 隔离级别的事务开始执行:

详解查询:


#使用 READ COMMITTED 隔离级别的事务
#Transaction 100、200未提交,得到的列 name 的值为 刘备
SELECT name FROM t_people WHERE number = 1;  

这个 SELECET 的执行过程如下:

  • (1) 在执行 SELECT 语句时会先生成一个 ReadView,ReadView 的 m_ids 列表的内容就是[100, 200],min_trx_id 为 100,max_trx_id 为 201,creator_trx_id 为 0。
  • (2) 然后从版本链中挑选可见的记录,从图中可以看出,最新版本的列 name 的内容是诸葛亮,该版本的 trx_id 值为 200,在 m_ids 列表内,所以不符合可见性要求。(如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id之间,就需要判断一下 trx_id 属性值是不是在m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问 ),根据 roll_pointer 跳到下一个版本。
  • (3) 诸葛亮下一个版本的列name的内容是赵云,该版本的trx_id值也为200,也在m_ids列表内,所以也不符合要求,继续跳到下一个版本。
  • (4) 赵云下一个版本的列name的内容是张飞,该版本的trx_id值也为100,也在m_ids列表内,所以也不符合要求,继续跳到下一个版本。
  • (5) 张飞下一个版本的列name的内容是关羽,该版本的trx_id值也为100,也在m_ids列表内,所以也不符合要求,继续跳到下一个版本。
  • (6) 关羽下一个版本是刘备,该版本的trx_id值为80,小于ReadView 中的 min_trx_id 值,所以这个版本是符合要求的。

不可重复读: 100事务、200事务开启读取到name都为刘备。当100事务提交时,由于是读已提交事务隔离级别,每次读取都会创建ReadView,200事务读取时,创建生成的ReadView m_ids 为 [200],这时根据读取规则读取到的name就为张飞。


# 使用 READ COMMITTED 隔离级别的事务
BEGIN;
# SELECE1:Transaction 100、200 均未提交,得到name值为刘备
SELECT name  FROM t_people WHERE number = 1; 

# SELECE2:Transaction 100 提交,Transaction 200 未提交
#Transaction 200 事务查询,得到name值为张飞,发生不可重复读。
SELECT name  FROM teacher WHERE number = 1; 

4.3.2 REPEATABLE READ(可重读)

可重读,在第一次读取数据时生成一个 ReadView。

解决不可重复读: 100事务、200事务开启,创建ReadView,m_ids 为[100,200],读取到name都为刘备。当100事务提交时,由于是可重读事务隔离级别,只创建一次ReadView,m_ids 仍然是[100,200],这时根据读取规则读取到的name仍然是刘备。

5 幻读

当一个事务同样的查询条件查询两次(多次),查出的条数不一致称为幻读。

在 REPEATABLE READ 隔离级别下的事务 T1 先根据某个搜索条件读取到多条记录,然后事务 T2 插入一条符合相应搜索条件的记录并提交,然后事务 T1 再根据相同搜索条件执行查询。结果会是什么?按照 ReadView 中的比较规则,不管事务 T2 比事务 T1 是否先开启,事务 T1 都是看不到 T2 的提交的。但是,在 REPEATABLE READ 隔离级别下 InnoDB 中的 MVCC 可以很大程度地避免幻读现象,而不是完全禁止幻读。


#SELECT:快照读。update:当前读。
REPEATABLE READ 可以解决快照读幻读问题。解决不了当前读幻读的问题。

案例:

1 执行begin,执行select *。

2 开启另一个窗口 执行insert、select、commit。

3 回到原窗口执行查询

4 执行 update 、提交

5 查找

6 总结

从上边的描述中我们可以看出来,所谓的 MVCC(Multi-Version ConcurrencyControl ,多版本并发控制)指的就是在使用 READ COMMITTD、REPEATABLE READ这两种隔离级别的事务在执行普通的 SELECT 操作时访问记录的版本链的过程,这样子可以使不同事务的读-写、写-读操作并发执行,从而提升系统性能。

READ COMMITTD、REPEATABLE READ 这两个隔离级别的一个很大不同就是,生成 ReadView 的时机不同,READ COMMITTD 在每一次进行普通 SELECT 操作前都会生成一个 ReadView,而 REPEATABLE READ 只在第一次进行普通 SELECT 操作前生成一个 ReadView,之后的查询操作都重复使用这个 ReadView 就好了,从而基本上可以避免幻读现象。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

MySQL多版本并发控制MVCC底层原理解析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何进行MySQL多版本并发控制MVCC底层原理解析

本篇文章为大家展示了如何进行MySQL多版本并发控制MVCC底层原理解析,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1 事务并发中遇到的问题1.1 脏读当一个事务读取到了另外一个事务修改但未提交的
2023-06-22

MySQL多版本并发控制——MVCC机制分析

原文:https://www.cnblogs.com/buptleida/p/14283943.html
MySQL多版本并发控制——MVCC机制分析
2019-04-27

MySQL多版本并发控制MVCC详解

目录1.什么是MVCC2快照读与当前读2.1 快照读2.2当前读3.复习3.1 再谈隔离级别3.2 隐藏字段、Undo Log版本链4、MVCC实现原理之ReadView4.1什么是ReadView4.2 设计思路4.3 ReadView的
2022-07-25

Mysql InnoDB多版本并发控制MVCC详解

目录一丶为什么需要事务隔离级别1.实现事务隔离的方式:串行执行2.实现事务隔离的方式:可串行执行二丶并发事务执行的问题:脏写,脏读,不可重复读,幻读1.脏写2.脏读3.不可重复读4.幻读三丶隔离级别1.Read UnCommitted 读未
2022-11-29

⑩⑧【MySQL】InnoDB架构、事务原理、MVCC多版本并发控制

个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ InnoDB存储引擎 ⑩⑧【MySQL】详解I
⑩⑧【MySQL】InnoDB架构、事务原理、MVCC多版本并发控制
2023-12-22

详解MySQL多版本并发控制机制(MVCC)源码

目录一、前言二、MVCC(多版本并发控制机制)2.1、Repeatable Read2.2、Read Commit2.3、MVCC的优势三、MVCC(实现机制)3.1、select运行栈3.2、read_view的创建过程3.3、行版本可见
2022-05-22

mysql的MVCC多版本并发控制的实现

1 什么是MVCC MVCC全称是: Multiversion concurrency control,多版本并发控制,提供并发访问数据库时,对事务内读取的到的内存做处理,用来避免写操作堵塞读操作的并发问题。举个例子,程序员A正在读数据库中
2022-05-31

怎么在mysql中实现MVCC多版本的并发控制

今天就跟大家聊聊有关怎么在mysql中实现MVCC多版本的并发控制,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。1 什么是MVCCMVCC全称是: Multiversion conc
2023-06-14

Mysql MVCC多版本并发控制的知识点有哪些

这篇文章主要介绍了Mysql MVCC多版本并发控制的知识点有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Mysql MVCC多版本并发控制的知识点有哪些文章都会有所收获,下面我们一起来看看吧。1、MVC
2023-06-30

MySQL的多版本并发控制MVCC实现方法是什么

这篇文章主要讲解了“MySQL的多版本并发控制MVCC实现方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“MySQL的多版本并发控制MVCC实现方法是什么”吧!什么是MVCCMVCC
2023-06-22

MySQL MVCC 原理解析:为什么是并发控制的最佳选择?

MySQL MVCC 原理解析:为什么是并发控制的最佳选择?在关系型数据库中,数据的一致性和并发控制是至关重要的。MySQL作为最流行的关系型数据库管理系统之一,采用了MVCC(Multi-Version Concurrency Contr
2023-10-22

编程热搜

目录