初识mysql数据库之事务的隔离性

news2024/11/6 9:24:18

目录

一、理解隔离性

二、隔离级别

1. 不同的隔离级别的简单概述

2. 查看隔离级别

2.1 查看全局隔离级别

2.2 查看会话隔离级别

3. 设置隔离界别

4. 读未提交(Read Uncommitted)

4.1 读未提交测试

5. 读提交(Read Committed)

5.1 读为提交测试

5.2 不可重复读带来的问题

6. 可重复读(Repeatable Read)

6.1 可重复读测试

6.2 可重复读在insert中的问题(已解决)

7. 串行化(Serializable)

7.1 串行化测试

 8. 总结

三、事务的一致性


一、理解隔离性

在理解隔离性之前,大家要先知道,mysql服务作为一个网络服务,是可能会同时被多个客户端进程访问的,访问的方式就是以事务进行的

同时我们知道,一个事务是由一个或多个sql语句构成。这也就意味着,任何一个事务都会有执行前,执行中和执行后三个阶段。但同时,事务是具有原子性的,这个原子性就保证了用户只会看到一个事务执行前或执行后的内容。当执行中出现了问题,就直接回滚。

在上文中说了,一个事务可能会由多个sql语句构成,且mysql可能同时被多个客户端访问,这也就导致在多客户端访问的情况下,可能会出现事务之间互相干扰的情况。例如两个不同的事务要对同一张表做不同的修改。这也就是说,不同的事务在执行的过程中是可能会被其他事务干扰的。

这就好比你和你的同学在一个教室上课,如果有一天你上数学课时忘记带书了,你就会想要和你的同桌看同一本数学书。但如果你们看同一本数学书,就可能出现互相干扰。例如你想看第10页的内容,但他刚好想在第10页做笔记。此时你们之间就产生了干扰。到底是让你先看呢,还是让同桌先做笔记呢?这是一个问题。

数据库中也是一样的。为了保证不同事务在执行的过程中尽量不受干扰,就有了隔离性这一重要特征

但同时,不同事务可能允许的受干扰程度不同,例如你的同桌可能对自己的字很有信心,允许你在他写笔记的时候看这些笔记的内容。但也可能你的同桌比较害羞,不喜欢别人看着自己做笔记,于是说让你在他做笔记的时候不要看,等他做完了再给你看。基于这种情况,就有了隔离性的一种重要特征:隔离级别

二、隔离级别

1. 不同的隔离级别的简单概述

首先大家要知道,在事务的场景中,隔离是必要的,每一个运行中的事务,都需要进行互相隔离。根据隔离造成的影响程度的不同,就分为了多个隔离级别。

在mysql中,一共有四种隔离界别,分别为“读未提交(Read Uncommitted)”,简称“RU”“读提交(Read Committed)”,简称"RC"“可重复读(Repeatable Read)”,简称"RR"“串行化(Serializable)”

在这里,为了让大家对这几个隔离级别有一个基础概念,先简单介绍一下。

读未提交,就是多个事务在同时运行时,一个事务对表进行修改后,其他事务可以立即看到修改后的内容,无论进行修改的事务是否退出

读提交,就是多个事务在同时运行时,一个事务对表进行修改后,其他事务无法立即看到修改后的内容只有当执行修改操作的事务退出后,其他事务才能看到

可重复读,就是多个事务在同时运行时,一个事务对表进行修改后,无论这个事务是否退出,其他事务都无法看到修改后的内容只有当执行修改操作的事务和同时运行的其他事务退出后,再新起一个事务,才能看到

串行化,就是让多个事务串行执行,当一个事务执行完后,才能让下一个事务执行

其中,可重复读是mysql默认的隔离级别

隔离级别的实现,基本都是通过锁完成的。不同的隔离级别,锁的使用时不同的。常见的有表锁、行锁、读锁、间隙锁(GAP)、Next-Key锁等。不过,这些内容我们暂时不做过多了解。

2. 查看隔离级别

要查看隔离级别,其实有两种大的查看方向,分别是查看全局隔离级别查看会话(当前)隔离级别

当登陆mysql后,mysql会读取全局隔离级别,然后用它来初始化本次登陆的会话所用的会话隔离级别。因此,全局隔离级别就可以看成是一种配置,登录mysql时就会自动加载全局隔离级别

其中,全局隔离级别影响的是后续所有登录到该mysql中的会话的隔离级别。而会话隔离级别则只会影响当前会话的隔离级别

2.1 查看全局隔离级别

要查看全局隔离级别,可以用“select @@global.tx_isolation;”命令查看: 

2.2 查看会话隔离级别

要查看会话隔离级别,有两种方法。

第一种是“select @@session.tx_isolation;”命令:

第二种是“select @@tx_isolation;”命令:

3. 设置隔离界别

要设置隔离级别,可以分别设置全局隔离级别会话隔离级别。语法如下:

其中,session表示会话隔离级别global表示全局隔离级别level后面的内容表示要设置的隔离级别

注意,重新设置了会话隔离级别后,重新设置的隔离级别只会影响当前会话,不会影响其他会话

当重新设置了全局隔离级别后,这个隔离级别不会影响当前会话的隔离级别必须要重新登录mysql,才能使用新设置的全局隔离级别

例如,在这里重新设置全局隔离级别并查看全局隔离界别:

可以看到,全局隔离级别已经修改为了可重复读。但如果再查看会话隔离级别:

可以看到,此时依然是读未提交。没有被修改。

退出并重新登录mysql后再查看会话隔离级别:

此时会话隔离级别就修改了。

在实际中,事务的隔离级别默认为可重复读如果要修改隔离级别,最好保证不同事务的隔离级别是一致的。这就意味着,如果要修改隔离级别,最好修改全局隔离级别,不要修改会话隔离级别。同时,虽然在后续的关于隔离级别的实验中会经常修改隔离级别,但在实际中非常不建议随意修改隔离级别

4. 读未提交(Read Uncommitted)

在该隔离级别,所有的事务都可以看到其他事务没有提交的执行结果。(实际生产中不可能使用这种隔离级别)。该隔离级别相当于没有任何隔离性,同时存在很多并发问题。如脏读幻读不可重复读等。

4.1 读未提交测试

在做测试之前,先将全局隔离级别设置为读未提交,并重新登录mysql。至于如何设置,在上文中已经讲过,不再赘述:

查看准备好的user表中的数据:

这张表在以前的文章中就有过使用,不再过多介绍。准备好这张表后,先在一个窗口中启动事务a,然后插入如下数据:

然后在另一个窗口中启动事务b,查看user表的数据:

可以看到,虽然此时事务a还没有结束,但是在事务b中却可以看到事务a对user表做的修改。这就叫做“读未提交”,即所有事务都可以在某个事务没有退出时看到它对表做的修改。

这种一个事务进行中,可以读到另一个执行中的事务的更新(或其他操作)的数据的现象,叫做“脏读”。是一种并发问题。因为事务需要保持原子性,用户只能看到事务执行前和事务执行后的数据,但是脏读却可以让用户看到事务执行中的数据,是一种非常不合理的现象。

因此虽然这个隔离级别中几乎没有加锁,效率很高,但是会出现很多问题,非常不建议采用

5. 读提交(Read Committed)

该隔离级别是大多数数据库的默认的隔离级别(不是mysql的默认隔离级别)。它满足了隔离的简单定义:一个事务只能看到其他的已经提交的事务所做出的改变。这种隔离级别会引起不可重复读,即一个事务执行时,如果多次select,可能得到不同的结果

5.1 读为提交测试

首先,将mysql的全局隔离级别设置为读提交,然后重新登录:

 准备如下一张user表:

开启事务a,并向里面插入如下数据:

然后再在另一个窗口中开启事务b,并查看user表的内容:

可以看到,在事务a未退出之前,我们在事务b中是看不到事务a对user表的修改的。

然后我们提交事务a,并再次在事务b中查看user表的数据:

此时在事务b中就可以看到事务a的修改了。这就是“读提交”隔离级别下的效果。即正在执行中的事务只能看到已经执行完毕的事务对数据的修改内容

大家可以想一想,这种同时处于运行状态的事务,当其中某个事务结束后,其他事务可以看到该事务对数据的修改的现象,一定合理吗?很明显,并不一定合理,当然这不是说一定不合理,这种情况在某些场景下还是需要的。

但它同时也存在不合理的场景。对于上述这种,一个正在运行的事务在查看某份数据时,可能查看到不同结果的现象,就被称为“不可重复读”。是一种并发问题

5.2 不可重复读带来的问题

上文中讲了,当一个正在运行的事务在查看同一份数据时,却查到不同结果的现象,就叫做“不可重复读”。那不可重复读会带来什么问题呢?举一个例子说明。

假设你现在在一个学校里面,你们学校刚好组织了一场考试。你们的班主任是一个很好的人,他在你们考完试后就承诺说,这次考试,他会按照考试的分数给大家发奖品。[90, 100]的人每人发200块钱;[80, 90]的人每人发150块钱;[70, 80]的人每人发100块钱。[60, 70]的人每人发50块钱;60以下的人每人发30块钱。

当你们的成绩出来后,班主任就将统计分数的任务交给了小王。刚好小王从小就喜欢计算机,学习了编程,于是在他接到这个任务后,它就在数据库中进行统计。在统计的过程中他就想,要统计这些分数,那直接用select语句不就好了么。于是他就将这5个select语句看做一个事务,在数据库中进行统计

但此时你们班有一个小李的同学,他在成绩出来后发现和自己的预期有点大,他自己算应该是84分,但是实际分数却是78分。于是他仔细检查了一遍试卷,发现是改卷老师改错了,于是他就拿着卷子去找班主任,班主任一看,确实是卷子改错了。于是班主任就告诉小李说,不用担心,我这就把这个问题反馈给学校。

当学校收到这个消息后,学校就去找负责管理学生成绩的人,告诉他某某班的某某同学的成绩改错了,你把他的成绩在数据库里面改一下。管理人员听了后,就马上开始动手了。

与此同时,小王也在数据库中统计同学的成绩。于是此时就有了两个客户端接入了数据库。小王在统计时,是按照成绩从小到大统计的。当小王统计到[70, 80]的区间时,里面就出现了小李的名字。但就在这个时候,管理人员修改了小李的成绩,将其从78改为了84,然后提交了事务。但是小王他并不知道这个修改,任由事务继续跑。于是当小王统计完[80, 90]的人时,里面也出现了小李的名字。

此时统计结果中就出现了两次小李。当班主任看到这份统计结果后,就对小王很不满意,觉得小王这么点小事都办不好。那小王也感觉很委屈,明明自己执行的sql语句没有任何问题,但为什么会出现两次小李呢?这就是“读提交”隔离级别带来的问题。 

由此,这种同时运行的事务,一方可以读取到另一方的提交结果所带来的“不可重复读”问题,也是需要解决的。基于这个问题,便有了下一个“可重复读”隔离级别。

6. 可重复读(Repeatable Read)

可重复读是mysql默认的隔离级别,它确保一个正在运行的事务在执行中多次读取数据时,能够看到同样的数据行。但是可能产生“幻读”问题(已解决)

6.1 可重复读测试

首先,将mysql的隔离级别设置为可重复读,然后重新登录mysql:

 准备如下一个user表:

开始事务a,然后插入如下数据:

插入完成后,在另一个窗口启动事务b,并查看user表内的数据:

可以看到,此时在事务b中无法看到事务a执行的操作。然后将提交事务a,再次在事务b中查看user表内的数据:

可以看到,此时虽然事务a已经提交了,但是事务b依然无法看到事务a对user表的修改。这就是“可重复读”。在正在执行的事务中查看某个数据,无论其他事务对该数据执行了什么操作,当前事务所看到的数据从始至终都是一样的

此时我们再将事务b结束,然后再查看user表内的数据:

此时就可以看到事务a对user表修改后的结果了。这也就说明在“可重复读”中,处于执行状态的事务是无法看到其他事务对数据的修改的,要看到修改结果,只能结束并新建事务

6.2 可重复读在insert中的问题(已解决)

 虽然从上面的测试中来看,在mysql数据库中的可重复读隔离级别下,事务在执行insert语句时,不会对其他事务所看到的数据造成影响, 符合可重复读的特点。

但是,在一般的数据库的可重复读级别下,是无法屏蔽其他事务insert的数据的。即正在运行的事务b是可以看到事务a用insert语句对数据的修改的。

因为隔离性的实验是对数据加锁完成的,但是当使用insert语句去插入数据时,要加锁的数据因为还没有被插入进表内,所以在数据库看来,这个数据时不存在的,因此,一般加锁无法屏蔽这个问题。这就会导致在可重复读的隔离级别下,会出现虽然大部分内容是可重复读的,但是insert的数据在可重复读下依然会被其他事务读到,导致在多次查找中,会对同一份数据查找到不同的记录,就如果产生了幻觉一般。这种现象,就被称为“幻读”

很明显,从上面的实验来看,mysql中并不存在幻读问题,这也就是说,mysql的RR级别下是解决了幻读问题的

7. 串行化(Serializable)

串行化是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了幻读的问题。简单来讲,就是让每个事务都串行执行。实现方式就是在每个读的数据航上面加上共享锁,但这种方式可能会导致超时和锁竞争。(这种隔离级别太极端,实际生产基本不使用)

7.1 串行化测试

首先,将mysql的限制级别设置为串行化,并重新登录mysql:

准备如下一张user表:

准备好后,启动事务b,在里面执行一次查询操作:

查询没有问题。然后在另一个窗口启动事务a,在里面执行一次查询操作:

可以看到,也没有问题。但是,我们再在事务a中执行一次删除操作:

可以发现,此时事务a就被阻塞住了。然后我们继续在事务b中查询:

可以看到,事务b依然可以正常运行。但是事务a不行。因为在当前的mysql中,mysql判断有在事务a执行修改数据的操作之前,有事务正在执行查询操作,于是在串行化的隔离级别下,就禁止其他事务对数据进行修改。

 此时我们再结束事务b:

当事务b结束后,就可以看到事务a中的delete操作执行成功了:

再在另一个窗口查看一下当前的user表:

可以发现,此时依然无法看到事务a对数据delete后的结果。原因就是事务a还没有结束,要在事务a结束后,才能真正的将这个操作持久化。

通过上面的例子就可以发现,在mysql的串行化下,阻塞并不是sql语句上的阻塞,而是事务上的阻塞。

 8. 总结

(1)事务的隔离级别越高,安全性就越高,但同时数据库的并发性能也就越低。因此为了应对不同的场景,往往需要在两者之间找一个平衡点,用户自行选择需要的隔离级别。

(2)不可重复读的重点是修改和删除:同样的条件下,事务读取过的数据和再次读取出现来的值不同。幻读的重点则是在insert情况下,会导致可重复读失效,让事务读取同一份数据时得到不同的结果。

(3)在mysql中,默认的隔离级别是可重复读,一般情况下不要随意修改

(4)事务是有长短事务之分的。在实际的开发中,sql语句一般都是提前写好等待上层调用的。长短事务就是指不同事务中的sql语句的数量和需要执行的时间的不同导致完成一个事务的时间上有长短之分。而事务间相互影响,指的就是事务在并发执行时,即都没有commit的时候,可能对其他事务造成影响。

三、事务的一致性

通过上面的例子,其实就已经验证完了事务的原子性、隔离性和持久性。那事务的一致性体现在哪里呢?其实,mysql并没有为了维护事务的一致性做任何工作,事务的一致性就是靠事务的原子性、隔离性和持久性来维持的

事务执行的结果,必须使数据库从一个一致性状态变为另一个一致性状态。当数据库只包含事务成功提交时的结果时,数据库处于一致性状态。但如果系统运行的过程中出现异常,例如系统运行发生中断,导致某个事务尚未完成而被迫中断,此时这个未完成的事务中已完成的sql语句就已经将对数据库做的修改写入了数据库,此时数据库就处于一种不正确(不一致)的状态,因此,需要通过回滚来让数据回到未执行该事务之前,保持数据库的一致性。因此,一致性是通过原子性来保证的。

但是大家要知道,mysql终归只是一个工具,而使用这个工具的是人。这也就导致数据是否符合用于预期是和用户的操作有关的。即一致性和用户的业务逻辑强相关,一般mysql仅提供技术支持,一致性的保证还是要用户业务逻辑做支持。也就是说,一致性,本质上是由用户决定的

 举个例子,假设你要给你的朋友转账,但是转账的逻辑在写的时候出现了问题,程序员在写转账逻辑时,只写了扣除了转账放的钱,但并没有写增加接收方的钱。此时数据库的一致性就遭到了破坏。而此次破坏并不是数据库的问题,而是写上层应用的程序员,即用户的问题。

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

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

相关文章

Windows 10 安装 PostgreSQL 12.x 报错 ‘psql‘ 不是内部或外部命令 由于找不到文件libintl-9.dll等问题

目录 序言一、问题总结问题 1 psql 不是内部或外部命令,也不是可运行的程序或批处理文件。问题 2 “由于找不到文件libintl-9.dll,无法继续执行代码,重新安装程序可能会解决此问题。“1、卸载2、安装3、安装 Stack Builder (这个可…

Easyexcel简介及写、读操作

Easyexcel简介及写、读操作 一、背景二、简介三、引入依赖四、代码实现1.创建实体类2.写入excel操作3.读取文件操作3.1 指定excel对应索引3.2 设置监听器3.3 执行读取操作 一、背景 作为一个经常进行数据分析的后端人员,免不了面对各种报表,且在日常的工…

onTouchEvent浅析

我们接着上次的自定义星星来作讲解 当 onTouchEvent 返回 super.onTouchEvent ( false ) 时 public boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:Log.d("ACTION_DOWN","ACTION_DOWN:"event.getA…

Ansible-playbook(剧本)

Ansible-playbook(剧本) 一、playbook的构成 (1)Tasks:任务,即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行(2)Variables:变量(3)Templates&am…

mybatis复杂环境搭建-多对一的处理-一对多的处理

复杂环境搭建: 1.1建表: CREATE table teacher( id int(10) not null, name varchar(30) default null, primary key(id) )engineInnoDB default charsetutf8mb3;INSERT INTO teacher (id, name) VALUES (1, 何老师);create table student( id int(10)…

用户端App自动化测试

一、自动化用例录制 1、Appium Inspctor 功能介绍 UI 分析录制用例元素查找测试Attcah 已有的 session云测试 2、用例录制 1)获取 app 的信息 2)配置待测应用 3、获取 app 的信息 1)app 入口,两种方式获取: * 通…

Linux系统中MySQL主从复制

本节主要学习了MySQL Replication概述,优点,复制类型,复制方式,复制过程和复制过程的限制,部署MySQL主从异步复制的流程及问题解决。 目录 一、MySQL Replication概述 1、优点 二、MySQL复制类型 1.异步复制&#…

#vue3报错 Cannot read properties of null (reading ‘isCE‘)#

场景:使用 npm 安装依赖包的时候,如如安装 npm i xlsx npm i file-saver 重新运行报错 Cannot read properties of null (reading isCE)# 解决办法: 使用的vite vue 在vite.config.ts添加如下配置: dedupe: [ vue ]

LeetCode使用最小花费爬楼梯(动态规划)

使用最小花费爬楼梯(动态规划) 题目描述算法流程(方法一)编程代码优化代码算法流程(方法二)编程代码代码优化 链接: 使用最小花费爬楼梯 题目描述 算法流程(方法一) 编程代码 class Solution { public:int minCostClimbingStair…

【雕爷学编程】Arduino动手做(104)---16X16点阵汉字屏模块2

37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&am…

8种工程设计优化问题Matlab代码:多种智能优化算法求解对比

目录 一、8种工程设计优化问题 二、8种工程设计优化问题部分问题介绍 2.1 焊接梁设计 2.2 拉力压缩弹簧设计 2.3 三杆衍架设计问题 2.4 压力容器设计 2.5 减速器设计问题 三、多种智能优化算法分别求解部分工程设计优化问题 3.1 焊接梁设计 3.2 拉力压缩弹簧设计 3.…

数据结构基础之二叉树

文章目录 二叉树性质二叉树分类遍历二叉树如何判断是否为完全二叉树 二叉树是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此…

企业选择租用CRM还是一次性买断CRM?分别有哪些优势?

CRM是企业管理客户关系,提升销售业绩,实现业务增长的重要工具。市场上的CRM系统销售方式主要有两种——租用型和买断型。那么,租用CRM好还是一次性买断CRM好?本文将从以下几个方面进行分析: 1、什么是租用型CRM和买断…

shell中按照特定字符分割字符串,并且在切分后的每段内容后加上特定字符(串),然后再用特定字符拼接起来

文件中的内容&#xff0c;可以这么写&#xff1a; awk -F, -v OFS, {for(i1;i<‌NF;i){$i$i"_suffix"}}1 input.txt-F,&#xff1a;设置输入字段分隔符为逗号&#xff08;,&#xff09;&#xff0c;这将使awk按照逗号分割输入文本。-v OFS‘,’&#xff1a;设置输…

数据结构:栈和队列的实现和图解二者相互实现

文章目录 写在前面栈什么是栈栈的实现 队列什么是队列队列的实现 用队列实现栈用栈模拟队列 写在前面 栈和队列的实现依托的是顺序表和链表&#xff0c;如果对顺序表和链表不清楚是很难真正理解栈和队列的 下面为顺序表和链表的实现和图解讲解 手撕图解顺序表 手撕图解单链表 …

VMware虚拟机中配置静态IP

目录 环境原因基础概念VMnet网络IPV4网络私有地址范围Vmnet8的作用网路通信的过程解决方法1&#xff1a;修改k8s组件重新启动解决方法2&#xff1a;配置静态IP系统网卡设置设置虚拟机网关修改虚拟机网卡 环境 本机系统&#xff1a;windows11虚拟机系统&#xff1a;CentOS-7-x8…

【AutoGluon_03】保存模型并调用模型

在训练好autogluon模型之后&#xff0c;可以将模型进行保存。之后当有新的数据需要使用autogluon进行预测的时候&#xff0c;就可以直接加载原来训练好的模型进行训练。 import pandas as pd from sklearn.model_selection import train_test_split from autogluon.tabular im…

第九章:stack类

系列文章目录 文章目录 系列文章目录前言stack的介绍stack的使用成员函数使用stack 总结 前言 stack是容器适配器&#xff0c;底层封装了STL容器。 stack的介绍 stack的文档介绍 stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除…

数字孪生技术从哪些方面为钢铁冶炼厂提高管理效率?

数字孪生系统是一种数字化技术&#xff0c;可以将物理世界中的实体对象、过程和数据进行数字化建模&#xff0c;以实现对其的可视化、模拟和优化。在炼铁生产管控中&#xff0c;数字孪生系统可以为以下方面提供支持&#xff1a; 炼铁生产线的可视化和控制&#xff1a;通过数字…

Web3 叙述交易所授权置换概念 编写transferFrom与approve函数

前文 Web3带着大家根据ERC-20文档编写自己的第一个代币solidity智能合约 中 我们通过ERC-20一种开发者设计的不成文规定 也将我们的代币开发的很像个样子了 我们打开 ERC-20文档 我们transfer后面的函数就是transferFrom 这个也是 一个账号 from 发送给另一个账号 to 数量 val…