MySQL MVCC 原理硬核解析,阿里面试官看了都直呼内行

来源:mikechen的互联网架构

MVCC 是提高 MySQL 数据库性能和并发性能的关键。

MVCC 是一种强大的数据库并发控制机制,它可以解决并发访问数据库时的很多问题,例如死锁、快照读、读写阻塞等。

当出现读写冲突时,MVCC 在不加锁、或尽量减少锁使用的情况下,也能高效处理读写冲突,实现非阻塞并发读。

大家好,我是 mikechen。

本文主要详解 MVCC,MVCC 是数据库性能优化提升的关键,也是大厂面试高频,非常重要。为方便大家系统学习,我已将本文归纳到《MySQL面试题专项突击》PDF,约 20 万字,全部是大厂高频必考,拉到文末可自取。


注:MyIsam 不支持事务,本文围绕 InnoDB 引擎来讲


01
   什么是 MVCC

MVCC 又称为多版本并发控制,英文全拼 Version Concurrency Control

MVCC 为每一个事务创建多个数据版本,每个版本对应一个特定时间点的数据库状态。

不同事务之间,彼此不干扰,基于各自的时间点,进行读取和写入操作。

想要更好掌握 MVCC ,有必要先了解当前读快照读。


02
   当前读

当前读Current Read)读取的是最新数据,而不是历史的数据。

加锁的 SELECT、或对数据进行增删改,都会进行当前读

当前读常用于需要读取最新数据状态的场景。

例如,库存管理系统、实时监控系统等。

当前读的特点:

  • 事务在读取数据时,每次都会读取当前的最新数据版本。


  • 事务在读取数据时,不允许写入;在写入数据时,不允许其他事务读取。


  • 当前读允许读取当前事务未提交的数据,在并发环境中,可能导致一致性问题。


03
   快照读

快照读(Snapshot Read,又称为一致性读 。

快照读读取的是快照数据,不加锁的普通 SELECT ,都属于快照读。

快照读用于需要事务隔离和数据一致性的场景。

例如,报表生成、数据分析和历史查询等。

快照读的特点:

  • 在读取数据时,会读取创建事务时的数据版本,不受其他事务影响。


  • 由于它只读取已提交的数据版本,确保了数据的一致性和隔离


  • 提供了事务开始时的数据一致性视图,避免了并发冲突和未提交数据的影响。


快照读和当前读的区别:

  • 快照读:仅读取已提交的数据版本,不读取其他事务未提交的数据。

  • 当前读:读取的是最新数据,而不是历史的数据。


04   MVCC 的主要作用


MVCC 的作用是提高数据库的并发性能。

  • 并发读-写时:读操作不阻塞写操作,写操作也不会阻塞读操作,避免了同一数据在不同事务之间的竞争。


  • 并发访问数据库时:解决了幻读、不可重复读等事务隔离问题。


MVCC 可以适应不同的应用需求,因为它允许读未提交读已提交可重复读串行化不同的事务隔离级别。

由于数据库不允许多个事务同时写入相同的数据,故 脏写 不会出现。

我们来看下,三种常见的 MySQL 数据库并发场景:

  • 读 - 读:通常不会存在任何问题,也不需要并发控制。


  • 读 - 写:有线程安全问题,可能会造成脏读,幻读,不可重复读,需要 MVCC 控制。


  • 写 - 写:有线程安全问题,可能会存在更新丢失问题,例如第一类更新丢失, 第二类更新丢失等。


对比下,

  • 没有引入 MVCC 机制前

    仅 读-读 可以并发执行,读-写、写-写 都会阻塞,并发性能较差。


  • 引入 MVCC 机制后

    只有 写-写 之间相互阻塞,其他操作都可以并行,并发性能较高。


05
   MVCC 的实现原理

MVCC 机制的实现核心是隐藏列事务链 和 ReadView

隐藏链

Innodb 为每行记录都添加了三个隐藏字段:row_id、trx_id、roll_pointer。

以确保数据的一致性和事务管理。

事务链

每次对记录进行 update 或者 delete 操作时,都会记录一条 undo log 信息。

每条 undo log 信息中,都有一个 roll_pointer 属性。

通过 roll_pointer ,将这些 undo 日志串成一个链表,构成数据的事务链,如下图所示

由于一个记录会被一堆事务进行修改,在一个记录中,就会存在多个版本的 Undo log 信息。

那么,这么多个版本的Undo log 信息,事务应该看到哪个

这里就要了解 ReadView 机制。

ReadView

ReadView,又称为读视图。

ReadView 是用于控制事务读取数据的逻辑视图,我们可以理解它为数据库中某一个时刻所有未提交事务的快照。

通过 ReadView ,可以确定一个事务能够看到哪些数据版本。

每个事务都会创建一个自己独有的 ReadView,记录当前活跃且未提交事务。

ReadView 的几个重要参数:

当事务在进行快照读时,会生成一个 ReadView 来进行可见性判断,去判断哪个版本的数据可读

可见性判断是由可见性算法来确定的。

可见性算法的规则:

不同隔离级别下,生成 ReadView 的逻辑:

串行化不需要版本链,每次只有一个事务执行;读未提交则直接取版本链最新的版本数据就行了。

只有在读已提交(RC)和可重复读(RR)的隔离级别下,才会使用 MVCC 机制。

  • 读已提交(RC)时:每次查询时,都会重新生成 ReadView。


  • 可重复读(RR)时:在当前事务第一次查询时,生成 ReadView ,然后一直沿用,直至事务提交,以保证可重复读。


读已提交(RC) 和 可重复读(RR) 的区别是,生成 ReadView 的时机不同。

接下来,我们再来看一个具体的示例,加深理解。


04
   MVCC 的实现示例

假设有一张表,表中有姓名、性别、年龄:











CREATE TABLE `user` (  `id` bigint NOT NULL COMMENT '主键',  `name` varchar(20) DEFAULT NULL COMMENT '姓名',  `sex` char(1) DEFAULT NULL COMMENT '性别',  `age` varchar(10) DEFAULT NULL COMMENT '年龄',  `url` varchar(40) DEFAULT NULL,  PRIMARY KEY (`id`),  KEY `suf_index_url` (`name`(3)) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

我们在表中插入数据:



INSERT INTO `user` (`id`, `name`, `sex`, `age`, `url`) VALUES ('1', 'mike', '1', '18', 'https://mikechen.cc');

插入该记录的事务 id 为 60:

事务 id 为 80、120 的两条事务,现在要对这条记录进行 UPDATE 操作:

版本链在每次进行 update 操作时,将每次的操作详细记录在 undo log 中。

每条 undo log 中,都记录了 rol_pointer 信息,通过 roll_pointer 进行关联,可以构成数据的版本链

版本链的头节点就是当前记录最新的值。

如图:

在各个版本中,包含了生成该版本时对应的事务 id。

利用这个记录的版本链来控制并发事务访问相同记录的行为,即 MVCC 机制


总结
   

本文重点介绍 MVCC 机制,包括 MVCC 的概念、作用、工作原理、实现示例等。

MVCC 提高数据库性能和并发性能的关键既是面试高频、也是必知必会,非常重要。

请使用浏览器的分享功能分享到微信等