手把手带你探索 MySQL 事务的隔离(下)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 手把手带你探索 MySQL 事务的隔离

还有一点就是 MySQL 有一个参数设置值 autocommit,默认是 1 表示的是事务自动提交,每一个查询都是一个单独的事务自动提交,就像图中事务 C,update 就是一个单独的事务,更新完自己提交。当然你可以使用显式的 begin/commit。


让我们把目光对准上面的图,事务 B 的查询结果 K 是 3,事务 A 的查询结果 K 是 1,你是不是想骂我?你先别急着骂,让我们来看下结果。打开三个控制台。


1668577486260.jpg


好吧我没瞎说吧,现在你可以开始骂我了,你不是说在可重复读隔离的情况下,当前事务执行过程中看到的视图始终是启动时的视图嘛。


在 MySQL 中有两个视图概念:


一个是 view。也就是创建视图,语句是 create view xxx () as....., 它并不是一个真实的表,它的内容是由存储在数据库中进行查询操作的 SQL 语句定义的。


另一个就是 InnoDB 在实现 MVCC 是用到的一致性读视图 (consistent read view)。用于支持读已提交 RC (Read Committed) 和可重复读 RR (Repeatable Read) 的隔离级别实现。


InnoDB 每一个事物都存在一个事务唯一 ID,叫做 transaction id, 它是一个事务在启动的时候 InnoDB 向系统申请的,严格按照递增的形式生成。


每一行数据都有对应多个版本,每次通过事务更新的数据都会生成新的版本,然后把 transaction id 赋值给这个版本事务 ID,记为 row trx_id, 同时,为了之后可以恢复数据,我们需要保留旧的数据版本。也就是说在当前最新的版本中,我们可以随时获取旧的数据。下图对应修改一行数据的版本图。


1668577497405.jpg

                                     注:图片来源极客时间


从上图可以知道,最新版本是 V4, 且是由事务 transaction id =25 更新的,所以对应此行数据的数据版本 row trx_id =25。


按照可重复读的定义。一个事务启动的时候,可以看到所有已经提交的事务,但是接下来的事务对它来说是不可见的。所以对于一个事务启动的时候,如果一个事务在我启动时刻之前生成的,我就认,如果在我启动之后生成的,我就不认,我必须要找到它的上一个版本。有点渣男的嫌疑。如果上一个版本还不可见,那就继续往前面找,当然在这个过程中,自己更新的东西得认。


在实现上,InnoDB 为每一个事务创建了一个数组。事务中 ID 最小的称为低水位,当前系统中已经创建的事务 ID 最大值加 1 就是高水位。这个数组和水位图,就组成了当前事务的一致性图。数据版本可见性,就是基于数据版本数组和水位视图的比较而得到的结果。


这个视图数组把所有的 row trx_id 分为以下几种情况


1668577513037.jpg

                                       注:图片来源极客时间


如果当前事务启动的时候,一个数据版本 (row trx_id) 落在绿色部分,表示这个事务是已提交的版本或者是自己生成的,可见。


如果落在红色部分,说明这个版本是由将来启动的事务生成的,肯定不可见。


如果落在黄色,又分为两种情况。如果 row trx_id 存在数组中,说明, 此时版本数据还未提交事务,不可见,如果不在,说明已经提交事务了,可见。  


接下来可以分析为什么上面 A 查询的 k=1,B 查询的 k=3 了。


1668577537254.jpg


查看上图,我们假设当前有一个活跃的事务 99,目前我们更新的这一行数据的数据版本 row trx_id=90,此时系统存在 4 个事务,那么对于 A 的视图数组就是 [99,100],B 的视图数组就是 [99,100,101],C 的活跃数组就是 [99,100,101,102]。当前版本 101。


从图中知道,事务 A 先启动,接着事务 B,最后事务 C,但是第一个有效更新的是事务 C, 设置 k 为 2 (k=1+1), 然后是事务 B 把 k 设置为 3 (k=2+1), 接着到 A 查询了,他的视图数组是 [99,100], 此时数据版本 (1,3) 也就是 row trx_id=101 , 一对比,发现这货在高水位。不可见,再往回追,(1,2) 数据版本 row trx_id=102, 我去还是在高水位,不可见,最后追到 (1,1) 数据版本 row trx_id=90,比水位低,可见,一看上面的值 K=1, 所以查询结果就等于 1。


这样一个流程走下来,虽然中间修改了数据,数据版本也发生了变化,但是对于事务 A 来说,对他都是不可见的,所以看到的结果还是之前的数据。


到这里还有一个疑问,也就是开头的,事务 B 是在事务 C 之前开启事务的啊,对于事务 B 来说,事务 C 的操作对他来说是不可见的啊,事务 B 为什么获取的值是 2,然后再 2 的基础上更新了,它启动事务的时候 k 可不等于 2。


是的道理是没错,前提是如果事务 B 在更新之前先查询一遍数据,那么之后在它更新完数据以后,再次查询得到的值将会是 1, 而不是 3。这里运用到一条规则,更新数据的时候都是先读后写的。而这个读,只能读当前的值,叫做 "当前读"。对于 B 来说,在它更新之前,并没有执行读操作,所以在更新的时候,不能再在历史版本上直接更新了,否则 C 的更新将会丢失。所以 B 在更新的时候当前读 (1,2),更新之后 (1,3),当前的版本也就是 row trx_id=101 了。等到他查询的时候,一看当前版本号 101 就是自己,所以查询的时候就等于 3。


下面可以试验一下当 B 在更新之前查询一遍数据,然后更新数据再查询,符合上面所说的,得到的值就是 1。此时运用的就是当前读。


1668577547322.jpg


最后如果改动一下 C 事务中的执行,结果又是什么?\


1668577559057.jpg


这时候的事务 C 不再是自动提交事务,也是显式提交。事务 C' 更新语句,先获取写锁,但是 C' 事务还未提交,此时事务 B 是当前读,必须读取当前最新版本,而且必须加锁,等到 C 提交了,事务 B 才得以继续进行。那么 B 查询的结果不会变依然是之前的 3,而事务 C 的事务已经提交了,在读隔离的情况下,是在创建新视图之前,所以 A 的结果 k=2。


可重复读的核心就是一致性读。而事务更新数据的时候,只能用当前读,如果当前要读的记录的行数被其他事务占用的时候,就需要等待。


而读提交的逻辑和可重复读的逻辑类似,最主要的区别在于:


在可重复读隔离级别下,只需要在事务开始的时候创建一致性视图,之后事务里的其他查询都共用这个一致性视图;


在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图



相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6天前
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
13天前
|
SQL 关系型数据库 MySQL
MySQL基础:事务
本文详细介绍了数据库事务的概念及操作,包括事务的定义、开启、提交与回滚。事务作为一组不可分割的操作集合,确保了数据的一致性和完整性。文章还探讨了事务的四大特性(原子性、一致性、隔离性、持久性),并分析了并发事务可能引发的问题及其解决方案,如脏读、不可重复读和幻读。最后,详细讲解了不同事务隔离级别的特点和应用场景。
58 4
MySQL基础:事务
|
1月前
|
SQL 关系型数据库 MySQL
Mysql原理与调优-事务与MVCC
【8月更文挑战第19天】
|
1月前
|
存储 SQL 关系型数据库
深入解析MySQL事务机制和锁机制
深入解析MySQL事务机制和锁机制
|
1月前
|
算法 关系型数据库 MySQL
一天五道Java面试题----第七天(mysql索引结构,各自的优劣--------->事务的基本特性和隔离级别)
这篇文章是关于MySQL的面试题总结,包括索引结构的优劣、索引设计原则、MySQL锁的类型、执行计划的解读以及事务的基本特性和隔离级别。
|
1月前
|
SQL 关系型数据库 MySQL
MySQL 事务回滚。在执行删除、更新等操作时,防止误操作
MySQL 事务回滚。在执行删除、更新等操作时,防止误操作
57 2
|
20天前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
56 0
|
20天前
|
C# Windows 监控
WPF应用跨界成长秘籍:深度揭秘如何与Windows服务完美交互,扩展功能无界限!
【8月更文挑战第31天】WPF(Windows Presentation Foundation)是 .NET 框架下的图形界面技术,具有丰富的界面设计和灵活的客户端功能。在某些场景下,WPF 应用需与 Windows 服务交互以实现后台任务处理、系统监控等功能。本文探讨了两者交互的方法,并通过示例代码展示了如何扩展 WPF 应用的功能。首先介绍了 Windows 服务的基础知识,然后阐述了创建 Windows 服务、设计通信接口及 WPF 客户端调用服务的具体步骤。通过合理的交互设计,WPF 应用可获得更强的后台处理能力和系统级操作权限,提升应用的整体性能。
42 0
|
20天前
|
存储 关系型数据库 MySQL
MySQL 中的事务存储引擎深入解析
【8月更文挑战第31天】
12 0
|
27天前
|
存储 关系型数据库 MySQL
深入MySQL:事务日志redo log详解与实践
【8月更文挑战第24天】在MySQL的InnoDB存储引擎中,为确保事务的持久性和数据一致性,采用了redo log(重做日志)机制。redo log记录了所有数据修改,在系统崩溃后可通过它恢复未完成的事务。它由内存中的redo log buffer和磁盘上的redo log file组成。事务修改先写入buffer,再异步刷新至磁盘,最后提交事务。若系统崩溃,InnoDB通过redo log重放已提交事务并利用undo log回滚未提交事务,确保数据完整。理解redo log工作流程有助于优化数据库性能和确保数据安全。
108 0

热门文章

最新文章