脏读、不可重复读、幻读

锁就是防止其他事务访问指定的资源的手段。锁是实现并发控制的主要方法,是多个用户能够同时操纵同一个数据库中的数据而不发生数据不一致现象的重要保障。一般来说,锁可以防止脏读、不可重复读和幻觉读。

 

事务并发产生的问题:
        脏读:一个事务读取到了另外一个事务没有提交的数据
            事务1:更新一条数据
                             ------------->事务2:读取事务1更新的记录
            事务1:调用commit进行提交
           
            ***此时事务2读取到的数据是保存在数据库内存中的数据,称为脏读。
            ***读到的数据为脏数据
            详细解释:
                脏读就是指:当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,
                另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个
                事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
           
        不可重复读:在同一事务中,两次读取同一数据,得到内容不同
            事务1:查询一条记录
                            -------------->事务2:更新事务1查询的记录
                            -------------->事务2:调用commit进行提交
            事务1:再次查询上次的记录
           
            ***此时事务1对同一数据查询了两次,可得到的内容不同,称为不可重复读
           
        幻读:同一事务中,用同样的操作读取两次,得到的记录数不相同
            事务1:查询表中所有记录
                              -------------->事务2:插入一条记录
                              -------------->事务2:调用commit进行提交
            事务1:再次查询表中所有记录
           
            ***此时事务1两次查询到的记录是不一样的,称为幻读
            详细解释:
                幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,
                这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表
                中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,
                就好象发生了幻觉一样。

处理以上隔离级别的问题,采用如下方是:

  事务隔离五种级别:
        TRANSACTION_NONE  不使用事务。
        TRANSACTION_READ_UNCOMMITTED  允许脏读。
        TRANSACTION_READ_COMMITTED  防止脏读,最常用的隔离级别,并且是大多数数据库的默认隔离级别
        TRANSACTION_REPEATABLE_READ  可以防止脏读和不可重复读,
        TRANSACTION_SERIALIZABLE  可以防止脏读,不可重复读取和幻读,(事务串行化)会降低数据库的效率

  以上的五个事务隔离级别都是在Connection接口中定义的静态常量,

  使用setTransactionIsolation(int level) 方法可以设置事务隔离级别。
        如:con.setTransactionIsolation(Connection.REPEATABLE_READ);

  注意:事务的隔离级别受到数据库的限制,不同的数据库支持的的隔离级别不一定相同

 

      1 脏读:修改时加排他锁,直到事务提交后才释放,读取时加共享锁,读取完释放事务1读取数据时加上共享锁后(这样在事务1读取数据的过程中,其他事务就不会修改该数据),不允许任何事物操作该数据,只能读取,之后1如果有更新操作,那么会转换为排他锁,其他事务更无权参与进来读写,这样就防止了脏读问题。

       但是当事务1读取数据过程中,有可能其他事务也读取了该数据,读取完毕后共享锁释放,此时事务1修改数据,修改完毕提交事务,其他事务再次读取数据时候发现数据不一致,就会出现不可重复读问题,所以这样不能够避免不可重复读问题。

      2 不可重复读:读取数据时加共享锁,写数据时加排他锁,都是事务提交才释放锁。读取时候不允许其他事物修改该数据,不管数据在事务过程中读取多少次,数据都是一致的,避免了不可重复读问题
      3 幻读问题:采用的是范围锁RangeS RangeS_S模式,锁定检索范围为只读,这样就避免了幻影读问题,在这里有个描述范围锁的文章

 

当执行不同的隔离级别时,可能会发生各种各样不同的问题。下面对它们进行总结并举例说明:

幻读:幻读发生在当两个完全相同的查询执行时,第二次查询所返回的结果集跟第一个查询不相同。

发生的情况:没有范围锁。

例子:

事务1 事务2
<span class="kw1">SELECT</span>
 * <span class="kw1">FROM</span>
 users

<span class="kw1">WHERE</span>
 age <span class="kw1">BETWEEN </span>
<span class="nu0">10 AND 30 </span>
;
 
 
<span class="kw1">INSERT</span>
 <span class="kw1"> INTO</span>
 users <span class="kw1">VALUES</span>
(<span class="nu0">3</span>
, <span class="st0">'Bob'</span>
, <span class="nu0">27</span>
 <span class="br0">)</span>
;
<span class="kw1">SELECT</span>
  * <span class="kw1">FROM</span>
 users<span class="kw1"> 

WHERE</span>
 age <span class="kw1">BETWEEN</span>
 <span class="nu0">10</span>
 <span class="kw1">AND</span>
 <span class="nu0">30</span>
;

 

 如何避免:实行序列化隔离模式,在任何一个低级别的隔离中都可能会发生。

 

 

不可重复读

在基于锁的并行控制方法中,如果在执行select时不添加读锁,就会发生不可重复读问题。

在多版本并行控制机制中,当一个遇到提交冲突的事务需要回退但却被释放时,会发生不可重复读问题。

 

事务1 事务2
<span class="kw1">SELECT</span>
 * FROM users WHERE id=1;
 
 
<span class="kw1">UPDATE</span>
 users <span class="kw1">SET</span>
 age = <span class="nu0">21</span>

<span class="kw1">WHERE</span>
 id = <span class="nu0">1</span>
;

COMMIT; <span class="coMULTI">/* in multiversion concurrency*/

   control, or lock-based READ COMMITTED *</span>
<span class="kw1">SELECT</span>
 * <span class="kw1">FROM</span>
 users 

<span class="kw1">WHERE</span>
 id = <span class="nu0">1</span>
;
 
 
COMMIT; <span class="coMULTI">/* lock-based REPEATABLE READ */</span>

 

在上面这个例子中,事务2提交成功,它所做的修改已经可见。然而,事务1已经读取了一个其它的值。在序列化和可重复读的隔离级别中,数据库管理系统会返回旧值,即在被事务2修改之前的值。在提交读和未提交读隔离级别下,可能会返回被更新的值,这就是“不可重复读”。

 

有两个策略可以防止这个问题的发生:

1. 推迟事务2的执行,直至事务1提交或者回退。这种策略在使用锁时应用。

2. 而在多版本并行控制中,事务2可以被先提交。而事务1,继续执行在旧版本的数据上。当事务1终于尝试提交时,数据库会检验它的结果是否和事务1、事务2顺序执行时一样。如果是,则事务1提交成功。如果不是,事务1会被回退。

 

脏读

脏读发生在一个事务A读取了被另一个事务B修改,但是还未提交的数据。假如B回退,则事务A读取的是无效的数据。这跟不可重复读类似,但是第二个事务不需要执行提交。 

 

事务1 事务2
<span class="kw1">SELECT</span>
 * FROM users WHERE id=1
 
 
<span class="kw1">UPDATE </span>
users SET age=21

WHERE id=1;
<span class="kw1">SELECT</span>

 * FROM users WHERE id=1;
 
 
COMMIT; <span class="coMULTI">/* lock-based DIRTY READ */</span>
时间: 2025-01-26 20:43:46

脏读、不可重复读、幻读的相关文章

.NET中 关于脏读 不可重复读与幻读的代码示例_实用技巧

并发可能产生的三种问题 脏读 定义:A事务执行过程中B事务读取了A事务的修改,但是A事务并没有结束(提交),A事务后来可能成功也可能失败. 比喻:A修改了源代码并且并没有提交到源代码系统,A直接通过QQ将代码发给了B,A后来取消了修改. 代码示例 复制代码 代码如下: [TestMethod]         public void 脏读_测试()         {             //前置条件             using (var context = new TestEnti

事务 脏读、不可重复读、幻影读的分析

1 丢失修改 2 脏读:当事务1修改了一条记录,没有提交时,事务2读取了该记录:当事务1回滚了,那么事务2的记录就是一条不存在的记录: 3 不可重复读:当事务1读取了一条记录,未提交事务,事务2修改了该条记录并且提交事务:事务1又读取了该条记录,发现两条记录不一样: 4 幻影读:当事务1根据某种检索条件读取了若干条记录,未提交事务:而事务2又插入了一条记录,该记录也符合事务1的检索条件:那么当事务1在根据相同查询条件检索数据时候,出现了不一致的现象. 根据锁机制来避免上诉问题: 排他锁:数据加锁

SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因

原文:SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因 原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中的事务概念,ACID 原则,事务中常见的问题,问题造成的原因和事务隔离级别等这些方面的知识好好的整理了一下. 其实有关 SQL Server 中的事务,说实话因为内容太多, 话题太广,稍

【MySQL】可重复读下的幻读

[背景] 在研究gap lock的时候,参考了一篇文章http://www.mysqlperformanceblog.com/2012/03/27/innodbs-gap-locks/按照文章的实验来进行测试并不会出现幻读. [概念] 幻读(Phantom Read) 是指当用户读取某一范围的数据行时,B事务在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的"幻影"行.InnoDB和Falcon存储引擎通 过多版本并发控制机制解决了幻读问题. [验证] 做一个小的测试来验

Mysql事务,并发问题,锁机制-- 幻读、不可重复读(转)

1.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库正确地改变状态后,数据库的一致性约束没有被破坏 持久性:事务的提交结果,将持久保存在数据库中   2.事务并发会产生什么问题 1)第一类丢失更新:在没有事务隔离的情况下,两个事务都同时更新一行数据,但是第二个事务却中途失败退出, 导致对数据的两个修改都失效了. 例如: 张三的工资为5000,事务A中获取工资为5000,事务B获取工资

数据库 不可重复读是为什么处理什么样的应用场景?

问题描述 数据库 不可重复读是为什么处理什么样的应用场景? 数据库 不可重复读是为什么处理什么样的应用场景? 在实际项目中会导致什么样的问题出现? 解决方案 在一个事务内,多次读同一个数据.在这个事务还没有结束时,另一个事务也访问该同一数据.那么,在第一个事务的两次读数据之间.由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复. 解决方案二: 第一个事务进行读取数据未结束,同时第二个事务对数据进行修改

浅析SQL Server在可序列化隔离级别下,防止幻读的范围锁的锁定问题

原文:浅析SQL Server在可序列化隔离级别下,防止幻读的范围锁的锁定问题   本文出处:http://www.cnblogs.com/wy123/p/7501261.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错误进行修正或补充,无他)     数据库在处理并发事物的过程中,在不同的隔离级别下有不同的锁表现,在非可序列化隔离级别下,存在着脏读,不可重复读,丢失更新,幻读等情况.本文不讨论脏读和不可重复读以及丢失更新的情形,仅

MySQL使用可重复读作为默认隔离级别的原因之一

一般的DBMS系统,默认都会使用读提交(Read-Comitted,RC)作为默认隔离级别,如Oracle.SQL Server等,而MySQL却使用可重复读(Read-Repeatable,RR).要知道,越高的隔离级别,能解决的数据一致性问题越多,理论上性能损耗更大,可并发性越低.隔离级别依次为 SERIALIZABLE > RR > RC > Read-Uncommited 在SQL标准中,前三种隔离级别分别解决了幻象读.不可重复读和脏读的问题.那么,为什么MySQL使用可重复读作

【MySQL】可重复读模式下 unique key失效案例

一 [背景]    今天上午文能提笔安天下,武能上马定乾坤的登博给团队出了一道题目,谁先复现问题,奖励星巴克一杯.激起了一群忙碌的屌丝DBA的极大热情.问题是这样滴,如下图登博提示了几个细节:   1. code上的uk并未失效.   2. rr隔离级别.   3. 有并发线程的操作.二 [原理分析]1 事务隔离级别的基础知识:  未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据.  提交读(Read Committed):只能读取到已经提交