MySQL进阶突击系列(05)突击MVCC核心原理 | 左右护法ReadView视图和undoLog版本链强强联合

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: 2024年小结:感谢阿里云开发者社区每月的分享交流活动,支持持续学习和进步。过去五个月投稿29篇,其中17篇获高分认可。本文详细介绍了MySQL InnoDB存储引擎的MVCC机制,包括数据版本链、readView视图及解决脏读、不可重复读、幻读问题的demo演示。

2024小结:在写作分享上,这里特别感谢阿里云开发者社区提供平台,支持大家持续学习分享交流,共同进步。社区诚意满满的干货,让大家收获满满。

对我而言,珍惜每一篇投稿分享,每一篇内容字数大概6000字左右,加上画图,以及案例demo代码编写、实战,撰稿时长平均3小时左右。由于年底工作特别忙,晚上下班回家,有时候娃已经睡着了,如果娃没睡还得陪娃玩直到她睡着才有空继续写作。每天空闲时间非常少,经常一篇文章从周一写到周末才能完成。

近5个月以来投稿并不多,仅29篇。好在其中超过一半,多达有17篇得到平台认可,认定为高分内容。2014是收获的一年。感恩感谢!新的一年,争取有更多时间,和大家交流学习分享,包括家庭、日常、职场其他非技术性内容。


一、前言背景

二、通俗演义-MVCC多版本并发控制核心原理

2.1 解密-基于undoLog实现的数据版本链

2.2 弯弯绕绕看不懂的readView视图-一句话总结看懂

三、MVCC解决脏读、不可重复读、幻读问题demo详解

3.1 验证MVCC解决脏读、不可重复读问题【并发事务一个重复查+另一个改】

3.2 验证MVCC解决幻读、不可重复读问题【并发事务一个重复查,一个新增】

四、脏写是什么?如何解决


期待可以写一篇2024总结,聊聊日常生活、职场等非技术内容。


一、前言背景


    之前系列4文章说过,MySQL InnoDB存储引擎,默认事务隔离级别是可重复读repeatable-read。我们可通过命令查看:SELECT @@SESSION.tx_isolation;



     而且也说到,MySQL的可重复读事务隔离级别,可以解决脏读、幻读、不可重复读三大事务并发问题。当时也留了一个思考题:MySQL是如何做到的?答案是MVCC+锁。核心在于MVCC。

     那MySQL如何让实现一个事务多次读,不受另一个事务的增、改、删的影响。带着这个问题,我们一步步解密MySQL的MVCC多版本并发控制核心机制。


二、通俗演义-MVCC多版本并发控制核心原理


    MVCC,全称是Multi Version Concurrency Control多版本并发控制。MySQL innoDB存储引擎,在新增修改删除数据的时候,并没有真正用新数据直接更新覆盖,而是采用版本链方式去保存数据修改记录。每个读事务,对应一个版本的数据快照。每个写事务,在事务提交之前,该事务内做的任何更新操作未提交之前,其他任何事务不可见该更新。

    举一个通俗的案例,也是我们日常实践的数据版本管理。比如用户信息user (id,name,age,city,cs_level)修改,我们不会简单的直接进行update,通常会进行数据历史记录。比如最简单的user表增加一个is_valid字段,利用主键id自增的特性,把它当做用户信息更新版本号。每次更新用户信息,将原信息is_valid置为false。然后用新信息去构建一条is_valid=true数据。这样就可以完成版本记录追溯。

     MVCC的数据快照、数据版本链就是是类似效果。但是在并发事务里读写,具体原理会复杂很多。


2.1 解密-基于undoLog实现的数据版本链


    在MySQL表里,有2个隐藏的字段,一个是事务的ID:trx_id,这个事务id就是最近一次更新该数据的事务id;另一个是回滚指针:roll_pointer ,该指针指向的就是更新该数据之前的undoLog,可以通俗理解为:修改前数据。据此,隐藏的事务id,和回滚指针的意义,一目了然,一个表示哪个事务更新了该数据,一个表示该数据更新前的样子。


     比如下图:事务trx_id=98的操作,新增了name=【拉丁解牛说技术】的这行数据。新增数据的时候,roll_pointer是空。此后,事务99对该数据进行修改,把name改为=【老牛】。此时如下图,回滚指针指向老版本事务98的数据。这样数据版本链清晰可见。



     另外说一下,在MVCC里只有更新、删除、新增操作有让事务ID新增,查询是不会让数据事务发生变化。


2.2 弯弯绕绕看不懂的readView视图-一句话总结看懂


     readview顾名思义是读视图,当开启一个事务,MySQL会根据当前事务隔离级别,给你这个事务开启独立的review视图空间。这里很多博文在讲解readview机制时候,会对当前最大max_trx_id最小,min_trx_id,当前事务this_trx_id,视图开启时候活跃的事务id组等多个事务id进行比较说明,讲的非常细。不过这里的判断规则说这么细,如果读者些微没跟上,或者失去耐心,可能就错失理解掌握readview的核心机制。


     我们坚持大道至简的方法,总结readview视图核心机制,最直接一句话:每个事务只能读到对应事务隔离级别的数据。

     我们简单举例说一下:

     如下图,当前事务隔离级别是可重复读RP,并发事务100要重复多次查询,事务101 要更新name为【zhangsan】。事务并发开始前如下:



接下来具体操作:

1、事务id=100进行查询,首先查到了事务99的数据,发现自己的事务ID100比99大,直接返回读到该数据【老牛】。

2、接下来事务id=101,把数据更新为【zhangsan】并提交更新trx_id=101,回滚指针指向了之前trx_id=99的老数据。



3、事务100,继续重复读,这时候读到了trx_id=101的最新数据,发现比自己的trx_id=100还大。在当前每个事务只能读到对应事务隔离级别的数据原则下,而且当前事务隔离级别是可重复读RP。按规则,不好意思,这个101事务修改的数据我不能读,得继续遍历undoLog版本链,找到了下一个事务id=99的数据【老牛】,发现99< 100,非常好,符合事务隔离级别要求。那这次重复读,读的还是之前的数据【老牛】。


      同样道理,如果是事务隔离级别在读已提交、读未提交,判断规则也只是对应判断当前自己的事务ID与读到的数据事务id大小关系是否满足事务隔离级别即可。

      当然,这里核心再详细展开确实有很多细节,比如读已提交隔离级别下,每次查询,就是开启一个新的readview。这个和可重复读隔离级别不一样。

      理解了MVCC核心原理,我们设计场景,在InnoDB默认的事务隔离级别「可重复读RP」下,一步步实践验证解决脏读、幻读、不可重复读问题。


三、MVCC解决脏读、不可重复读、幻读问题demo详解


      新建一个user_mvcc_demo表,多个事务并发读、修改name值、以及新增写入,来具体验证mvcc核心机制。


CREATE TABLE user_mvcc_demo (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(16) DEFAULT NULL,
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
-- 新增一条数据,id=1
insert into user_mvcc_demo(name)values ('拉丁解牛说技术001');


3.1 验证MVCC解决脏读、不可重复读问题【并发事务一个重复查+另一个改】

     之前说过:脏读,特指的就是一个事务里select查询到另一个事务update语句未提交的脏数据场景。

     本demo模拟场景:事务1里面多次重复查询id=1的name值,而事务2并发修改了name值并提交。

     预期结果:在事务1里每次查询都是数据快照‘拉丁解牛说技术001’;事务2提交事务前、后,事务1均读不到name的新值zhangsan。


具体如下:

事务1 SQL:

begin;

select * from user_mvcc_demo where id=1;

select now();-- 时间 2025-01-03 16:04:46

select * from user_mvcc_demo where id=1;

多次查询

.....

事务2 SQL:

begin;

select * from user_mvcc_demo where id=1;

select now();-- 时间 2025-01-03 16:04:46

update user_mvcc_demo set='zhangsan' where id=1;-- 更新name 为zhangsan

select * from user_mvcc_demo where id=1;

select * from user_mvcc_demo where id=1;--- 此时未提交,但事务1读不到脏数据zhangsan

commit;--提交后,事务1仍然读不到新快照数据zhangsan

.....

实践结果:

      在事务1内,多次查结果都是:拉丁解牛说技术001。实际上,在另一个事务2, 时间 2025-01-03 16:04:26 已经修改name为zhangsan。


     且事务2提交事务后,在事务1里,继续多次查询,查到的也是事务1开启事务后,对应的快照数据:拉丁解牛说技术001,验证MVCC解决脏读、不看重复读问题完成。


如下图两个会话:



3.2 验证MVCC解决幻读、不可重复读问题【并发事务一个重复查,一个新增】


      系列3具体说过,不可重复读问题:一个事务读到了另一个事务已提交的数据。主要针对的是一个事务里select到另一个事务update或者delete语句的更新结果。

      而幻读:一个事务读到另一个事务新增的数据,特指一个事务里select查询查到了另一个事务insert数据。


      本demo模拟场景:事务1里面多次重复查询全表,而事务2新增了一条数据并提交。

      预期结果:在事务1里每次查询都只有一条数据;事务2提交事务前、后,事务1均读不到新增那条数据。

仍然是user_mvcc_demo表,里面只有一条id=1,name=‘zhangsan’;的数据。


事务1:重复读select * from user_mvcc_demo 。

事务2:新增1条数据。insert into user_mvcc_demo(name)values('拉丁解牛说技术');

具体如下:


事务1 SQL:

begin;

select * from user_mvcc_demo;

select now();-- 时间 2025-01-03 16:40:45

select * from user_mvcc_demo;

多次查询

.....

事务2 SQL:

begin;

select * from user_mvcc_demo;

select now();-- 时间 2025-01-03 16:40:22

insert into user_mvcc_demo(name)values('拉丁解牛说技术');;-- 新增了一条'拉丁解牛说技术'的数据

select * from user_mvcc_demo;

commit;--提交后,事务1仍然读不到新快照数据'拉丁解牛说技术'

.....


      实践结果,与预期一致:在事务1里每次查询都只有一条数据zhangsan;而事务2提交事务前、后,事务1均读不到新增那条数据'拉丁解牛说技术'。



四、脏写是什么?如何解决

      脏读,说过很多次,脏写大家听的很少。所谓脏写:就是并发事务更新同一个数据,比如2个并发事务修改id=1,的name值,之前name值是【拉丁解牛说技术】。事务1此时想改成zhansan并提交,而事务2改成lisi,但是中途回滚了,回滚为老数据【拉丁解牛说技术】。对于事务1来说,这就是脏写问题。

      MySQL是通过锁机制来保障并发事务串行化执行,避免事务并发脏写问题。简单的说,更新事务读到数据后,需要先加锁,加锁成功才能开始执行更新事务。未拿到锁的事务,需要等待锁。这个和java并发编程的锁机制类似。

      篇幅有限,具体锁相关类型、以及具体场景锁分析,我们下一篇继续分享。


推荐阅读拉丁解牛相关专题系列(欢迎交流讨论,搜索:拉丁解牛):

1、JVM进阶调优系列(3)堆内存的对象什么时候被回收?

2、JVM进阶调优系列(2)字节面试:JVM内存区域怎么划分,分别有什么用?

3、JVM进阶调优系列(1)类加载器原理一文讲透

4、JAVA并发编程系列(13)Future、FutureTask异步小王子

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7月前
|
存储 关系型数据库 MySQL
MySQL数据库进阶第六篇(InnoDB引擎架构,事务原理,MVCC)
MySQL数据库进阶第六篇(InnoDB引擎架构,事务原理,MVCC)
|
5月前
|
算法 关系型数据库 MySQL
一天五道Java面试题----第七天(mysql索引结构,各自的优劣--------->事务的基本特性和隔离级别)
这篇文章是关于MySQL的面试题总结,包括索引结构的优劣、索引设计原则、MySQL锁的类型、执行计划的解读以及事务的基本特性和隔离级别。
|
5月前
|
缓存 关系型数据库 MySQL
MySQL调优秘籍曝光!从索引到事务,全方位解锁高可用秘诀,让你的数据库性能飞起来!
【8月更文挑战第6天】MySQL是顶级关系型数据库之一,其性能直接影响应用的高可用性与用户体验。本文聚焦MySQL的高性能调优,从索引设计到事务管理,逐一解析。介绍如何构建高效索引,如联合索引`CREATE INDEX idx_order_customer ON orders(order_id, customer_id);`,以及索引覆盖查询等技术。
87 0
|
8月前
|
存储 关系型数据库 MySQL
Mysql学习--深入探究索引和事务的重点要点与考点
Mysql学习--深入探究索引和事务的重点要点与考点
MySQL沉浸式面试:隔离级别、锁、索引原理连环炮你扛得住吗?
基础篇主要是侧重基础知识,原理篇是有一定基础后的递进,通过学习本篇,不仅可以进一步了解MySQL的各项特性,还能为接下来的容灾调优打下坚实的基础。 现在,就让我们继续跟随阿柴进行这场沉浸式面试吧。
|
数据建模 索引
白话Elasticsearch57-数据建模之实现悲观锁并发控制的三种方式(未成功)
白话Elasticsearch57-数据建模之实现悲观锁并发控制的三种方式(未成功)
102 0
|
存储 SQL 缓存
滴滴滴,请看MYSQL事务的四大特征(ACID)的实现原理:晓其原理而通其实现。
滴滴滴,请看MYSQL事务的四大特征(ACID)的实现原理:晓其原理而通其实现。
|
SQL Java 程序员
数据库关于事务的详解分析(全)包含面试常问的细节
目录前言1. 定义2. 特性 前言 在敲代码的时候,提交事务、事务回滚等,事务二字确官方笼统,不知道具体什么意思 这篇文章将为你解答疑惑 1. 定义 事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。事务通常由高级数据库操纵语言或编程语言(如SQL,C++或Java)书写的用户程序的执行所引起,并用形如begin transaction和end transaction语句(或函数调用)来界定。 事务的提交是指事务
157 0
数据库关于事务的详解分析(全)包含面试常问的细节
|
SQL 关系型数据库 MySQL
学习MySQL的第六天:事务(基础篇)
事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
73 0
学习MySQL的第六天:事务(基础篇)
|
SQL 关系型数据库 MySQL
数据库的事务四大特性&&隔离级别总结(面试高频)
数据库的事务四大特性&&隔离级别总结(面试高频)
217 0
下一篇
开通oss服务