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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS SQL Server,基础系列 2核4GB
简介: 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
相关文章
|
1月前
|
存储 SQL 关系型数据库
MySQL进阶突击系列(03) MySQL架构原理solo九魂17环连问 | 给大厂面试官的一封信
本文介绍了MySQL架构原理、存储引擎和索引的相关知识点,涵盖查询和更新SQL的执行过程、MySQL各组件的作用、存储引擎的类型及特性、索引的建立和使用原则,以及二叉树、平衡二叉树和B树的区别。通过这些内容,帮助读者深入了解MySQL的工作机制,提高数据库管理和优化能力。
|
20天前
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
MySQL事务日志-Undo Log工作原理分析
|
16天前
|
SQL 关系型数据库 MySQL
MySQL派生表合并优化的原理和实现
通过本文的详细介绍,希望能帮助您理解和实现MySQL中派生表合并优化,提高数据库查询性能。
56 16
|
17天前
|
SQL 关系型数据库 MySQL
MySQL派生表合并优化的原理和实现
通过本文的详细介绍,希望能帮助您理解和实现MySQL中派生表合并优化,提高数据库查询性能。
33 7
|
1月前
|
缓存 关系型数据库 MySQL
MySQL 索引优化与慢查询优化:原理与实践
通过本文的介绍,希望您能够深入理解MySQL索引优化与慢查询优化的原理和实践方法,并在实际项目中灵活运用这些技术,提升数据库的整体性能。
107 5
|
1月前
|
SQL 存储 缓存
MySQL进阶突击系列(02)一条更新SQL执行过程 | 讲透undoLog、redoLog、binLog日志三宝
本文详细介绍了MySQL中update SQL执行过程涉及的undoLog、redoLog和binLog三种日志的作用及其工作原理,包括它们如何确保数据的一致性和完整性,以及在事务提交过程中各自的角色。同时,文章还探讨了这些日志在故障恢复中的重要性,强调了合理配置相关参数对于提高系统稳定性的必要性。
|
1月前
|
SQL 存储 关系型数据库
MySQL进阶突击系列(01)一条简单SQL搞懂MySQL架构原理 | 含实用命令参数集
本文从MySQL的架构原理出发,详细介绍其SQL查询的全过程,涵盖客户端发起SQL查询、服务端SQL接口、解析器、优化器、存储引擎及日志数据等内容。同时提供了MySQL常用的管理命令参数集,帮助读者深入了解MySQL的技术细节和优化方法。
|
4天前
|
缓存 关系型数据库 MySQL
【深入了解MySQL】优化查询性能与数据库设计的深度总结
本文详细介绍了MySQL查询优化和数据库设计技巧,涵盖基础优化、高级技巧及性能监控。
58 0
|
1月前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
61 3
|
1月前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
79 3

相关产品

  • 云数据库 RDS MySQL 版