MySQL——隔离级别及解决方案

news2024/11/14 19:14:28

CRUD不加控制,会有什么问题?

在这里插入图片描述

比如上图场景,当我们的客户端A发现还有一张票的时候,将票卖掉,嗨还没有执行更新数据库的时候,客户端B又检查票数,发现票数大于0,又卖掉了一张票。然后客户端A更新数据库,客户端B也更新数据库。 那么此时就导致了同一张票被卖了2次的问题。

所以要解决以上的问题,有满足一下的操作

  • 买票的过程是原子的
  • 买票相互之间不能影响
  • 买完票后是永久有效的
  • 买票和买后都要是确定的状态

所以要解决以上问题,有了事务的概念。

什么是事务

一个MySQL数据库,同一时刻有很多客户端,甚至有大量的请求包装成事务,而每条事务至少有一条SQL,甚至多条SQL,如果大家都访问相同的表数据,在不加保护的情况下,绝对会出现问题。因为事务由多条SQL组成,那么就存在着执行到一半或出现其他意外的情况发生,那么已经执行的SQL语句怎么办呢?

所以,一个完整的事务,要包括一下的四个属性

  • 原子性:一个事务中所有的操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。 事务在执行的过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 一致性:在事务开始之前和结束之后,数据库的完整性没有被破坏。这表示写入的资料必须为完全符合所有的预设规则,这包含了资料的精确度、串联性以及后序数据库可以自发性的完成预定的工作
  • 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行的时候,由于交叉执行而导致数据的不一致。事务隔离分为了不同的级别,包括读未提交读提交可重复度串行化

上述也就是所谓的ACID。

  • 原子性(Atomicity,或称不可分割性)
  • 一致性(Consistency)
  • 隔离性(Isolation,又称独立性)
  • 持久性(Durability)

并发的事务会导致什么问题

MySQL服务端是允许多个客户端来连接的,这意味着MySQL会出现同时处理多个事务的情况。

那么在处理多个事务的时候,就可能会出现脏读不可重复读幻读的问题

脏读

如果一个事务读到了另一个事务没有提交后的数据,那么就叫做脏读。

下面以读未提交的场景下来演示

首先先将隔离级别设置为 读为提交

mys l> set global transaction isolation level read uncommitted; 
Query OK, 0 rows affected (0.00 sec) 
mysql> select @@tx_isolation; 
+------------------+ 
| @@tx_isolation | 
+------------------+ 
| READ-UNCOMMITTED | 
+------------------+ 
1 row in set, 1 warning (0.00 sec) 
 
mysql> select * from account; 
+----+--------+----------+ 
| id | name | blance | 
+----+--------+----------+ 
| 1 | 张三 | 100.00 | 
| 2 | 李四 | 10000.00 | 
+----+--------+----------+ 
2 rows in set (0.00 sec) 
 
mysql> begin; --开启事务 
Query OK, 0 rows affected (0.00 sec) 
 
mysql> update account set blance=123.0 where id=1; --更新指定行 
Query OK, 1 row affected (0.05 sec) 
Rows matched: 1 Changed: 1 Warnings: 0 
 
--没有commit哦!!! 
--终端B 
mysql> begin; 
mysql> select * from account; 
`+----+--------+----------+ 
| id | name | blance | 
+----+--------+----------+ 
| 1 | 张三 | 123.00 | --读到终端A更新但是未commit的数据[insert,delete同样] 
| 2 | 李四 | 10000.00 | 
+----+--------+----------+ 
2 rows in set (0.00 sec) 
 
--一个事务在执行中,读到另一个执行中事务的更新(或其他操作)
-- 但是未commit的数据,这种现象叫做脏读(dirty read) ``

不可重复读

在同一个事务中多次读取同一个数据,如果出现了数据不一致的问题,那么就发生了不可重复读

下面以读提交来演示

-- 终端A 
mysql> set global transaction isolation level read committed; 
Query OK, 0 rows affected (0.00 sec) 
--重启客户端 
mysql> select * from account; --查看当前数据 
+----+--------+----------+ 
| id | name | blance | 
+----+--------+----------+ 
| 1 | 张三 | 123.00 | 
| 2 | 李四 | 10000.00 | 
+----+--------+----------+ 
2 rows in set (0.00 sec) 
 
mysql> begin; --手动开启事务,同步的开始终端B事务 
Query OK, 0 rows affected (0.00 sec) 
 
mysql> update account set blance=321.0 where id=1; --更新张三数据 
Query OK, 1 row affected (0.00 sec) 
Rows matched: 1 Changed: 1 Warnings: 0 
mysql> commit; --commit提交! 
Query OK, 0 rows affected (0.01 sec) 
--切换终端到终端B,再次查看数据。 
 
--终端B 
mysql> begin; --手动开启事务,和终端A一前一后 
Query OK, 0 rows affected (0.00 sec) 
 
mysql> select * from account; --终端A commit之前,查看不到 
+----+--------+----------+ 
| id | name | blance | 
+----+--------+----------+ 
| 1 | 张三 | 123.00 | --老的值 
| 2 | 李四 | 10000.00 | 
+----+--------+----------+ 
2 rows in set (0.00 sec) 
 
--终端A commit之后,看到了! 
mysql> select *from account; 
+----+--------+----------+ 
| id | name | blance | 
+----+--------+----------+ 
| 1 | 张三 | 321.00 | --新的值 
| 2 | 李四 | 10000.00 | 
+----+--------+----------+ 
2 rows in set (0.00 sec) 

幻读

在一个事务内多次查询某个已经符合条件的数据,如果出现了前后两次查询到的记录数量不一致的情况,那么就出现了幻读问题

事务的隔离级别

当多个事务并发执行的时候,会出现脏读、不可重复读、幻读的情况。这些现象会对一致性产生同步程度的影响。

  • 脏读:读到了另一个事务还没有提交的数据
  • 不可重复读:前后读取到的数据不一致
  • 幻读:前后读取的记录数量不一致

所以针对以上问题,MySQL提出了四种隔离级别来解决问题。

  • **读未提交(**read uncommitted),指一个事务还没提交时,它做的变更就能被其他事务看到
  • 读提交(read committed),指一个事务提交之后,它做的变更才能被其他事务看到
  • 可重复读 (repeatable read),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别
  • 串行化(serializable )会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行

不同的隔离级别,并发的现象也会不同

加粗样式

总结:

  • 其中隔离级别越严格,安全性越高,但数据库的并发性能也就越低,往往需要在两者之间找一个平衡点
  • 不可重复读的重点是修改和删除:同样的条件, 你读取过的数据,再次读取出来发现值不一样了幻读的重点在于同样的条件, 第1次和第2次读出来的记录数不一样
  • 在这里插入图片描述

幻读问题解决

在解决幻读问题的时候有两种方式,第一种就是MVCC,第二种就是通过加next-key锁解决

在聊MVCC之前,先理解一下3个知识

  • 3个记录隐藏字段
  • undo日志
  • Read View

3个隐藏列字段

  • DB_TRX_ID:6btye,最近修改事务ID,记录创建这条记录/最近一次修改该记录的事务ID
  • DB_ROLL_PTR:7byte,回滚指针,指向了这条记录的上个版本(数据一般保存在undo日志中)
  • DB_ROW_ID:6btye,隐含了自增的ID(隐藏主键),如果数据表中没有主键,InnoDB会自动以BD_ROW_ID产生一个聚簇索引
  • flag删除字段,既记录被更新或删除并不代表真的删除了,只是flag字段别设置为了1

假设表的结构是:

mysql> create table if not exists student( 
 name varchar(11) not null, 
 age int not null 
 ); 
 
mysql> insert into student (name, age) values ('张三', 28); 
Query OK, 1 row affected (0.05 sec) 
 
mysql> select * from student; 
+--------+-----+ 
| name | age | 
+--------+-----+ 
| 张三 | 28 | 
+--------+-----+ 
1 row in set (0.00 sec) 

其实表结构在MySQL中真实为
在这里插入图片描述
目前并不知道创建该记录的事务ID,隐式主键,我们就默认设置成null,1。第一条记录也没有其他版本,所以设置回滚指针为null。

undo日志,先简单理解为一段内存缓冲区,用来保存日志数据。

模拟MVCC

现在有一个事务10(仅仅为了好区分),对student表中记录进行修改(update):将name(张三)改成name(李四)。

  • 事务10,因为要修改,所以要给该记录加锁
  • 修改前,现将改行记录拷贝到undo log中,所以,undo log中就有了一行副本数据。(原理就是写时拷贝)
  • 所以现在 MySQL 中有两行同样的记录。现在修改原始记录中的name,改成 ‘李四’。并且修改原始记录的隐藏字段 DB_TRX_ID 为当前 事务10 的ID, 我们默认从 10 开始,之后递增。而原始记录的回滚指针 DB_ROLL_PTR 列,里面写入undo log中副本数据的地址,从而指向副本记录,既表示我的上一个版本就是它。
  • 事务10提交,释放锁。

在这里插入图片描述

现在又有一个事务11,对student表中记录进行修改(update):将age(28)改成age(38)。

  • 事务11,因为也要修改,所以要先给该记录加行锁。
  • 修改前,现将改行记录拷贝到undo log中,所以,undo log中就又有了一行副本数据。此时,新的副本,我们采用头插方式,插入undo log。
  • 现在修改原始记录中的age,改成 38。并且修改原始记录的隐藏字段 DB_TRX_ID 为当前 事务11 的ID。而原始记录的回滚指针 DB_ROLL_PTR 列,里面写入undo log中副本数据的地址,从而指向副本记录,既表示我的上一个版本就是它。
  • 事务11提交,释放锁。

在这里插入图片描述

你看,上面的一个一个版本,其实就是一个一个的快照。

如果上面的操作是delete呢? 不是update呢? 是否可以形成版本链呢??

其实是可以的,我们隐藏字段中,有一个flag字段,删除数据其实不是将数据真的删除了,而是将flag字段设置为了1

对于select而言,select不会对数据产生影响,所以为了seelct维护版本链没有意义。 那么有一个问题就很重要了,select 是读取最新的数据呢,还是读取历史的数据呢??

  • 当前读:读取最新的记录,就是当前读。增删改,都叫做当前读,select也可以通过 select lock in share 或者 select … for update

可以看到在多个事务中,同时进行增删改的操作的时候,是要加锁的。 那么同时select也是要加锁的。 但如果是快照读,那么是不需要加锁的,也就是可以并发控制了。 这就提高了效率,这就是MVCC的意义所在了。

如何保证不同的事务之间,看到的不同的内容呢? 也就是如何实现隔离级别的

那么就要讲到Read View了。 Read View就是事务进行快照读的时候生产的读视图,在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务ID。

在MySQL源码中,Read View其实就是一个类,本质就是对可见性的判断。当我们某个事务执行快照读的时候,该记录会生成一个Read View读视图,用来判断当前事务可以看到哪个版本的数据。 可能是最近的数据,也可能是该记录中undo日志中的某个版本。

class ReadView { 
 // 省略... 
 private: 
 /** 高水位,大于等于这个ID的事务均不可见*/ 
 trx_id_t m_low_limit_id 
 
 /** 低水位:小于这个ID的事务均可见 */ 
 trx_id_t m_up_limit_id; 
 
 /** 创建该 Read View 的事务ID*/ 
 trx_id_t m_creator_trx_id; 
 
 /** 创建视图时的活跃事务id列表*/ 
 ids_t m_ids; 
 
 /** 配合purge,标识该视图不需要小于m_low_limit_no的UNDO LOG, 
 * 如果其他视图也不需要,则可以删除小于m_low_limit_no的UNDO LOG*/ 
 trx_id_t m_low_limit_no; 
 
 /** 标记视图是否被关闭*/ 
 bool m_closed; 
 
 // 省略... 
}; 

m_ids; //一张列表,用来维护Read View生成时刻,系统正活跃的事务ID 
up_limit_id; //记录m_ids列表中事务ID最小的ID(没有写错) 
low_limit_id; //ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1(也没
有写错) 
creator_trx_id //创建该ReadView的事务ID 

在这里插入图片描述

所以一个事务去访问记录的时候,除了自己的更新记录总是可见的之外,还有几种情况:

  • 如果记录的trx_id值小于 Read View 中的min_trx_id ,表示这个版本的记录是在创建 Read View前已经提交的事务生成的,所以该版本的记录对当前事务可见
  • 如果记录的 trx_id 值大于等于 Read View 中的max_trx_id 值,表示这个版本的记录是在创建 Read View 后才启动的事务生成的,所以该版本的记录对当前事务不可见
  • 如果记录的 trx_id 值在 Read View 的min_trx_id 和max_trx_id 之间,需要判断 trx_id 是否在 m_ids 列表中
    • 如果记录的trx_id在m_ids列表中,表示生成的该版本的活跃事务依然在运行,还没有提交事务。 所以对当事务不可见
    • 如果记录的trx_id不在m_ids列表中,表示该事务已经提交了,所以对当前的事务是可见的。

RR和RC的本质区别

  • 正是Read View生成时机的不同,从而造成RC,RR级别下快照读的结果的不同
  • 在RR级别下的某个事务的对某条记录的第一次快照读会创建一个快照及Read View, 将当前系统活跃的其他事务记录起来
  • 此后在调用快照读的时候,还是使用的是同一个Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可见
  • 而在RC级别下的,事务中,每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因
  • 总之,RC隔离级别下,是每个快照读都会生成并获取最近的Read View,而在RR的隔离级别下,是同一个事务中的第一个快照读才会创建Read View,之后的快照读获取的都是第一个Read View
  • 正是因为RC快照读的时候,都会生成一次Read View,所以RC才会有不可重复读的问题。

next-key锁

针对当前读来说,是通过next-key lock(记录锁 + 间隙锁)方式解决幻读问题,因为当执行select … for update,会加上next-key lock,如果有其他事务在锁的范围内进行增删改操作,就会被阻塞住。 这就解决了幻读问题。

MySQL完全解决了幻读问题吗

其实并没有,MySQL只是针对幻读问题,提出了2种解决方案

  • 针对快照读,通过MVCC来解决
  • 针对当前读,通过next-key lock来解决

我举出一种发生幻读的场景

  • T1时刻,事务A先执行了快照读,select * from table where id > 100
  • T2时刻,事务B进行插入id 为200 的数据并提交事务
  • T3时刻,事务A进行当前读, select * from table for update,此时会得到id为200的数据,所以发生了幻读情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2101427.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于FPGA实现SD NAND FLASH的SPI协议读写

基于FPGA实现SD NAND FLASH的SPI协议读写 在此介绍的是使用FPGA实现SD NAND FLASH的读写操作,以雷龙发展提供的CS创世SD NAND FLASH样品为例,分别讲解电路连接、读写时序与仿真和实验结果。 目录 1 FLASH背景介绍 2 样品申请 3 电路结构与接口协议 …

微信管理工具真的那么好用么?

01 多号一个界面聚合聊天 可以同时登录多个微信号,不再需要频繁切换账号或使用多台设备在一个界面聚合聊天。 02 多号朋友圈同步发朋友圈 多个微信号可以即时发布或定时发布朋友圈,省去了逐个发送的繁琐。 03 机器人自动回复 不仅可以自动通过好友…

Android Camera系列(三):GLSurfaceView+Camera

人类的悲欢并不相通—鲁迅 Android Camera系列(一):SurfaceViewCamera Android Camera系列(二):TextureViewCamera Android Camera系列(三):GLSurfaceViewCamera 本系…

Telephony SMS

1、短信的协议架构 如下图,参考3GPP 23.040 4.9节 Protocols and protocol architecture 1、SM-AL : 应用层 2、SM-TL :传输层 3、SM-RL :中继层 4、SM-LL :链路层 由于我们只关注手机终端,因此只需要关注SM-TL这一层即可 2、SM-TL分类 短信的协议架构参考3GPP 23.04…

猛兽财经:在股价创下历史新高后,5个因素将使Netflix股价进一步上涨

来源:猛兽财经 作者:猛兽财经 股价创三年来新高后, Netflix股价还会继续上涨 作为流媒体领域无可争议的领导者,Netflix(NFLX)的股价在上周再次创下了新高(每股超过了700美元,这一涨幅已经超过了2021年底创…

[Linux] 项目自动化构建工具-make/Makefile

标题:[Linux] 项目自动化构建工具-make/Makefile 水墨不写bug 目录 一、什么是make/makefile 二、make/makefile语法 补充(多文件标识): 三、make/makefile原理 四、make/makefile根据时间对文件选择操作 正文开始&#xff…

基于SpringBoot的校园闲置物品租售系统

你好呀,我是计算机学姐码农小野!如果有相关需求,可以私信联系我。 开发语言:Java 数据库:MySQL 技术:SpringBootMyBatis 工具:IDEA/Eclipse、Navicat、Maven 系统展示 首页 用户管理界面 …

华为云征文|Flexus云服务X实例应用,通过QT连接华为云MySQL,进行数据库的操作,数据表的增删改查

引出 4核12G-100G-3M规格的Flexus X实例使用测评第3弹:Flexus云服务X实例应用,通过QT连接华为云MySQL,进行数据库的操作,数据表的增删改查 什么是Flexus云服务器X实例 官方解释: Flexus云服务器X实例是新一代面向中…

【python】如何用python代码快速生成二维码

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…

【算法思想·二叉树】思路篇

本文参考labuladong算法笔记[东哥带你刷二叉树(思路篇) | labuladong 的算法笔记] 本文承接 【算法思想二叉树】纲领篇,先复述一下前文总结的二叉树解题总纲: 二叉树解题的思维模式分两类: 1、是否可以通过遍历一遍二…

数据结构——单链表相关操作

zhuzhu1、结构框图: 2、增删改查: 定义链表节点和对象类型 /*************************************************************************> File Name: link.h> Author: yas> Mail: rage_yashotmail.com> Created Time: Tue 03 Sep 2024…

ServiceStage集成Sermant实现应用的优雅上下线

作者:聂子雄 华为云高级软件工程师 摘要 优雅上下线旨在确保服务在进行上下线操作时,能够平滑过渡,避免对业务造成影响,保证资源的高效利用。Sermant基于字节码增强的技术实现了应用优雅上下线能力,应用发布与运维平…

摩博会倒计时!OneOS操作系统抢先了解!

2024年第二十二届中国国际摩托车博览会(摩博会)临近,中移物联OneOS与智能硬件领域佼佼者恒石智能宣布强强合作,与9月13日至16日在重庆国家会展中心共同展现多款Model系列芯片(Model3、Model4、Model3C、Model3A&#x…

I2C软件模拟时序的基本要素

目录 前言 一、关于I2C 二、正文 1.引脚的配置 2.I2C的起始和终止时序 3.发送一个字节 4.接收一个字节 5.应答信号 6.指定地址写和指定地址读 总结 前言 环境: 芯片:STM32F103C8T6 Keil:V5.24.2.0 本文主要参考江科大教程&#…

系统架构师考试学习笔记第三篇——架构设计高级知识(11)软件可靠性基础知识

本章知识点: 第11课时主要学习软件可靠性基本概念、建模、管理、设计、测试和评价等内容。本课时内容侧重于概念知识,根据以往全国计算机技术与软件专业技术资格(水平)考试的出题规律,考查的知识点多来源于教材,扩展内容较少。根据考试大纲,本课时知识点会涉及单项选…

注册安全分析报告:央视网

前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…

day47——面向对象特征之继承

一、继承(inhert) 面向对象三大特征:封装、继承、多态 继承:所谓继承,是类与类之间的关系。就是基于一个已有的类,来创建出一个新类的过程叫做继承。主要提高代码的复用性。 1.1 继承的作用 1> 实现…

16、修改Markdown Preview Enhanced默认样式

前言 vscode的markdown preview enhanced插件的主题并不一定符合每个人的审美,所以有的时候需要自定义,笔者根据网上大佬的文章整合了下自定义修改Markdown Preview Enhanced默认样式的方法,模板在文章中,大家可以直接使用,希望能…

【数据结构】反射,枚举你必须知道的相关知识

前言: 🌟🌟本期讲解关于反射以及枚举,希望能帮到屏幕前的你。 🌈上期博客在这里:http://t.csdnimg.cn/7D225 🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 目录 &#x1f…

获得并修改硬件序列号--CPU、主板、内存、硬盘等(有源码)

大家都知道很多Anti Cheat会封硬件序列号,所以本文探索一下如何get and modify序列号。 这个服务是比较贵的: 于是有了研究一下的想法。 思路: 1. 通过厂商自带的程序刷新固件。 2. 自己写驱动修改。 思路1不讨论,要拿到厂商去修改,很不方…