MySQL 到底是如何做到多版本并发的?
之前的文章简单的介绍了 MySQL 的事务隔离级别,它们分别是:读未提交、读已提交、可重复读、串行化。这篇文章我们就来探索一下 MySQL 事务隔离级别的底层原理。
本篇文章针对 InnoDB 存储引擎
多版本并发控制
我们知道,读未提交会造成脏读、幻读、不可重复读,读已提交会造成幻读、不可重复读,可重复读可能会有幻读,和串行化就不会有这些问题。
那 InnoDB 到底是怎么解决这些问题的呢?又或者,你有没有想过造成脏读、幻读、不可重复读的底层最根本的原因是什么呢?
这就是今天要聊的主角——MVCC(Multi-Version Concurrent Controll),也叫多版本并发控制。InnoDB 是一个支持多事务并发的存储引擎,它能让数据库中的读-写操作能够并发的进行,避免由于加锁而导致读阻塞。
正是由于有了 MVCC,在事务B更新 id=1 的数据时,事务A读取 id=1 的操作才不会被阻塞。而不阻塞的背后则是不加锁的一致性读。那什么是一致性读?
一致性读
简单来讲,当进行 query 查询时,InnoDB 会对当前时间点的数据库创建一个快照,快照创建完之后,当前查询就只能感知到快照创建之前提交的事务改动,在快照创建之后再提交的事务就不会被当前query感知。
当然,当前事务自己更新的数据是个例外。当前事务修改过的行,再次读取时是能够拿到最新的数据的。而对于其他行,读取的仍然是打快照时的版本。

而这个快照就是 InnoDB 实现事务隔离级别的关键。
在读已提交(Read Committed)的隔离级别下,事务中的每一次的一致性读都会重新生成快照。而在可重复读(Repeatable Read)的隔离级别下,事务中所有的一致性读都只会使用第一次一致性读生成的快照。
这也就是为什么,在上图中事务B提交了事务之后,读已提交的隔离级别下能看到改动,可重复读的隔离级别看不到改动,本质上就是因为读已提交又重新生成了快照。
在读已提交、可重复读的隔离级别下,SELECT 语句都会默认走一致性读,并且在一致性读的场景下,不会加任何的锁。其他的修改操作也可以同步的进行,大大的提升了 MySQL 的性能。而这也就是MVCC多版本并发控制的实现原理。这种读还有个名字叫 快照读 。
那如果我在事务中想要立马看到其他的事务的提交怎么办?有两种方法:
使用读已提交隔离级别 对 SELECT加锁,共享锁和排他锁都行,再具体点就是FOR SHARE和FOR UPDATE
当然,第二种方法如果对应的记录加的锁和 SELECT 加的锁互斥,SELECT 就会被阻塞,这种读也有个别名叫 当前读。
了解完上面的解释,下次再有人问你 MVCC 是怎么实现的,你就能从一致性读(快照读)和当前读来进行解释了,并且把不同的隔离级别下对一致性读快照的刷新机制也讲清楚。
但是我觉得还不够,应该还需要继续往下深入了解。因为我们只知道个快照,其底层到底是怎么实现的呢?其实还是不知道的。
深入一致性读原理
从常理来说,不同的一致性读可能会读到不同版本的数据,那么这些肯定都存储在 MySQL 中的,否则不可能被读取到。是的,这些数据都存储在 InnoDB 的表空间内,再具体点这些数据存储在 Undo 表空间内。
InnoDB 内实现 MVCC 的关键其实就是三个字段,并且数据表中每一行都有这三个字段:

DB_TRX_ID 该字段有6个字节,用于存储上次插入或者更新该行数据的事务的唯一标识。你可能会问,只有插入和更新吗?那删除呢?其实在InnoDB的内部,删除其实就是更新操作,只不过会更新该行中一个特定的比标志位,将其标记为删除。 DB_ROLL_PTR 该字段有7个字节,你可以叫它回滚指针,该指针指向了存储在回滚段中的一条具体的Undo Log。即使当前这行数据被更新了,我们同样的可以通过回滚指针,拿到更新之前的历史版本数据。 DB_ROW_ID 该字段有6个字节,InnoDB给该行数据的唯一标识,该唯一标识会在有新数据插入的时候单调递增,就跟我们平时定义表结构的时候定义的 primary key的时候单调递增是一样的。DB_ROW_ID会被包含在聚簇索引中,其他的非聚簇索引则不会包含。
通过 DB_ROLL_PTR 可以拿到最新的一条 Undo Log,然后每一个对应的 Undo Log 指向其上一个 Undo Log,这样一来,不同的版本就可以连接起来形成链表,不同的事务根据需求和规则,从链表中选择不同的版本进行读取,从而实现多版本的并发控制,就像这样:

可能有人对 Undo Log 没啥概念,记住这个就好了:
Undo Log 记录的是此次事务开始前的数据状态,就有点类似于 Git 中的某个 commit,你提交了某个 commit, 然后开始做一个及其复杂的需求,然后做着做着心态就崩了,就不想要这些改动了,你就可以直接
git reset --hard $last_commit_id回退,上个 commit 你就可以理解为 Undo Log,感兴趣的可以去看看 基于Redo Log和Undo Log的MySQL崩溃恢复流程
Undo Log 的组成
可能也有人会有疑问,说 Undo Log 不是应该在事务提交之后就被删除了吗?为什么我通过 MVCC 还能查到之前的数据呢?
实际上在 InnoDB 中,Undo Log 被分成了两部分,分别是
Insert Undo Log Update Undo Log

对于 Insert Undo Log 来说,它只会用于在事务中发生错误的回滚,因为一旦事务提交了,Insert Undo Log 就完全没用了,所以在事务提交之后 Insert Undo Log 就会被删除。
而 Update Undo Log 不同,其可以用于 MVCC 的一致性读,为不同版本的请求提供数据源。那这样一来,是不是 Update Undo Log 就完全没法移除了?因为你不清楚啥时候就会有个一致性读请求过来,然后导致其占用的空间越来越大。
对,但也不完全对。
一致性读本质上是要处理多事务并发时,需要按需给不同的事务以不同的数据版本,所以如果当前没有事务存在了,Update Undo Log 就可以被干掉了。
MySQL 的官方建议有点皮,建议大家定期提交事务,这样机器上的 Undo Logs 就可以被定期的清理。我寻思,不提交事务整个 DB 不就 hang 住了,那不完犊子了吗..
EOF
本篇文章就先到这里,至于怎么 Update Undo Log 怎么被干掉的,之后有空专门写篇文章来聊聊。
本篇文章已放到我的 Github github.com/sh-blog 中,欢迎 Star。微信搜索关注【SH的全栈笔记】,回复【队列】获取MQ学习资料,包含基础概念解析和RocketMQ详细的源码解析,持续更新中。
如果你觉得这篇文章对你有帮助,还麻烦点个赞,关个注,分个享,留个言。

MySQL 到底是如何做到多版本并发的?的更多相关文章
- 《高性能Mysql》解读---Mysql的事务和多版本并发
1.base:ACID属性,并发控制 2.MySql事务的隔离级别有哪些,含义是什么? 3.锁知多少,读锁,写锁,排他锁,共享锁,间隙锁,乐观锁,悲观锁. 4.Mysql的事务与锁有什么关联?MySq ...
- Mysql加锁过程详解(5)-innodb 多版本并发控制原理详解
Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...
- MySQL 到底能不能放到 Docker 里跑?
https://weibo.com/ttarticle/p/show?id=2309404296528549285581 前言 前几月经常看到有 MySQL 到底能不能放到 Docker 里跑的各种讨 ...
- (转)innodb 多版本并发控制原理详解
转自:https://blog.csdn.net/aoxida/article/details/50689619 多版本并发控制技术已经被广泛运用于各大数据库系统中,如Oracle,MS SQL Se ...
- MySQL实战 | 01-当执行一条 select 语句时,MySQL 到底做了啥?
原文链接:当执行一条 select 语句时,MySQL 到底做了啥? 也许,你也跟我一样,在遇到数据库问题时,总时茫然失措,想重启解决问题,又怕导致数据丢失,更怕重启失败,影响业务. 就算重启成功了, ...
- Quartz.NET 3.0.7 + MySql 实现动态调度作业+动态切换版本+多作业引用同一程序集不同版本+持久化+集群(一)
原文:Quartz.NET 3.0.7 + MySql 实现动态调度作业+动态切换版本+多作业引用同一程序集不同版本+持久化+集群(一) 前端时间,接到领导任务,写了一个调度框架.今天决定把心路历程记 ...
- MySQL到底能支持多大的数据量?
MySQL是中小型网站普遍使用的数据库之一,然而,很多人并不清楚MySQL到底能支持多大的数据量,再加上某些国内CMS厂商把数据承载量的责任推给它,导致很多不了解MySQL的站长对它产生了很多误解,那 ...
- Mac卸载mysql并安装mysql升级到8.0.13版本
引言 今天mysql升级到8.0.13版本,遇到了很多问题,在此进行总结方便以后查看. 卸载mysql brew uninstall mysql sudo rm /usr/local/mysql su ...
- 转:MySQL到底能支持多大的数据量?
MySQL到底能支持多大的数据量? MySQL是中小型网站普遍使用的数据库之一,然而,很多人并不清楚MySQL到底能支持多大的数据量,再加上某些国内CMS厂商把数据承载量的责任推给它,导致很多不了解M ...
随机推荐
- Spring Boot WebFlux-03——WebFlux 整合 MongoDB
第03课:WebFlux 整合 MongoDB 前言 上一课的内容讲解了用 Map 数据结构内存式存储了数据,这样数据就不会持久化,本文我们用 MongoDB 来实现 WebFlux 对数据源的操作. ...
- 裸辞闭关2个月,成功进大厂!吃透这份562页《算法知识手册》,化身offer收割机!
前言 记得我上本科的时候,我们老师一直跟我们强调:"算法才是编程的灵魂,一定要把算法学好."因为不管你是Java编程爱好者.还是python的忠实粉丝,亦或觉得PHP才是这个世界最 ...
- 【科普】MySQL中DDL操作背后的并发原理
一. 简介 DQL:指数据库中的查询(select)操作. DML:指数据库中的插入(insert).更新(update).删除(delete)等行数据变更操作. DDL:指数据库中加列(add co ...
- Duilib的双缓冲实现,附带GDI、WTL的双缓冲实现
前言: 闪烁问题,之前的经验是使用双缓冲,借此机会,把双缓冲的研究心得总结下. 双缓冲的含义: 缓冲这个词,相信大家都不陌生,Cache.主要是为了解决上下游(或者模块.或者系统)等性能不匹配问题.如 ...
- 《手把手教你》系列基础篇之(四)-java+ selenium自动化测试- 启动三大浏览器(下)基于Maven(详细教程)
1.简介 上一篇文章,宏哥已经在搭建的java项目环境中实践了,今天就在基于maven项目的环境中给小伙伴们 或者童鞋们演示一下. 2.eclipse中新建maven项目 1.依次点击eclipse的 ...
- JavaScript与服务端进行数据交互的方式
XMLHttpRequest XHR是项古老的技术,不同的浏览器厂商对其实现方式不同,例如有些浏览器只支持onload事件处理器,有些只支持onreadystatechange事件处理器. 发送Get ...
- 『心善渊』Selenium3.0基础 — 15、Selenium对多窗口的操作
目录 1.多标签/多窗口之间的切换 2.句柄练习 1.多标签/多窗口之间的切换 (1)多标签/多窗口场景: 在页面操作过程中有时候点击某个链接会弹出新的窗口,这时就需要切换到新打开的窗口上进行操作,如 ...
- SpringCloud微服务(基于Eureka+Feign+Hystrix+Zuul)
一.搭建注册中心 1.1.创建一个cloud-service项目 1.2:POM文件依赖 1 <?xml version="1.0" encoding="UTF-8 ...
- 二叉搜索树(Binary Search Tree)(Java实现)
@ 目录 1.二叉搜索树 1.1. 基本概念 1.2.树的节点(BinaryNode) 1.3.构造器和成员变量 1.3.公共方法(public method) 1.4.比较函数 1.5.contai ...
- Vue Element-ui表单校验规则,你掌握了哪些?
1.前言 Element-ui表单校验规则,使得错误提示可以直接在form-item下面显示,无需弹出框,因此还是很好用的. 我在做了登录页面的表单校验后,一度以为我已经很了解表单的校验规则. ...