MySQL 的多版本并发控制(MVCC) 是干啥的?
MySQL 的多版本并发控制(MVCC) 是干啥的?
点击蓝色“架构文摘”关注我哟
加个“星标”,每天上午 09:25,干货推送!
来源:https://segmentfault.com/a/1190000037557620
作者:白菜1031
一、什么是多版本并发控制
多版本并发控制 技术的英文全称是 Multiversion Concurrency Control ,简称 MVCC 。
多版本并发控制(MVCC)
是通过保存数据在某个时间点的快照来实现并发控制的。也就是说,不管事务执行多长时间,事务内部看到的数据是不受其它事务影响的,根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。
简单来说, 多版本并发控制
的思想就是保存数据的历史版本,通过对数据行的多个版本管理来实现数据库的并发控制。这样我们就可以通过比较版本号决定数据是否显示出来,读取数据的时候不需要加锁也可以保证事务的隔离效果。
可以认为 多版本并发控制(MVCC)
是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。
MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。不仅是MySQL,包括Oracle、PostgreSQL等其他数据库系统也都实现了MVCC,但各自的实现机制不尽相同,因为MVCC没有一个统一的实现标准,典型的有
乐观(optimistic)并发控制 和 悲观(pessimistic)并发控制 。
二、多版本并发控制解决了哪些问题
1. 读写之间阻塞的问题
通过 MVCC 可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力。
提高并发的演进思路:
普通锁,只能串行执行;
读写锁,可以实现读读并发;
数据多版本并发控制,可以实现读写并发。
2. 降低了死锁的概率
因为 InnoDB 的 MVCC 采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定必要的行。
3. 解决一致性读的问题
一致性读也被称为 快照读
,当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果。
三、快照读与当前读
快照读(SnapShot Read) 是一种 一致性不加锁的读 ,是 InnoDB并发如此之高的核心原因之一 。
这里的 一致性 是指,事务读取到的数据,要么是 事务开始前就已经存在的数据 ,要么是 事务自身插入或者修改过的数据 。
不加锁的简单的 SELECT 都属于 快照读 ,例如:
`SELECT * FROM t WHERE id=1`
与 快照读 相对应的则是 当前读 , 当前读 就是读取最新数据,而不是历史版本的数据。加锁的 SELECT 就属于当前读,例如:
SELECT * FROM t WHERE id=1 LOCK IN SHARE MODE;
SELECT * FROM t WHERE id=1 FOR UPDATE;
四、InnoDB 的 MVCC 是如何工作的
1. InnoDB 是如何存储记录的多个版本的
事务版本号
每开启一个事务,我们都会从数据库中获得一个事务 ID(也就是事务版本号),这个事务 ID 是自增长的,通过 ID 大小,我们就可以判断事务的时间顺序。
行记录的隐藏列
InnoDB 的叶子段存储了数据页,数据页中保存了行记录,而在行记录中有一些重要的隐藏字段:
DB_ROW_ID :6-byte,隐藏的行 ID,用来生成默认聚簇索引。如果我们创建数据表的时候没有指定聚簇索引,这时 InnoDB 就会用这个隐藏 ID 来创建聚集索引。采用聚簇索引的方式可以提升数据的查找效率。
DB_TRX_ID :6-byte,操作这个数据的事务 ID,也就是最后一个对该数据进行插入或更新的事务 ID。
DB_ROLL_PTR :7-byte,回滚指针,也就是指向这个记录的 Undo Log 信息。
InnoDB数据记录隐藏列
Undo Log
InnoDB 将行记录快照保存在了 Undo Log 里,我们可以在回滚段中找到它们,如下图所示:
Undo Log回滚历史记录
从图中能看到回滚指针将数据行的所有快照记录都通过链表的结构串联了起来,每个快照的记录都保存了当时的 db_trx_id,也是那个时间点操作这个数据的事务
ID。这样如果我们想要找历史快照,就可以通过遍历回滚指针的方式进行查找。
2. 在 可重复读(REPEATABLE READ) 隔离级别下, InnoDB 的 MVCC 是如何工作的
查询(SELECT)
InnoDB 会根据以下两个条件检查每行记录:
InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以 确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的 。
行的删除版本要么未定义,要么大于当前事务版本号。这可以确保 事务读取到的行,在事务开始之前未被删除 。
只有符合上述两个条件的记录,才能返回作为查询结果。
插入(INSERT)
InnoDB为新插入的每一行保存当前系统版本号作为行版本号。
删除(DELETE)
InnoDB为删除的每一行保存当前系统版本号作为行删除标识。
删除在内部被视为更新,行中的一个特殊位会被设置为已删除。
更新(UPDATE)
InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。
五、总结
多版本并发控制(MVCC) 在一定程度上实现了 读写并发 ,它只在 可重复读(REPEATABLE READ) 和
提交读(READ COMMITTED) 两个隔离级别下工作。其他两个隔离级别都和 MVCC 不兼容,因为 未提交读(READ UNCOMMITTED) ,总是读取最新的数据行,而不是符合当前事务版本的数据行。而 可串行化(SERIALIZABLE)
则会对所有读取的行都加锁。
行锁,并发,事务回滚 等多种特性都和MVCC相关。
参考
MySQL5.7文档:innodb-multi-versioning
《高性能MySQL》
end
推荐阅读:
TCP 三次握手、四手挥手,这样说你能明白吧!
拜托,不要再问我线程池啦!
为什么 Redis 单线程还这么快?
Spring Cloud架构的各个组件的原理分析
一口气说出 5 种 IO 模型,蒙圈了!
MySQL 的多版本并发控制(MVCC) 是干啥的?相关教程
MySQL 数据库崩溃(crash)的常见原因和解决办法
MySQL 数据库崩溃(crash)的常见原因和解决办法 墨墨导读: 本文来自墨天轮用户投稿,详述MySQL 数据库崩溃(crash)的常见原因和解决办法,希望对大家有帮助。 数据技术嘉年华,十周年盛大开启, 点我 立即报名 !大会以“自研智能新基建——云和数据促创新
Mysql中,21个写SQL的好习惯,你值得拥有呀!
Mysql中,21个写SQL的好习惯,你值得拥有呀! 每一个好习惯都是一笔财富,本文分SQL后悔药, SQL性能优化,SQL规范优雅三个方向,分享写SQL的21个好习惯,谢谢阅读,加油哈~ 日常开发写SQL的时候,尽量养成这个好习惯呀:写完SQL后,用explain分析一下,尤其
centos7安装Mysql(傻瓜式操作)
centos7安装Mysql(傻瓜式操作) 配置mysql8安装源 sudo rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm 安装mysql8 一路y即可(时间可能有点长) sudo yum --enablerepo=mysql80-community install mysql-community-server
MySQL基础知识——INSERT INTO
MySQL基础知识——INSERT INTO 点击上方SQL数据库开发,关注获取SQL视频教程 SQL专栏 SQL数据库基础知识汇总 SQL数据库高级知识汇总 INSERT INTO 作用 INSERT INTO 语句用于向表中插入新记录。 INSERT INTO 语法 INSERT INTO 语句可以用两种形式编写。 第一个
MySQL基础语句
MySQL基础语句 ## MySQL语言基础 一、MySQL 的数据类型(数值型、浮点型、日期/时间和字符串(字符)类型) 1.字符串类型 MySQL数据库基本语句 数据库 # 查看所有的数据库show databases ; # 创建一个数据库create database dat_name; # 删除一个数据库 drop da
Intellij idea Language level和Java Compiler版本编译失败问题
Intellij idea Language level和Java Compiler版本编译失败问题 相信大家都遇到过用idea新建一个maven工程,编译时因为 Java 版本为1.5 而失败当原因。今天这篇博客就来分析下解决方法。 一般在开始编译工程时会先设置 Java 语言版本,即如下两个设置: 我们
MyBatis配置逆向工程(mysql8版本)
MyBatis配置逆向工程(mysql8版本) 前言:本人查遍全网教程大多都是烂大街的过时教程(5版本的,要不就是引入SpringBoot的),所以本人本着雷锋精神,耗费一上午的时间为大家实践出这款纯净版的mybatis教程不涉及其他框架知识,但涉及到Maven包管理的(版本
MySQL数据库基于Binlog日志的主从同步
MySQL数据库基于Binlog日志的主从同步 目录 前言 一、实验环境 二、主从同步配置 2.1 配置Master端 2.2 配置Slave端 总结 在我之前的博客中,对MySQL数据库的AB复制原理、主从同步已做过相应的介绍,只不过那个主从同步是基于GTID的主从同步,本次来简单介绍