“时光机”与“多维视界”⭐️MySQL中原子性与隔离性的科幻大片

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: “时光机”与“多维视界”⭐️MySQL中原子性与隔离性的科幻大片

“时光机”与“多维视界”⭐️MySQL中原子性与隔离性的科幻大片

上篇文章 我们描述完MySQL的持久性等知识点,本篇文章来描述MySQL的原子性与隔离性知识

”时光机“指的是实现原子性的undo log,”多维视界“指的是实现并发场景下读不加锁的MVCC,一起往下看看吧~

内容脑图如下:

image.png

MySQL中支持事务的只有Innodb,因此本篇文章描述的原理也是Innodb实现原子性的原理

事务的原子性:一组事务要么都成功,要么都失败

在事务中可能存在一些写操作(新增/修改/删除),有的写操作会执行成功,但是后续写操作执行失败就会导致事务需要回滚,那么执行成功的写操作就要退回到原来的“版本”

为了方便回滚,Innodb使用undo log来记录每次写操作修改的内容,根据undo log能够快速退回到原始版本

每一行记录中存在隐藏列 roll_pointer 回滚指针,它指向上一次写操作产生的undo log,undo log中也存在类似回滚指针的列,它能够找到它上一次写操作产生的undo log

image.png

记录与undo log就会形成一条”版本链“,这样在遇到回滚时便能快速的回到原来的版本以此来实现原子性(回滚)

image.png

注意:undo log只是记录修改的数据,并不是完整数据,图中只是为了方便展示,图中执行SQL顺序为从下到上

隔离性

为了防止并发事务交叉执行导致的数据不一致等并发问题,MySQL会根据不同的隔离级别来解决不同的隔离性问题

隔离性问题与隔离级别

不同隔离级别下会使用不同的方案来达到隔离性,我们先来复习一下隔离性问题和隔离级别:

隔离性问题

脏写:A事务和B事务同时修改一行记录,A可以覆盖B修改后未提交的记录,后续B又进行回滚

脏读:A事务读取B事务未提交的记录,然后B事务回滚,导致A事务读取的数据不一致

不可重复读:A事务前后两次执行同一个查询,返回的结果数据不同,B事务在此期间进行修改

幻读:A事务前后两次执行同一个查询,返回的结果数量不同,B事务在此期间进行新增

幻读强调结果数量不同,不可重复读强调结果数据不同

隔离级别

读未提交(RU):可以读到其他事务未提交的记录,会出现脏读、不可重复读、幻读

读已提交(RC):只能读到其他事务提交的记录,会出现不可重复读、幻读

可重复读(RR):Innodb默认,不会出现不可重读的情况,可能出现部分幻读

串行化(S):所有隔离性问题都会不发生

随着隔离性的严格,性能也会降低:RU > RC > RR > S

MVCC

那么在undo log的版本链中是如何做到有的事务能够看到该版本、有的事务看不到该版本的呢?

其实这是通过MVCC(Multi Version Concurrency Control 多版本并发控制)来实现的,MVCC在这种读写并发的场景下,使用无锁机制读取到满足隔离级别的数据

对于事务来说,当事务中出现第一条写操作的语句时就会生成事务ID,这个事务ID是全局递增的

当使用MVCC时会生成read view来判断当前事务能够读到哪个版本上的记录

read view中就包含事务ID相关的属性,便于判断事务能读到哪个版本

  1. 最小事务ID:read view生成时活跃事务中最小的id
  2. 最大事务ID:read view生成时活跃事务中最大的id
  3. 活跃事务列表:read view生成时存在的活跃事务

image.png

前面说到:事务ID是全局自增的,这表示可以根据事务ID查看哪个事务先执行、哪个事务后执行、哪个事务已提交/回滚、哪个事务当前还在执行中

使用read view与版本链上的最新记录进行判断(判断规则如下):

  1. 如果read view的最小事务id大于该记录的事务id,说明该记录的事务已经提交了,那么是可以查看的
  2. 如果read view的最大事务id小于该记录的事务id,说明该记录的事务在当前事务开启后才开始,那么无法查看
  3. 如果该记录id在read view最小、大事务id之间,那么要查看当前活跃事务列表,如果在列表中,说明该列的事务活跃(还未结束),则无法查看;反之则可以查看
  4. 如果记录的事务id是当前事务,则可以查询(当前事务执行的,当然可以查看)

当该版本的记录无法查看时,就会遍历版本链继续向下一条记录使用该规则

比如read view 最小事务id:20 最大事务id:30 活跃事务列表:20,22,24,27,30

当记录的事务id为19时,小于最小事务id,说明它已经提交可以查看

当记录的事务id为31时,说明生成read view时,它还没开启事务,不能查看

当记录的事务id为24时,在最小19、最大30之间,要查看活跃事务列表中是否存在,存在则不能查看

当记录的事务id为23时,活跃事务列表中不存在则可以查看

分析读相关解决隔离性问题的方案

在了解隔离性问题与隔离级别后,我们来进行一一分析:

脏写发生在写写的场景下会破坏数据的一致性,禁止这种情况发生,会使用行锁保证(锁相关知识下篇文章再说)

脏读发生在读写的场景下会读的数据不一致,也要禁止这种情况发生,正常情况读写场景下需要保证数据一致性的办法就是加行锁互相阻塞

RC使用MVCC机制,在进行读操作时使用read view查看版本链上的记录是否可读,如果事务已提交(事务id会比read view最小事务id要小),以此通过无锁机制达到防止脏读的效果

不可重复读侧重的是有其他事务在本次事务多次查询过程中修改这个数据的内容(幻读侧重数量)

RC下,由于每次读都生成read view,前后两次读生成的read view不同,能看到的数据也就可能不同

RR使用MVCC机制,在同个事务中只有第一次读生成read view,后续读都使用这个read view,以此来达到防止不可重复读,和大部分幻读

幻读案例

为啥说RR可以防止大部分幻读呢?

RR通过read view可以看到的数据是不变的,因此可以防止幻读

那为啥说是防止大部分幻读而不是所有幻读呢?

还记得第四条规则(如果记录的事务id是当前事务,则可以查询)吗?

在某些场景下就会导致特殊幻读,来看一个案例:

时间点 T1 T2
1 begin;
SELECT * FROM s where id < 30;
2 insert into s (id,s_name,s_age) value (18,'caicai',18);
3 update s set s_name = '菜菜的后端私房菜' where id = 18;
4 SELECT * FROM s where id < 30;
commit;

T1 先进行查询(-∞,30),T2在该区间插入一条记录(18),如果此时再进行读则数据还是一致的

但是T1对这条新增的记录进行修改,导致T1的read view对该事务可见,从而导致第二次读到这条T2新增的数据发生幻读

在S串行化下,如果是自动提交则只有一条读会使用mvcc,写则会加锁

至此,我们描述完读相关解决不同隔离性的方案,写相关解决不同隔离性的方案与锁相关,我把它放在下一篇文章和锁一起进行讲解

总结

记录的隐藏列中存在回滚指针和事务ID,回滚指针指向undo log,undo log又可以通过指针找到上一次修改产生的undo log,从而形成版本链(undo log只记录修改数据)

事务的原子性(需要回滚时)可以通过undo log实现

在并发读写场景下,Innodb的读操作通过mvcc来保证不同隔离性下数据一致性

mvcc使用生成read view、全局递增的事务ID和能够看到哪个版本的校验规则来实现

RU读时没使用mvcc,可以直接读到版本最新记录

RC每次读时生成read view,只能防止脏读

RR第一次读生成read view,可以防止脏读、不可重复读、大部分幻读

S只在自动提交时使用mvcc

最后(不要白嫖,一键三连求求拉~)

本篇文章被收入专栏 MySQL进阶之路,感兴趣的同学可以持续关注喔

本篇文章笔记以及案例被收入 gitee-StudyJavagithub-StudyJava 感兴趣的同学可以stat下持续关注喔~

有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~

关注菜菜,分享更多干货,公众号:菜菜的后端私房菜

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
关系型数据库 MySQL 数据库
确保数据完整性:探究MySQL中的原子性特性
在数据库管理中,"原子性"是一个至关重要的概念,对于维护数据的一致性和可靠性具有关键作用。在本文中,我们将深入探讨MySQL中的原子性特性,解释其含义、重要性以及如何保障数据的原子性操作。
522 0
|
SQL JSON 前端开发
太实用了!JSON在Mysql中原来可以这么玩
太实用了!JSON在Mysql中原来可以这么玩
162 0
|
SQL 缓存 关系型数据库
MySQL日志(undo log 和 redo log 实现事务的原子性/持久性/一致性)
MySQL日志(undo log 和 redo log 实现事务的原子性/持久性/一致性)
MySQL日志(undo log 和 redo log 实现事务的原子性/持久性/一致性)
|
存储 SQL 缓存
跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现
跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现
222 0
跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现
|
8天前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
22 4
|
6天前
|
SQL 关系型数据库 MySQL
12 PHP配置数据库MySQL
路老师分享了PHP操作MySQL数据库的方法,包括安装并连接MySQL服务器、选择数据库、执行SQL语句(如插入、更新、删除和查询),以及将结果集返回到数组。通过具体示例代码,详细介绍了每一步的操作流程,帮助读者快速入门PHP与MySQL的交互。
19 1
|
1月前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
61 3
Mysql(4)—数据库索引
|
15天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
76 1
|
17天前
|
关系型数据库 MySQL Linux
在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。
本文介绍了在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。同时,文章还对比了编译源码安装与使用 RPM 包安装的优缺点,帮助读者根据需求选择最合适的方法。通过具体案例,展示了编译源码安装的灵活性和定制性。
59 2
|
20天前
|
存储 关系型数据库 MySQL
MySQL vs. PostgreSQL:选择适合你的开源数据库
在众多开源数据库中,MySQL和PostgreSQL无疑是最受欢迎的两个。它们都有着强大的功能、广泛的社区支持和丰富的生态系统。然而,它们在设计理念、性能特点、功能特性等方面存在着显著的差异。本文将从这三个方面对MySQL和PostgreSQL进行比较,以帮助您选择更适合您需求的开源数据库。
79 4