脏读,不可重复读,幻读讲解

  • 首先我们先讨论一下问题,是不是在ACID的保护下,数据就一定不会产生不一致的现象呢?
  • 在关系数据库库系统中,多个会话可以访问同一个数据库的同一个表的同一行,这样,对于数据而言,就意味着在同一个时间内,有多个会话可以对其施加操作(或读操作或写操作),读写操作施加的顺序不停以及事务A特性对事务结果的影响(或成功或失败,也就是要不提交要不中止)。这三种因素叠加在一起,会存在几种对数据有不同影响的情况
    1. 读-读操作

      • 如果同时只存在多个读操作,对于数据没有影响,也就是说读-读操作不影响数据的一致性,可以并发执行
    2. 读-写操作
      • 如果读写操作都存在,写在前读在后(如脏读现象),读在前写在后(如不可重复读现象),或者读在前写在后然后再读(如幻读),就可能因为数据被写而导致另外一个读操作的会话读到错误的数据。这个操作可以根据动作发生的先后顺序被细分为读-写操作,写-读操作
    3. 写-写操作
      • 如果同时存在多个写操作,写-写操作直接改变了同一时刻的语义,这就更不允许,所以写-写操作通常不允许并发执行,但是如果不做并发控制,写-写操作也会带来数据异常形象
  • 这三种情况的第二种,对应的SQL标准中定义的三种数据异常,注意这三种异常主要是针对某个事物的读操作而言的,我们看下SQL2003对于数据异常现象的定义
    • Dirty reads 脏读
    • A dirty read (aka uncommitted dependency) occurs when a transaction is allowed to read data from a row that has been modified by another running transaction and not yet committed.
    • 当一个事务被允许从另一个正在运行的事务修改而尚未提交的行中读取数据时,就会发生脏读
    • Non-repeatable reads 不可重复读
    • A non-repeatable read occurs, when during the course of a transaction, a row is retrieved twice and the values within the row differ between reads.
    • 不可重复读取发生在事务过程中,当一行被检索两次,且该行中的某些值在读取之间有不同时。
    • Phantom reads 幻读
    • A phantom read occurs when, in the course of a transaction, new rows are added or removed by another transaction to the records being read.
    • 当在事务过程中,另一个事务向正在读取的记录添加或删除新行时,就会发生幻读。
  • 请注意上面的标注,标注显示了脏读和不可重复读是以a row(一行)为单位,而幻读操作的是一个数据集合(零行到多行)
  • 下面我们看一下四个隔离级别分别解决了什么问题
  • 下面我们开始案例
    • 首先我们创建表,以及向表里面填充一些数据
    • Create Table: CREATE TABLE goods (
    • id int(11) NOT NULL AUTO_INCREMENT,
    • title varchar(32) NOT NULL DEFAULT ‘‘ COMMENT ‘商品名称‘,
    • classify tinyint(4) NOT NULL DEFAULT ‘0‘ COMMENT ‘商品类型‘,
    • PRIMARY KEY (id),
    • KEY idx_classify (classify)
    • ) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8
    • mysql> select * from goods;
    • +----+----------+----------+
    • | id | title | classify |
    • +----+----------+----------+
    • | 1 | 商品1 | 1 |
    • | 2 | 商品2 | 3 |
    • | 3 | 商品3 | 5 |
    • | 4 | 商品4 | 8 |
    • | 5 | 商品5 | 10 |
    • | 6 | 商品6 | 1 |
    • | 7 | 商品7 | 3 |
    • | 8 | 商品8 | 5 |
    • | 9 | 商品9 | 8 |
    • | 10 | 商品10 | 10 |
    • +----+----------+----------+
    • 10 rows in set (0.00 sec)
  • 脏读
    *
    • 我们可以看到T1执行step 5的时候,已经读取到了事务T2未提交的修改的行,这个时候就发生了脏读
  • 不可重复读
    • 在上图中,我们可以看到,T2执行了step 4,并且在step 5上看到了最后修改的数据,然后T1执行了step6 ,这时候读取到的还是旧值,因为T2还没有提交,那么这就解决了脏读问题,然后T2执行step 7提交,T1执行step8,这时候因为T2 已经提交了,所以看到了最新的值,那么这时候就发生了不可重复读问题
  • 幻读
    • 虽然T2在step 7提交了, 但T1在step8 查出来的title还是商品2,这说明解决了不可重复读问题,下面我们再看一下幻读问题是如何出现的
    • 我们可以看到T1在step 3的时候查出来是4条数据,接着T2执行了step 4,5,6 ,在这个过程,classify=3的数据是有5条的,接着T1执行了step7 看到了还是4条,接着执行了T1的step8,这时候T1的step9就看到5条了,就发生了幻读问题,幻读指的是执行同样的sql,第二次会返回之前不存在的行或者之前的行不见了,这就叫做幻读
    • 那么如何解决幻读问题呢,有两种方法
      1. 将隔离级别改为 SERIALIZABLE
      2. 隔离级别为 REPEATABLE READ,但是加上锁定读
    • 方案1:
      • 我们可以看到T2在step 4的时候就执行不下去,因为T1在step3的时候已经加了共享锁,然后T2在step4又去申请排他锁,因为T1没有释放classify=3行的锁,所以T2的step4最终因为锁等待超时而报错,那么在这种加锁的机制下,也就不存在幻读问题了
    • 方案2
      • 我们可以看到T2在step 4的时候就执行不下去,因为T1在step3的时候已经加了共享锁,然后T2在step4又去申请排他锁,因为T1没有释放classify=3行的锁,所以T2的step4最终因为锁等待超时而报错,那么在这种加锁的机制下,也就不存在幻读问题了
      • SELECT ... LOCK IN SHARE MODE
        • 在读取出来的记录上面加上一个共享锁,其他会话也可以读取这些记录,但在你的事务提交之前其他事务是不能修改这些记录的,如果这些行中的任何一行已被另一个尚未提交的事务更改,那么您的查询将一直等到该事务结束,然后使用最新值。
      • SELECT ... FOR UPDATE
        • 对于搜索查到的索引记录,锁定行和任何关联索引项,就像你为这些行发送了update语句一样,其他事务如果修改这些行将会被阻塞,例如执行SELECT ... LOCK IN SHARE MODE操作或者从某些事务隔离级别读取数据,一致读取忽略设置在读视图中的记录上的任何锁(老版本记录无法被锁定,它们通过在记录的内存副本中撤销日志来重建。)
  • 很多人不是很清除不可重复读和幻读的区别,下面我们解释一下
    • 首先,这两种异常对于T1来说,都是先读取了数据,之后因为T2“写”了数据而导致T1再次读取数据的时候出现了异常,但是对应不可重复读来说,T1读取的是一个存在的确定的一行数据,这个行数据被T1使用删除或者更新操作而改变,而幻读对应T1读取的是满足条件的多行数据,意味着这是一个范围找到,数据集是不确定的,所以从第一次读取数据的操作的角度看,前者是读取特定的行,后者读取的是多行,一行,或者零行,其次这两种异常,对于T2来说,都是’写’数据,但是写操作的具体动作不同,不可重复读对T2的写操作是更新或者删除操作,而幻读对于T2的写操作是插入(插入的新条件满足where条件)或更新(使不满足where条件的数据在更新后满足where条件)操作,而且不可重复读和幻读最大的区别是前者只需要“锁住”已经读过的数据,而幻读需要对“不存在的数据”做出预防
  • 参考书籍:数据库事务处理的艺术 事务管理与并发控制

    原文地址:https://blog.51cto.com/itzhoujun/2357093

    时间: 2024-10-11 23:30:22

    脏读,不可重复读,幻读讲解的相关文章

    mysql事务隔离级别/脏读/不可重复读/幻读详解

    一.四种事务隔离级别 1.1 read uncommitted 读未提交 即:事务A可以读取到事务B已修改但未提交的数据. 除非是文章阅读量,每次+1这种无关痛痒的场景,一般业务系统没有人会使用该事务隔离级别,标准实在太宽松了. 1.2 read committed 读已提交(简称RC) 即:事务A只能读取到事务B修改并已提交的数据. 这个级别相对要严格一些,至少是要等其它事务把变更提交到db,才能读取到,听上去蛮靠谱的.但是有些业务场景,比如会员系统中,如果要在一个事务中,多次读取用户身份,判

    数据库并发导致的脏读 不可重复读 幻读的处理方法

    在做ERP订单接入仓库库存的时候出现了一个问题.下单会读取该商品是否有库存.如果有那么就下单 减去(可下单库存,物理库存不变) 没有则提示库存不足 在并发情况下就会导致 u1用户下单编号为1的商品1个  u2用户也下单编号为1的商品1个.编号为1的商品库存只有1个. 那么当u1在读取出数据的时候发现库存有一个 但是还没有写入库存的操作   u2也下单成功并提交  那么就会导致数据错乱 本来应该是只有u1会下单成功u2则提示库存不足 这里可以用事物的隔离机制或乐观锁来解决 事物是一个逻辑单元 原子

    [MySQL]对于事务并发处理带来的问题,脏读、不可重复读、幻读的理解与数据库事务隔离级别 - 分析脏读 & 不可重复读 & 幻读

    刚开始写博客.. 写的太low. 1.数据库的两种读,每种读读的数据版本不一样,所以也称为MVCC,即多版本并发控制 a) 快照读 select * from where xxx  这种形式的都是快照读. b) 当前读 update , insert ,delete ,select xx from xx for update ,  in share mode 都是当前读 当前读会等待,不会返回数据的历史版本 2.mvcc 的实现原理 mvcc是基于read view.活跃事务列表 做的,以后的文

    脏读 不可重复读 幻读

    脏读(Dirty Read)     脏读意味着一个事务读取了另一个事务未提交的数据,而这个数据是有可能回滚 不可重复读(Unrepeatable Read) 不可重复读意味着,在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据.这是由于查询时系统中其他事务修改的提交而引起的. 例如:事务B中对某个查询执行两次,当第一次执行完时,事务A对其数据进行了修改.事务B中再次查询时,数据发生了改变 幻读(phantom read) 幻读,是指当事务不是独立执行时发生的一种现象,例如第一个事务对

    脏读 不可重复读 幻读以及第一、二类丢失更新

    http://blog.csdn.net/gaoshan_820822/article/details/4582561 脏读(Dirty Read) 脏读意味着一个事务读取了另一个事务未提交的数据,而这个数据是有可能回滚. 不可重复读(Unrepeatable Read) 不可重复读意味着,在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据.这是由于查询时系统中其他事务修改的提交而引起的. 例如:事务B中对某个查询执行两次,当第一次执行完时,事务A对其数据进行了修改.事务B中再次查询时

    理解数据库事务隔离级别以及脏读, 不可重复读, 幻读(转)

      转自:http://blog.csdn.net/sunxing007/article/details/6427290 数据库事务的4个特性: 原子性(atomic): 都成功或者都失败: 一致性(consistency):事务操作之后,数据库所处的状态和业务规则是一致的;比如a,b账户相互转账之后,总金额不变: 隔离性(isolation):操作中的事务不相互影响; 持久性(durability):事务提交后被持久化到数据库. 脏读,不可重复读, 幻读 幻读phantom read:事务1读

    数据库事务隔离级别 - 分析脏读 & 不可重复读 & 幻读

    一 数据库事务的隔离级别 数据库事务的隔离级别有4个,由低到高依次为Read uncommitted .Read committed .Repeatable read .Serializable ,这四个级别可以逐个解决脏读 .不可重复读 .幻读这几类问题. 1. Read UnCommitted(读未提交) 最低的隔离级别.一个事务可以读取另一个事务并未提交的更新结果. 2. Read Committed(读提交) 大部分数据库采用的默认隔离级别.一个事务的更新操作结果只有在该事务提交之后,另

    商品分类-商品详情-数据库脏读-不可重复读-幻读-订单生成

    商品详情的无限极分类 --北京 --朝阳区 ---上海 --松江 --大学城 def get_level(data): data_list=[] for item in data: if item['parent_id']==0: item['level']=0 else: item['level']=1 data_list.append(item) return data_list data=[ {"cat_id":1,"name":"北京",

    了解数据库中脏读+不可重复读+幻影读

    "读现象"是多个事务并发执行时,在读取数据方面可能碰到的状况.先了解它们有助于理解各隔离级别的含义.其中包括脏读.不可重复读和幻读. 脏读: 脏读又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的. 脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交(commit)到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据.因为这个数据是还没有提交