轻松上手MYSQL:MYSQL事务隔离级别的奇幻之旅

2024-06-23 22:36

本文主要是介绍轻松上手MYSQL:MYSQL事务隔离级别的奇幻之旅,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

​🌈 个人主页:danci_
🔥 系列专栏:《设计模式》《MYSQL》
💪🏻 制定明确可量化的目标,坚持默默的做事。


✨欢迎加入探索MYSQL索引数据结构之旅✨
    👋 大家好!文本学习研究事务隔离级别。👋 无论您是刚接触MySQL的初学者,还是希望深入优化性能的资深开发者,这篇文章都将为您揭开MySQL事务隔离级别的神秘面纱,让您掌握其中的奥秘,进而提升数据库操作的效率和精度。快来一起探索吧!


1. 什么是事务?


目录

一、事务隔离级别

1.1 事务并发执行的一致性问题

1.1.1 脏写

1.1.2 脏读

1.1.3 不可重复读:

1.1.4 幻读

1.2 SQL标准中的4种隔离级别

1.3 MYSQL查询事务隔离级别

1.4 MYSQL设置隔离级别

二、MYSQL4种隔离级别实战

2.1 READ UNCOMMITTED

2.2 READ COMMITTED

2.3 REPEATABLE READ(MYSQL默认的隔离级别)

2.4 SERIALIZABLE

三、总结


一、事务隔离级别

MYSQL是客户端 / 服务器的软件,是多对一的关系,即同时可以有多个客户端同时连一个接服务,每个客户端连接服务器后,就生成一个会话。每个会话都可以向服务器发送请求语句,一个请求语句可能是一个事务,也可能是一个事务的某一部分语句。而服务器可以同时处理来自多个客户端的请求语句。

事务简述:一个事务就对应着现实世界的一次状态转换。

下面举个例子,比如用户A向用户B转账100元,账户状态变更简化为以下几个步骤:

  1. 取出A账户余额a1。即一次select A
    1. a1 < 100,退出转账
    2. a1 >= 100,则继续
  2. a1 -= 100
  3. 更新入库。即一次update A
  4. 取出B账户余额b1。即一次select B
  5. b1 += 100
  6. 更新入库。即一次update B

在这个转账事务中,一定要保证 A减100 和 B加100 都成功,换句话说就是必须保证参与转账的账户的总余额保持不变,这也就是这个转账事务的一致性要求。

如果事务是以单个的形式一个接一个地执行,那么在一个事务开始时,面对的就是上一个事务执行结束后留下的一致性状态,它执行之后又会产生下一个一致性状态。那么在多个事务的情况下,情况就变得比较复杂。假如事务是交替执行的,如下图

如图中的交替执行的两个事务,A转账了两次5最后余额为6 ,B转入两次余额为12。显然A账户余额是错误的,这就违背了“参与转账的账户的总余额保持不变”的一致性要求。

这时就需要某种手段来强制让这些事务按照顺序一个一个单独地执行,或者最终执行的效果和单独执行一样。换句话说就是让多个事务隔离的执行,也就是互不干涉。这就是事务的隔离性要求。

如何实现事务的隔离性?

  • 串行执行:在系统中同一时刻最多只允许一个事务运行
    • 能保证一致性
    • 导致许多事务等待时间,资源利用率低
  • 可串行化执行:事务可并发执行,但涉及到同一个数据时,就等待其它事务提交之后才能继续访问这个数据。

可串行化执行对同一数据写操作就会出现等待,性能还是不高。在一些高并发场景中影响会非常明显。

1.1 事务并发执行的一致性问题

1.1.1 脏写

事务T1和事务T2同时修改数据x,事务T1开启事务写x值且还没有提交事务,事务T2开始写x值,这就发生了脏写。如下图:

脏写不只影响数据的一致性,还影响事务的原子性,如下图:(假设x初始值为 5)

事务 T1 因某种原因要回滚,那么需要将它的数据回滚到事务开启时的状态,也就是将x的值变为初始值为 5。但是对于事务 T2 来说,它修改x为200并且已经提交了,如果 T1 回滚,则 T2 事务的修改有一部分数据回滚了,这就影响了事务的原子性。

1.1.2 脏读

事务T1读到未提交事务T2修改过的数据,这就发生了脏读。如下图:

事务T1 读 x 时 T2 未提交,这时 T1 读到的是不一致的数据。

1.1.3 不可重复读:

事务 T1 修改了未提醒事务 T2 读取的数据,这就发生了不可重复读。如下图:

事务 T1 和事务 T2 都开启事务,事务 T2 读 x 为 50,之后事务 T1 修改 x = 100 并且提交了事务,事务 T2 再次读 x,此时 x 的值为 100了,这就出现在同一个事务中读同一条数据的值不一致问题。这种在同一个事务中读同一条数据不一致问题不应该暴露给用户的。

1.1.4 幻读

事务 T1 根据某种搜索条件井底出记录,在该事务未提交时,另一事务写入了符合那些搜索条件的记录,这就产生了幻读。如下图:

事务 T1 查询 col = 1 的数据有记录1 和 记录2,此时事务 T2 修改记录3 的col = 1并且提交事务,这时,事务 T1 再查询 col = 1 的数据有 记录1、记录2 和 记录3,与之前的查询结果不一致,不符合一致性要求。

影响一致性严重性大到小排序: 脏写 > 脏读 > 不可重复读 > 幻读

1.2 SQL标准中的4种隔离级别

  • READ UNCOMMITTED: 读未提交

  • READ COMMITTED: 读已提交

  • REPEATABLE READ: 可重复读

  • SERIALIZABLE: 可串行化

四种隔离级别在并发事务中可能发生的数据一致性问题如下:

也就是说:

  • READ UNCOMMITTED隔离级别下,可能脏读、不可重复读和幻读
  • READ COMMITTED隔离级别下,可能发生不要重复读和幻读
  • REPEATABLE READ隔离级别下,可能发生幻读
  • SERLIALIZABLE隔离级别下,脏读、不可重复读和 幻读 均不可能发生

注:脏写这个情况对一致性影响很严重,以上四种隔离级别都不准脏写发生。

1.3 MYSQL查询事务隔离级别

Mysql8以前:SELECT @@GLOBAL.tx_isolation, @@tx_isolation;Mysql8开始:SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;

1.4 MYSQL设置隔离级别

MYSQL支持4种事务隔离级别,默认的隔离级别为REPEATABLE READ(可重复读)

设置事务隔离级别

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level

其中

  • level有:REPEATABLE READ、READ UNCOMMITTED、READ COMMIITTED 和 SERIALIZABLE
  • GLOBAL| SESSION:可设置可不设置
    • 不设置:只对执行SET后的下一个事务有效
      • 对下一个事务有效
      • 对下下个事务无效,事务隔离级别恢复到之前的隔离级别
      • 在已经开启的事件中执行会报错
    • GLOBAL:全局范围有效
      • 当前会话无效
      • 只对执行完该SET后的会话有效
    • SESSION:在当前会话有效
      • 多个事务执行,只对该语句后面执行的事务有效
      • 可在开启的事务中执行,不影响之前正在执行的事务
      • 对当前会话后续的事务无效

查看事务隔离级别

mysql> SHOW VARIABLES LIKE 'transaction_isolation';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.03 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)

其实设置事务隔离级别相当于修改transaction_isolation的值。

设置隔离级别:

语法作用范围
SET GLOBAL transaction_isolation = 隔离级别全局
SET @@GLOBAL.var_name = 隔离级别全局
SET var_name = 隔离级别会话
SET SESSION var_name = 隔离级别会话
SET @@SESSION.var_name = 隔离级别会话
SET @@var_name = 隔离级别下一个事务

二、MYSQL4种隔离级别实战

创建个测试表

CREATE TABLE `test`.`test`  (`id` int NULL DEFAULT NULL,`name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
INSERT INTO test values(1, 'zhangsan');

2.1 READ UNCOMMITTED

设置事务隔离级别、开启两个会话且开启事务,操作如下:(左边:事务a;右边:事务b)

                                                                图 2.1-1

 打开两个会话,事务隔离级别设置为 READ UNCOMMITTED(读未提交)

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

如图2.1-1中
脏读: name初始化为zhangsan,事务a修改name为lisi 且事务未提交,
事务b已读出name为lisi,而不是zhangsan(读未提交),出现了“脏读”现象。不可重复读:在事务b中两次读取的数据不一样,出现了“不可重复读”现象。

                                                                图 2.1-2

如图2.1-2中
在事务a插入新数据之前读name='zhangsan'和事务b读name='zhangsan'都为一条数据,
事务a插入一条数据且未提交,
幻读: 此时事务b用相同的查询条件,读出来了两条两次读出来不一致的数据,出现了“幻读”现象。

2.2 READ COMMITTED

设置事务隔离级别、开启两个会话且开启事务,操作如下:(左边:事务a;右边:事务b)

                                                                图2.2-1

如图2.1-1中,同时启动两个事务ab

name初始化为 'lisi',事务a修改name为 'zhangsan' 且事务未提交,事务b读name时,值仍然为 'lisi',而不是 'zhangsan'(读已提交)。当事务a提交了事务,事务读出的数据为 'zhangsan'。

未出现脏读: 在事务a未提交的情况下,事务b读的数据没有变化,未出现“脏读”现象。

不可重复读: 在同一个事务b中两次读取的数据不一致,出现了“不可重复读”现象。

幻读:在同一个事务b中相同条件下读数据不一致,出现了“幻读”现象。

2.3 REPEATABLE READ(MYSQL默认的隔离级别)

图 2.3-1

如图2.3-1,区域 1,同时启两个事务ab。

可重复读:事务ab执行之后,事务a删除id为2的数据,提交前或提交后,事务b再次读取的数据者与之前读取的数据保持一致。


如图2.3-1,区域 2,同时启两个事务ab。(事务a已提交,默认自动提交)

幻读:事务a插入一条新数据id为3(自动提交事务)后,事务b读取数据,还是两条,保证了可重复读特性。但,事务b可以修改 id=3 的数据,然后再查询就能把 id为3 的数据查询出来了,出现了幻读的现象。

2.4 SERIALIZABLE

                                                                图 2.4-1

 如图2.4-1,设置隔离级别为SERIALIZABLE,同时开启两个事务,各步骤说明和结论:

  • 1: 读 id = 1 的数据
  • 2: 修改 id = 1 的数据报锁超时,结论:读某数据且加锁,本事务可读写,别的事务只可读不可写
  • 3: 更新 id = 5 成功,无其它事务读或写的数据,可写
  • 4: 读 id = 5 报锁超时,结论:写某数据,别的事务不允许读也不允许写
  • 5: 插入 id = 2 成功,无其它事务读或写的数据,可写
  • 6: 范围查询 id (5, 11)
  • 7: 插入 id = 7 成功,无其它事务读或写的数据,可写
  • 8: 插入 id为6 报锁超时
  • 9: 插入 id为6 成功
  • 10: 范围查询 id (5, 11)
  • 11: 插入 id为7 成功
  • 12: 插入 id为8 报锁牛奶时,结论:范围查询加锁,该范围数据本事务可读写,其它事务只可读
  • 13: 插入 id = 12 成功,无其它事务读或写的数据 id = 12,可写

 如此,本事务查询的数据,本事务可修改,其它事务只可读; 本事务修改的数据,其它事务不可查询和修改。完美避免脏读、不可重复读、幻读等读现象。当然因各种加锁,使得该事务隔离级别下效率低下,耗费数据库性能,不推荐使用。

三、总结

        本文我们深入探索了MySQL事务隔离级别的奥秘。从READ UNCOMMITTED到SERIALIZABLE,每个级别都有其独特的特点和适用场景。READ UNCOMMITTED虽快但风险高,SERIALIZABLE则提供最严格的数据一致性保证但性能消耗大。在实际应用中,根据业务需求和性能考量,合理选择事务隔离级别至关重要。通过本次奇幻之旅,我们掌握了事务隔离级别的核心概念及实战,为在MySQL数据库中实现高效、稳定的数据处理提供了有力支持。

    希望你喜欢这次的探索之旅!不要忘记 "点赞" 和 "关注" 哦,我们下次见!🎈

这篇关于轻松上手MYSQL:MYSQL事务隔离级别的奇幻之旅的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1088469

相关文章

闲置电脑也能活出第二春?鲁大师AiNAS让你动动手指就能轻松部署

对于大多数人而言,在这个“数据爆炸”的时代或多或少都遇到过存储告急的情况,这使得“存储焦虑”不再是个别现象,而将会是随着软件的不断臃肿而越来越普遍的情况。从不少手机厂商都开始将存储上限提升至1TB可以见得,我们似乎正处在互联网信息飞速增长的阶段,对于存储的需求也将会不断扩大。对于苹果用户而言,这一问题愈发严峻,毕竟512GB和1TB版本的iPhone可不是人人都消费得起的,因此成熟的外置存储方案开

SQL中的外键约束

外键约束用于表示两张表中的指标连接关系。外键约束的作用主要有以下三点: 1.确保子表中的某个字段(外键)只能引用父表中的有效记录2.主表中的列被删除时,子表中的关联列也会被删除3.主表中的列更新时,子表中的关联元素也会被更新 子表中的元素指向主表 以下是一个外键约束的实例展示

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

如何去写一手好SQL

MySQL性能 最大数据量 抛开数据量和并发数,谈性能都是耍流氓。MySQL没有限制单表最大记录数,它取决于操作系统对文件大小的限制。 《阿里巴巴Java开发手册》提出单表行数超过500万行或者单表容量超过2GB,才推荐分库分表。性能由综合因素决定,抛开业务复杂度,影响程度依次是硬件配置、MySQL配置、数据表设计、索引优化。500万这个值仅供参考,并非铁律。 博主曾经操作过超过4亿行数据

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

MySQL高性能优化规范

前言:      笔者最近上班途中突然想丰富下自己的数据库优化技能。于是在查阅了多篇文章后,总结出了这篇! 数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 临时库表必须以tmp_为前缀并以日期为后缀,备份

[MySQL表的增删改查-进阶]

🌈个人主页:努力学编程’ ⛅个人推荐: c语言从初阶到进阶 JavaEE详解 数据结构 ⚡学好数据结构,刷题刻不容缓:点击一起刷题 🌙心灵鸡汤:总有人要赢,为什么不能是我呢 💻💻💻数据库约束 🔭🔭🔭约束类型 not null: 指示某列不能存储 NULL 值unique: 保证某列的每行必须有唯一的值default: 规定没有给列赋值时的默认值.primary key:

MySQL-CRUD入门1

文章目录 认识配置文件client节点mysql节点mysqld节点 数据的添加(Create)添加一行数据添加多行数据两种添加数据的效率对比 数据的查询(Retrieve)全列查询指定列查询查询中带有表达式关于字面量关于as重命名 临时表引入distinct去重order by 排序关于NULL 认识配置文件 在我们的MySQL服务安装好了之后, 会有一个配置文件, 也就