mysql系列:加深对脏读、脏写、可重复读、幻读的理解

关于相关术语的专业解释,请自行百度了解,本文皆本人自己结合参考书和自己的理解所做的阐述,如有不严谨之处,还请多多指教。

**不可重复读的重点是修改: **同一事务,两次读取到的数据不一样

幻读的重点在于新增或者删除同样的条件 , 第 1 次和第 2 次读出来的记录数不一样

脏读:
强调的是第二个事务读到的不够新

事务有四种基本特性,叫ACID,它们分别是:

Atomicity-原子性,Consistency-一致性,Isolation-隔离性,Durability-持久性。

 

接着关于ACID的理解和隔离性语法都是转的网上资料,大家可以顺便再了解熟悉下。

1、原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。

2、一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。

3、隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。

4、持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

 

而其中的隔离性特点,说的就是在并发的多个事务中事务之间是互不影响的这种情形。Mysql里支持四种不同的隔离级别,这也为解决并发问题提供了选择。

 

为了更好的理解隔离级别,我们需要给每个会话设置不同的隔离级别,从而辅助自己实践。

相关语法:

SET GLOBAL TRANSACTION ISOLATION LEVEL ;
SET SESSION TRANSACTION ISOLATION LEVEL ;
SET TRANSACTION ISOLATION LEVEL ; 

上面语法设置的值选项就是mysql的四种隔离级别:

READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE

 

由于mysql默认隔离级别是可重复读(Repeatable Read):

show variables like '%tx_isolation%';  //查询数据库当前的隔离级别

所以实践过程中咱们需要给会话设置隔离级别,就如下所示:

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED

 

其中全局事务(global transaction)的隔离级别设置,是对现有已经建好的会话是没有影响的。

set global tx_isolation='READ-COMMITTED'
select @@tx_isolation;
show variables like 'tx_isolation';

注意:设置的全局默认事务隔离级别适用于从设置时起所有新建立的会话连接。现有连接不受影响。

有关实践过程,这里不再赘述,请参考这位博主的文章:MySQL的四种事务隔离级别

 

接着主要着重帮助自己加强对脏读、脏写、可重复读、更新丢失、幻读、写偏离等的理解。

在此先上一张隔离级别的对比图:

(图中红框仅表示提醒)

 

脏读:

如果一个事务A向数据库写了数据,但事务还没提交或终止,另一个事务B就看到了事务A写进数据库的数据,这就是脏读。

 

经过前面的实践,就能得知,在读未提交(Read Uncommitted)隔离级别下,是会出现脏读的。

仔细体会读未提交(Read Uncommitted)隔离级别的命名--读取事务还未提交的数据,就会发现说的就是脏读。

 

脏读会导致什么问题呢?

1. 给用户带来数据混乱的感觉。

例如在一个多对象的事务A里,A需要生成一条邮件发送记录,同时需要在用户未读取邮件的计数里+1,这里涉及两张表的业务情形,就是对多对象的诠释。如果事务A insert邮件发送记录时(还没执行计数+1这个后面的操作),就被事务B查询了,可事务B此时看到的邮件计数还是+1之前的,这样就会导致事务B看到的未读取邮件条数与计数数据不一致。

 

2. 让用户看到根本不存在的数据。

例如事务A 是转账业务,由乔峰转给段誉,在更新段誉账户余额时(假定此时事务A还未提交),事务B下段誉正查询自己账户余额,发现乔峰给自己转账了。可是事务A下乔峰突然意识段誉是大理太子,家底丰实得很,于是撤销了转账(事务A回退或终止)。整个过程,段誉就看到了根本不存在的转账记录。本以为钱来了,结果还没眨眼就没了,你说用户是段誉,生不生气。

 

脏写:

当两个事务同时尝试去更新某一条数据记录时,就肯定会存在一个先一个后。而当事务A更新时,事务A还没提交,事务B就也过来进行更新,覆盖了事务A提交的更新数据,这就是脏写。

 

文上提到的4种隔离级别下,都不存在脏写情况。因为在这些隔离级别下,当两个事务A和B尝试去更新同一条数据时,假定A先更新数据,会对更新的数据行记录加上排他锁(也叫写锁,悲观锁),除非事务A提交或终止从而释放排他锁,否则事务B都是无法更新数据的。(设计数据密集型应用只是说读提交隔离级别一定可以杜绝脏写问题,并未提到读未提交隔离级别,经过实践,读未提交下事务B的更新操作也是需要等待事务A的排他锁释放,才得以执行)

 

脏写会带来什么问题呢?

脏写是会导致更新丢失的一种情形,具体会带来什么问题,可看后面的更新丢失这块内容。

 

可重复读:

我本来认为,不可重复读之下的结果也正是所谓的正确结果,也就没必要去避讳。就如下图里的Alice,只要再查询下Account1下的余额,就可以拼出正确的总额600+400 = 1000。

 

如图所示,假设转账行为是由银行操控的,Alice一开始看两个账户的总额是500+500,后又无意看到Account2账户变为了400,这种Alice纳罕总额怎么从1000变为了900的现象,我们就称之为读偏离(read skew)。但我们知道,Alice的Account2下的钱的的确确是400,并没有说少了的100元被谁给私吞了。所以说,这种现象勉强还是可以接受的,毕竟Alice的钱也没变少,只要再查询一次Account1,就能释疑了。

 

那Mysql为啥默认级别是可重复读呢,不是读提交呢,说明可重复读还是有非常的必要。通过以下几点可以看出:

1. mysql分布式,多节点同步数据时,可重复读可以保证多个节点数据的一致性。具体请参考下图:

image

图中有两个从库A和B,主库同步数据时,会有多个事务并发的执行,由于不可重复读的特点,就会导致从库A同步到的数据里我的余额是100元,而从库B里我的余额数据是90元,从而导致AB两个从库之间以及主库和从库A之间数据的不一致。

 

2. 备份数据库时,不可重复读会导致备份一部分是旧数据一部分是更新后的新数据,从这样的备份来恢复数据,就会导致数据的不一致(例如钱变少了,此点本人也不是很清楚,大概了解即可)。

 

3.对于分析查询,需要的就是遍历大量数据来进行分析和数据的完整性检查。如果是不可重复读,就会导致一前一后数据不一致,影响到分析结果。

 

更新丢失:

当多个事务并发写同一数据时,先执行的事务所写的数据会被后写的覆盖,这也就是更新丢失。前面的脏写情形,就属于会导致更新丢失问题的一种情形。

除了这个,更新丢失主要发生在read-modify-write类型的事务当中:就是要先查询数据,然后计算新的数据,最后写回新的数据。下面是几个具体的情形例子:

1. 数值更新,例如计数或账户余额更新(先要查询当前值,再计算出要更新的值,最后执行更新操作写进数据库)

2. 更新一个复杂的数据,例如要往json对象里添加数据。(先查询获取json对象数据进行解析,再添加数据得到新的数据写回数据库)

3. 两个用户同时编辑Wiki保存wiki内容。

 

结合读未提交和读提交的区别就可知道,带来更新丢失的根本原因:

在读提交以及更高级的隔离级别下,只要事务A没有提交,事务B永远也无法查到事务A所做的更新,从而事务B在计算要更新的数据时,必定忽略掉了事务A所产生的变更。

在读未提交下实践,只要事务B的查询操作是发生在事务A的更新操作之后,就不会有更新丢失问题。但前提是要保证,事务B的查询操作是发生在事务A的更新操作之后。这很难控制,所以说读未提交下也是需要应对更新丢失问题的。

 

针对这个问题,数据库给了一些解决方法:

1.原子写操作。

就是将上面的read-modify-write情形下的3步骤,直接转化为1个步骤来执行。下面就是两种情形的mysql语句对比:

原子写操作(atomic write operations):
update news set counter = counter + 1 where id = 1;
查询-计算-更新(read-modify-write):
select counter from news;
new_counter = counter + 1 //此行逻辑由程序语言代码(php,java等)执行
update news set counter = new_counter where id = 1;

用过PHP框架的就知道,框架原本支持的都采用查询-计算-更新这种方式,下面是phalcon框架的例子:

use PhalconMvcModelTransactionFailed as TxFailed;
use PhalconMvcModelTransactionManager as TxManager;$m = new TxManager();
$t = $m->get();$model_wallet =  new Wallet();
$row = $model_wallet->findFirst("user_id='".$uid."'");$model_wallet->setTransaction($t);
$row->money  = $row->money + 100;
$row->operation_time  = date("Y-m-d H:i:s");if(!$row->save()){$t->rollback();
}else{$t->commit();
}

所以使用框架时,就要注意这种写法在并发情形下带来的潜在数据更新丢失的问题。

 

2.加锁(Explicit locking,显示锁定)

通过for update来给即将更新的数据记录添加锁。也就是说事务A下执行查询时,用select for update,那么事务B下的select for update就无法进行,只有等待事务A提交或终止,事务B才得以进行。这样就相当于将并发的两个事务给串行化了,事务B查询的结果一定是在事务A提交之后,从而解决了数据更新丢失问题。

 

mysql下有select ... for update 和select ... lock in share mode两种显示加锁的语句,具体用途这里就不拓展了。

 

3.自动检测

方法1和2实质上都是将事务给串行化了。自动检测说的就是当检测到事务A造成更新丢失问题,就立即终止事务A,让事务A再一次尝试查询-计算-更新的流程,事务仍然是并行执行的。

PostgreSQL的可重复读,Oracle的串行以及SQL Server的快照隔离能够自动检测更新丢失,但Mysql的Innodb引擎下的可重复读没有此功能。

 

4.CAS,比较和设置(compare and set)

说简单点,就是在sql更新语句里加一个判断原本旧数据的条件,例如:

update news set counter = 30 where id = 1 and counter = 29;  //29是一开始的文章点赞数

如果两个事务A和B,现在都要对id=1的新闻文章进行点赞操作,只要其中任何一个事务执行了更新操作,另一个事务执行时counter=29的条件都不会满足,从而就规避了更新丢失的问题。

 

但是有的数据库where条件里counter获取的本就是旧的快照数据,即不是某一个事务更新后的新数据,那这里的更新丢失问题还是要发生的了。

经过实践,mysql下可重复读隔离级别下,使用此方法,的确可以避免更新丢失问题。一旦事务A提交(对counter做更新),事务B里的类似counter=29的判断就不满足了,如此一来,事务B的写入操作就没有执行成功,就更不用说造成数据的更新丢失了。(实践里,事务A不提交,事务B里的更新操作会一直等待事务A的排它锁释放,否则是不会执行的)

 

好啦,最后就来说说幻读的问题。

 

幻读(phantom):

看网上很多博客,都对幻读的理解不太准确。他们的理解可见此链接进行了解:MySQL的InnoDB的幻读问题

通过提交事务B之后来发现事务A出现莫名奇妙的数据遗失或数据增多,这个实践中已经涉及到事务B的提交操作,这也就已经确定了对幻读的误解。

 

当事务A和B各自写入一笔数据(不像更新丢失里事务A和B是往同一笔记录写入数据),破坏了潜在的竞争条件,造成的结果我们称之为“写偏离”(write skew),而造成这种结果的事务B里的查询操作的结果,才是我们所说的幻读。(至于幻读的前提是不是得一定导致了写偏离,这个待确定,现姑且当做是的)

 

由上可见,幻读出现的前提是出现了写偏离,而出现写偏离是只有当事务A和B都是read-write型事务时才会出现的,这也是为何有的书上说快照隔离级别下read-only型事务是没有幻读的,read-write型事务才导致幻读。

 

什么样的结果是写偏离?

如图所示,一个医院每个班次必须有一个医生值班,所以每个医生请假的前提是当下该班次必须至少有两个医生值班,可是图中Alice和Bob很遗憾的同时点击了提交请假的按钮,导致最终1234班次无人守班,这就是破坏了潜在的竞争条件----必须至少要有一人值班,造成的这种结果,就是我们上述所说的写偏离。

 

假设Alice下的事务是先执行的,那么Bob下执行的查询1234班次在班的医生人数结果导致Bob也能请假,这就对潜在的竞争条件造成了破坏,我们就称Bob下事务的查询操作带来了幻读现象。

 

我们再总结一下写偏离和更新丢失的区别:

更新丢失是多个事务并发写同一笔数据记录造成的。 而写偏离是多个事务并发写不同数据记录影响到了潜在的竞争条件而造成的。

写偏离的情景还有:

1. 抢注用户名,两个用户同时抢注某一个用户名并且都成功了,破坏了用户名必须唯一性的潜在竞争条件。

2. 游戏里多人移动不同人物到同一位置,破坏了某一时刻某一位置只允许一个人物的潜在竞争条件。

 

那针对这些写偏离问题,该如何解决呢?

上文中的医生请假问题,我们可以用select...for update,就保证了第二个事务执行更新操作时必须先等待第一个事务释放排它锁。

可是这方法对于游戏移动人物位置就不适用了,因为select查询有结果才能用for update来加排它锁,而游戏里移动人物select操作结果是要保证在某一时刻某一位置必须没有人物,也就是select查询根本没有结果,就更不用谈加排它锁了。

 

针对这种情形,有一种物化冲突(Materialiing conflicts)的解决方法。

就是既然select查询没有结果供添加排它锁来保证串行执行,那我就想方法让select查询有结果。

针对多人游戏这个例子,假设画面是1280*720且由1*1的像素组成的屏幕,游戏人物有貂蝉、吕布、虞姬和项羽,时间维度以秒为单位,游戏开始时间从0开始计时。如此下来,我们就可以先创建一张表的数据如下:

时间x轴y轴英雄
111貂蝉
111吕布
111虞姬
111项羽
211貂蝉
211吕布
211虞姬

将任意秒任意位置可能出现的英雄情形全都列举出来,在多个事务并发移动英雄人物时,就给某一时间某一位置的记录加上for update,以上表只提供添加排它锁,不做实际修改和更新。例如:

//如此就能保证在第1秒(1,1)这个同一位置绝对不会出现多个英雄
select * from table_name where time =1 and x = 1 and y =1 for update  

当然解决这个问题,还有一种方法就是采用串行化隔离级别了,也是最高的隔离级别,简单理解就是严格确保了事务串行执行,避免了脏读幻读现象,但是由于性能问题,实际生产环境很少用到。这个我以后再好好了解,本文就不细说了。

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

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

相关文章

重读经典(点云深度学习开山之作):《Deep learning on point clouds for 3D scene understanding》(持续更新中)

本文介绍的是 PointNet 作者的博士论文:3D场景理解中的点云深度学习。从上图可以看到,整个博士论文主要贡献有两块:一是点云深度学习的网络架构(PointNet 和 PointNet);二是在3D场景理解中的应用&#xff0…

Coursera自动驾驶课程第17讲:An Autonomous Vehicle State Estimator

在第16讲《Coursera自动驾驶课程第16讲:LIDAR Sensing》我们学习了自动驾驶目前常用的3D 传感器,激光雷达,了解了激光雷达的工作原理,掌握了对点云数据的操作以及如何使用点云配准方法来进行汽车定位。 回顾一下,在本…

!何为脏读、不可重复读、幻读

2.0、前言 事务的隔离性是指多个事务并发执行的时候相互之间不受到彼此的干扰的特性,隔离性是事务ACID特性中的I,根据隔离程度从低到高分为Read Uncommitted(读未提交),Read Committed(读已提交&#xff0…

【转】JPA、Hibernate和Mybatis区别和总结

很多人都用过java的数据库连接池C3P0,但官方没有说明名称的由来。 据传闻:连接池作者是《星球大战》迷,C3P0就是其中的一个机器人,并且这个名称中包涵connection 和pool的单词字母。因此叫这个名字(根据网友提醒&…

详解3D物体检测模型: Voxel Transformer for 3D Object Detection

本文介绍一个新的的3D物体检测模型:VoTr,论文已收录于ICCV 2021。 这是第一篇使用 voxel-based Transformer 做3D 主干网络,用于点云数据3D物体检测。由于有限的感受野,传统的 3D 卷积网络检测器(voxel-based&#xff…

一步步编写操作系统 65 标准调用约定stdcall 汇编实战

因为c语言遵循的调用约定是cdecl,咱们也自然要遵守cdecl约定了。不过为了起到对比的作用,除了介绍cdecl外,也会介绍下stdcall。 既然咱们用的是调用约定是cdecl,那对它的介绍最好让它离下一节的内容近一些,所以先说一…

Coursera自动驾驶课程第18讲:The Planning Problem

在第17讲《Coursera自动驾驶课程第17讲:An Autonomous Vehicle State Estimator》 我们学习了如何使用多传感器融合进行自车定位,以及传感器的内外参标定和时间同步,我们还讨论了在实际应用中常遇到的问题。 从本讲开始我们将学习一个新的模…

详解3D物体检测模型:Focal Sparse Convolutional Networks for 3D Object Detection

用于3D目标检测的焦点稀疏卷积神经网络【CVPR2022】【3D检测】本文介绍一篇新的 3D 物体检测模型:Focals Conv,论文收录于 CVPR2022。在 3D 检测任务中,点云或体素数据不均匀地分布在3维空间中,不同位置的数据对物体检测的贡献是不…

地平线:面向规模化量产的自动驾驶感知研发与实践

导读 4月27日,地平线智能驾驶感知研发部负责人苏治中就 《面向规模化量产的自动驾驶感知研发与实践》 这一主题进行了直播讲解。 本次课程内容分为4个部分: 1、地平线自动驾驶环境感知量产实践 2、软硬协同的自动驾驶感知算法设计 3、实现规模化量产的“…

Power BI与Power Query、Power Pivot 是什么关系?

搞不清楚Power BI与Power Query、Power Pivot是什么关系?看这篇文章就够了。 刚开始学习PowerBI的时候,总是能碰到Power Query和Power Pivot这两个词(下文简称为PQ和PP),现在中文里面学习PowerBI的资源本来就不是很多&#xff0c…

地平线:上帝视角与想象力——自动驾驶感知的新范式

导读 3月28日,在「地平线自动驾驶技术专场」上,地平线自动驾驶系统架构师刘景初博士围绕《上帝视角与想象力——自动驾驶感知的新范式 》这一主题进行了直播讲解。 本次课程主要分为以下4个部分: 1、自动驾驶结构演化提出算法新需求 2、软件2…

详解Class类文件的结构(上)

前言 相信搞Java开发的同学都经常会接触到Class类文件,了解了JVM虚拟机之后也会大量接触到class字节码,那么它到底是什么样的文件?内部由什么构成?虚拟机又是如何去识别它的?这篇文章就来学习一下Class类文件的结构。…

Coursera自动驾驶课程第19讲:Mapping for Planning

在第18讲 《Coursera自动驾驶课程第18讲:The Planning Problem》 我们对自动驾驶中的规划问题有了一个全面的了解,理解了规划问题中的约束和目标;同时我们还讨论了如何分层如解决规划问题(任务规划、行为规划、路径规划和速度曲线…

详解Class类文件的结构(下)

本文继续使用上次的Test.class文件,它是由下面单独的一个类文件编译而成的,没有包。 6. 索引(Index) 索引又分类索引、父类索引和接口索引集合,类索引(this_class)和父类索引(super…

自动驾驶开源软件和算法库

1. Carla(自动驾驶开源仿真软件) github:https://github.com/carla-simulator/carladoc:https://carla.readthedocs.io/en/latest/website:http://carla.org/Bounding boxes:https://carla.readthedocs.io/…

Coursera自动驾驶课程第20讲:Mission Planning in Driving Environments

在第19讲《Coursera自动驾驶课程第19讲:Mapping for Planning》 我们学习了自动驾驶中两种环境建图方法:占用网格图(occupancy grid map) 和 高清地图(high-definition road map)。 在本讲中,我…

Java实例化对象过程中的内存分配

问题引入 这里先定义一个很不标准的“书”类,这里为了方便演示就不对类的属性进行封装了。 class Book{String name; //书名double price; //价格public void getInfo(){System.out.println("name:"name";price:"price);} } 在这个类中定义了两个属…

【Python学习】 - sklearn学习 - KNN

前言: 针对一个完整的机器学习框架目前还没有总结出来,所以目前只能总结每一个单独的算法。由于现在研究的重点是算法,所以对于数据的处理,数据的分析和可视化呈现,在现阶段并不进行展示(这样容易陷入纠结…

重读经典:《End-to-End Object Detection with Transformers》

DETR 论文精读【论文精读】这一次朱毅博士给大家精读的论文是 DETR,是目标检测领域里程碑式的一个工作,文章收录于 ECCV20 。DETR 是 Detection Transformer 的缩写,作者使用 Transformer 简化了目标检测流程,不再需要进行 NMS&am…

Execute SQL Task 参数和变量的映射

Execute SQL Task能够执行带参数的SQL查询语句或存储过程(SP),通过SSIS的变量(Variable)对参数赋值。对于不同的Connection Manager,在Task中需要使用不同的符号(Parameter marker)来…