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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: “时光机”与“多维视界”⭐️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下持续关注喔~

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

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

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
关系型数据库 MySQL 数据库
确保数据完整性:探究MySQL中的原子性特性
在数据库管理中,"原子性"是一个至关重要的概念,对于维护数据的一致性和可靠性具有关键作用。在本文中,我们将深入探讨MySQL中的原子性特性,解释其含义、重要性以及如何保障数据的原子性操作。
562 0
|
SQL JSON 前端开发
太实用了!JSON在Mysql中原来可以这么玩
太实用了!JSON在Mysql中原来可以这么玩
184 0
|
SQL 缓存 关系型数据库
MySQL日志(undo log 和 redo log 实现事务的原子性/持久性/一致性)
MySQL日志(undo log 和 redo log 实现事务的原子性/持久性/一致性)
MySQL日志(undo log 和 redo log 实现事务的原子性/持久性/一致性)
|
存储 SQL 缓存
跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现
跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现
238 0
跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现
|
11天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
39 3
|
11天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
41 3
|
11天前
|
SQL 关系型数据库 MySQL
数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog
《数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog》介绍了如何利用MySQL的二进制日志(Binlog)恢复误删除的数据。主要内容包括: 1. **启用二进制日志**:在`my.cnf`中配置`log-bin`并重启MySQL服务。 2. **查看二进制日志文件**:使用`SHOW VARIABLES LIKE &#39;log_%&#39;;`和`SHOW MASTER STATUS;`命令获取当前日志文件及位置。 3. **创建数据备份**:确保在恢复前已有备份,以防意外。 4. **导出二进制日志为SQL语句**:使用`mysqlbinlog`
53 2
|
24天前
|
关系型数据库 MySQL 数据库
Python处理数据库:MySQL与SQLite详解 | python小知识
本文详细介绍了如何使用Python操作MySQL和SQLite数据库,包括安装必要的库、连接数据库、执行增删改查等基本操作,适合初学者快速上手。
174 15
|
18天前
|
SQL 关系型数据库 MySQL
数据库数据恢复—Mysql数据库表记录丢失的数据恢复方案
Mysql数据库故障: Mysql数据库表记录丢失。 Mysql数据库故障表现: 1、Mysql数据库表中无任何数据或只有部分数据。 2、客户端无法查询到完整的信息。
|
25天前
|
关系型数据库 MySQL 数据库
数据库数据恢复—MYSQL数据库文件损坏的数据恢复案例
mysql数据库文件ibdata1、MYI、MYD损坏。 故障表现:1、数据库无法进行查询等操作;2、使用mysqlcheck和myisamchk无法修复数据库。