脏读、不可重复读、幻读

简介:

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

 

事务并发产生的问题:
        脏读:一个事务读取到了另外一个事务没有提交的数据
            事务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>



目录
相关文章
|
1月前
|
消息中间件 运维 监控
海尔智家 x 阿里云 Kafka 实践:轻松支撑百亿级消息,稳定性与效率双提升
海尔智家通过与阿里云深度共创,采用定制化迁移与调优方案,平滑升级至Kafka Serverless,不仅保障了极致稳定性,更实现运维自动化,大幅释放研发人力。
160 22
|
1月前
|
缓存 安全 算法
深入拆解 Java CAS:从底层原理到 ABA 问题实战
本文深入解析Java中CAS(比较并交换)的底层原理:从CPU指令级支持(cmpxchg、缓存锁/总线锁)、Unsafe类的核心作用,到ABA问题的成因及AtomicStampedReference等解决方案,全面剖析无锁并发机制的优劣与实践要点。
155 10
|
Java API Spring
SpringBoot项目调用HTTP接口5种方式你了解多少?
SpringBoot项目调用HTTP接口5种方式你了解多少?
3206 2
|
2月前
|
人工智能 安全 API
深入理解OpenClaw技术架构与实现原理(上)
本文深度剖析OpenClaw——当前最热门的个人AI助手系统,涵盖其本地优先、多端联动的总体架构,以及Gateway网关、Agentic Loop、定时任务、工具系统、Channels连接生态、上下文管理、SubAgent子智能体等16大核心模块。全文以AI-Coding实现为特色,强调安全沙箱、协议化设计与自进化能力,展现新一代软件构建范式的开山之作。
深入理解OpenClaw技术架构与实现原理(上)
|
人工智能 Java 物联网
没有好的学历,Java开发未来的路应该怎么走?
在数字化时代,Java开发者即使没有高学历,也能通过拥抱新兴技术(如大模型应用与鸿蒙系统开发)、积累实战经验、持续学习新技能等途径实现职业突破。从参与开源项目到关注行业动态,再到规划技术专家或管理路线,建立人脉网络并利用教育平台提升能力,开发者可拓宽技术边界,适应日新月异的技术需求,在未来发展中占据一席之地。
|
Java Spring
【Spring配置】创建yml文件和properties或yml文件没有绿叶
本文主要针对,一个项目中怎么创建yml和properties两种不同文件,进行配置,和启动类没有绿叶标识进行解决。
IDEA 自定义注解(类注释、方法注释)
IDEA 自定义注解(类注释、方法注释)
7157 1
IDEA 自定义注解(类注释、方法注释)
|
存储 Oracle 关系型数据库
达梦数据库入门语法:从基础到进阶的指南
达梦数据库入门语法:从基础到进阶的指南
3746 2
|
SQL Java 数据库
Activity7整合springboot:流程部署,定义,实例(一)
Activity7整合springboot:流程部署,定义,实例(一)
|
数据采集 领域建模 数据库
如何画领域模型图(数据架构/ER图)
如何画领域模型图(数据架构/ER图)
6503 1
如何画领域模型图(数据架构/ER图)