Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL MVCC机制

一文带你搞懂MySQL的MVCC机制

作者:JAVA旭阳

MySQL中的MVCC机制想必大家都有所耳闻吧,虽然在平时MySQL使用过程中基本上用不到,但是面试中出场率十分高,那么你对MVCC机制了解多少呢,MVCC机制是用来干嘛的呢,底层的工作原理是怎么样的呢,本文就带你一探究竟

MVCC机制是什么?

MVCC,英文全称Multiversion Concurrency Control,多版本并发控制。简单理解,就是相当于给我们的MySQL数据库拍个“快照”,定格某个时刻数据库的状态。

那你可能问为什么要拍个“快照”,也就是MVCC机制?

还记得事务的一大特性就是隔离性,一共有4个隔离级别,读未提交,读已提交,可重复读,串行化。

MySQL InnoDB 引擎的默认隔离级别可重复读为例,可重复读指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的。

为了保证事务启动到结束整个生命周期看到的数据是一致的, 一般有两种方案:

第一种方案存在明显的问题,加锁会引发阻塞,从而降低数据库性能。而MySQL设计者们采用第二种,也就是大名鼎鼎的MVCC,它不仅能够解决不可重复读,还一定程度解决幻读的问题,因为你整个数据库快照都有了,你就知道那个时刻的数据了。

虽然说SQL标准定义中可重复读隔离级别下会存在幻读的现象,但是不同的数据库厂商可以基于SQL标准下有不同的实现,那么不同隔离级别下发生的现象也会有出入,就拿MySQL的可重复读隔离级别就可以一定程度保证幻读。

小结一下:

MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突 ,做到即使有读写冲突时,也能做到不加锁非阻塞并发读,而这个读指的就是快照读 , 而非当前读

什么是快照读和当前读?

前面提到了快照读和当前读,这又有什么不一样呢,什么样的sql语句算是快照读,什么样的又算是当前读呢?

快照读

快照读又叫普通读,也就是利用MVCC机制读取快照中的数据。不加锁的简单的SELECT 都属于快照读,比如这样:

SELECT * FROM user WHERE ...

当前读

当前读读取的是记录的最新版本,读取时会对读取的记录进行加锁, 其他事务就有可能阻塞。加锁的 SELECT,或者对数据进行增删改都会进行当前读。比如:

SELECT * FROM user LOCK IN SHARE MODE; # 共享锁
SELECT * FROM user FOR UPDATE; # 排他锁
INSERT INTO user values ... # 排他锁
DELETE FROM user WHERE ... # 排他锁
UPDATE user SET ... # 排他锁

MVCC机制是咋工作的呢?

前面打个比方说MVCC机制相当于是基于整个数据库“拍了个快照”,这时,你会说这看上去不太现实啊。如果一个库有 100G,那么我启动一个事务,MySQL 就要保存 100G 的数据出来,这个过程得多慢啊,而且也很占用空间啊,根本就不能支持几个事务啊。别急,我们现在来讲解下MVCC机制是如何工作的。

数据的多个版本

首先MySQL innoDB存储引擎需要支持一条数据可以保留多个历史版本。怎么保留呢?还记得事务日志undo log吗?

对于使用 InnoDB 存储引擎的数据库表,它的聚簇索引记录中都包含下面两个隐藏列:

InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。

如上图所示,针对id=1的这条数据,都会将旧值放到一条undo日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被 roll_pointer 属性连接成一个链表,我们把这个链表称之为版本链,根据版本链就可以找到这条数据历史的版本。

一致性视图ReadView

利用undo log日志我们已经保留下了数据的各个版本,那么现在关键的问题是要读取哪个版本的数据呢?

这时就需要用到ReadView了,ReadView就是事务在使用MVCC机制进行快照读操作时产生的一致性视图, 比如针对可重复读隔离级别,是在事务启动的时候,创建一个ReadView, 那ReadView种都有哪些关键信息呢?

对于当前事务的启动瞬间来说,读取的一个数据版本的trx_id,有以下几种可能:

这种通过版本链 + 一致性视图 来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制),现在你明白MySQL如何实现了“秒级创建快照”的能力了吧。

还是不懂?举例说明

如果你对MVCC机制的整个流程还是比较模糊,我们现在举例来说明下。

比如student表中有一个事务id为8的插入记录:

insert into student(id, name, class) values(1, '张三', '一班')

我们现在在MySQL的读已提交和可重复读隔离级别下,MVCC机制的整个工作流程。

MySQL中的读未提交和序列化并不需要MVCC机制,读未提交,直接读取别人未提交的数据,而序列化全程用加锁的方式,也用不上MVCC, 大家体会下。

可重复读隔离级别下

可重复读REPEATABLE READ 隔离级别的事务来说,只会在第一次执行查询语句时生成一个 ReadView ,之后的查询就不会重复生成了。

begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。如果你想要马上启动一个事务,可以使用 start transaction with consistent snapshot 这个命令。

事务10事务20事务30
beginUPDATE student SET name="李四" WHERE id=1;UPDATE student SET name="王五" WHERE id=1;
begin更新了一些其他表的数据
beginSELECT * FROM

事务10和20均未提交,现在事务30执行select, 那么得到的结果是什么呢?

读已提交隔离级别下

读已提交READ COMMITTED是每次读取数据前都生成一个ReadView。基本的规则和流程与可重复读隔离级别一致,这里不做重复赘叙。

总结

本问重点介绍了MVCC机制,以及 MVCC 在 READ COMMITTDREPEATABLE READ 这两种隔离级别的事务在执行快照读操作时访问记录的版本链的过程。这样使不同事务的 读-写 、 写-读 操作并发执行,从而提升系统性能。

以上就是一文带你搞懂MySQL的MVCC机制的详细内容,更多关于MySQL MVCC机制的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文