脏读-不可重复读和幻读

"不可重复读 ":
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。
那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数
据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次
读取文档时,文档已更改。原始读取不可重复。
"幻读":
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,
这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插
入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象
发生了幻觉一样
"脏读":
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,
另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个
事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

解决办法:
处理以上隔离级别的问题,采用如下方是:
1 脏读:修改时加排他锁,直到事务提交后才释放,读取时加共享锁,
读取完释放事务1读取数据时加上共享锁后(这样在事务1读取数据的过程中,其他事务就不会修改该数据),
不允许任何事物操作该数据,只能读取,之后1如果有更新操作,那么会转换为排他锁,其他事务更无权参与进来读写,
这样就防止了脏读问题。但是当事务1读取数据过程中,有可能其他事务也读取了该数据,读取完毕后共享锁释放,
此时事务1修改数据,修改完毕提交事务,其他事务再次读取数据时候发现数据不一致,就会出现不可重复读问题,
所以这样不能够避免不可重复读问题。
2 不可重复读:读取数据时加共享锁,写数据时加排他锁,都是事务提交才释放锁。读取时候不允许其他事物修改该数据,
不管数据在事务过程中读取多少次,数据都是一致的,避免了不可重复读问题
3 幻读问题:采用的是范围锁RangeS RangeS_S模式,锁定检索范围为只读,这样就避免了幻影读问题,
在这里有个描述范围锁的文章

元数据:MetaData
元数据就是描述存储用户数据的容器的数据结构。
ResultSet rs=ps.executeQuery(sql);
ResultSetMetaData m=rs.getMetaData();

getColumnCount(),获得实际列数
getColumnName(int colnum),获得指定列的列名
getColumnType(int colnum),获得指定列的数据类型
getColumnTypeName(int colnum),获得指定列的数据类型名

DatabaseMetaData
getURL(),获得连接数据库的URL
getDatabaseProductName() 获得数据库产品的名称
getDriverVersion() 获得JDBC驱动程序的String形式的版本号
getTables()获得数据库中该用户的所有表
getUserName() 获得数据库用户名。


1. 脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

2. 不可重复读 :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。

3. 幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。

补充 : 基于元数据的 Spring 声明性事务 :

Isolation 属性一共支持五种事务设置,具体介绍如下:

l          DEFAULT 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 .

l          READ_UNCOMMITTED 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 )

l          READ_COMMITTED  会出现不可重复读、幻读问题(锁定正在读取的行)

l          REPEATABLE_READ 会出幻读(锁定所读取的所有行)

l          SERIALIZABLE 保证所有的情况不会发生(锁表)

不可重复读的重点是修改 :
同样的条件 ,   你读取过的数据 ,   再次读取出来发现值不一样了
幻读的重点在于新增或者删除
同样的条件 ,   第 1 次和第 2 次读出来的记录数不一样

 

数据库带来的并发问题包括:  

   1.丢失或覆盖更新。(幻像读)

   2.未确认的相关性(脏读)。

   3.不一致的分析(非重复读)。

详细描述如下:

一.丢失更新
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。   

e.g.事务A和事务B同时修改某行的值,

 1.事务A将数值改为1并提交

       2.事务B将数值改为2并提交。

这时数据的值为2,事务A所做的更新将会丢失。

 

解决办法:对行加锁,只允许并发一个更新事务。

二.未确认的相关性(脏读)
  当第二个事务选择其它事务正在更新的行时,会发生未确认的相关性问题。第二个事务正在读取的数据还没有确认并且可能由更新此行的事务所更改。

      e.g.

        1.Mary的原工资为1000, 财务人员将Mary的工资改为了8000(但未提交事务)         2.Mary读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!

     3.而财务发现操作有误,回滚了事务,Mary的工资又变为了1000

      像这样,Mary记取的工资数8000是一个脏数据。

 

 解决办法:如果在第一个事务提交前,任何其他事务不可读取其修改过的值,则可  以避免该问题。


三.不一致的分析(非重复读)
  当第二个事务多次访问同一行而且每次读取不同的数据时,会发生不一致的分析问题。不一致的分析与未确认的相关性类似,因为其它事务也是正在更改第二个事务正在读取的数据。然而,在不一致的分析中,第二个事务读取的数据是由已进行了更改的事务提交的。而且,不一致的分析涉及多次(两次或更多)读取同一行,而且每次信息都由其它事务更改;因而该行被非重复读取。

      在一个事务中前后两次读取的结果并不致,导致了不可重复读。


e.g.

1.在事务1中,Mary 读取了自己的工资为1000,操作并没有完成

2.在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.

3.在事务1中,Mary 再次读取自己的工资时,工资变为了2000


解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。


 四.幻像读     

  当对某行执行插入或删除操作,而该行属于某个事务正在读取的行的范围时,会发生幻像读问题。事务第一次读的行范围显示出其中一行已不复存在于第二次读或后续读中,因为该行已被其它事务删除。同样,由于其它事务的插入操作,事务的第二次或后续读显示有一行已不存在于原始读中。

   e.g. 目前工资为1000的员工有10人。

1.事务1,读取所有工资为1000的员工。

2.这时事务2向employee表插入了一条员工记录,工资也为1000

3.事务1再次读取所有工资为1000的员工 共读取到了11条记录,

 

解决办法:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题


转载自: http://hi.baidu.com/310934797/blog/item/63eb51003bfff29be950cd98.html


<think>嗯,用户这次的问题是关于数据库事务中不可重复的区别。之前我已经解释过事务传播行为隔离级别的区别,现在用户想深入理解这些并发问题。用户引用了多篇关于数据库事务的文章,内容很专业。从引用[1][3][4][5]来看,用户主要关注三种现象的具体表现差异。特别是引用[5]强调与数据行数变化的关系,这点需要重点说明。用户可能正在学习数据库事务,或者遇到了实际的并发问题。ta特意要求区分这三个概念,说明之前可能混淆过它们。需要特别注意用生活化的类比帮助理解。最危险,就像偷看别人未提交的草稿。引用[1][3]都强调这是取未提交数据。不可重复更像是同一事务内数据"变脸",引用[3]用"两次查询不一致"描述得很准确。最特别,引用[5]出关键在于"行数变化",就像魔术师凭空变出鸽子。 表格对比会很有用,但要注意引用[4]的隔离级别对照表不能直接复制,需要转化。隔离级别部分要简明,引用[2]提到"SERIALIZABLE_READ能解决所有问题"这点值得强调。最后用"文件草稿/合同金额/员工名单"的类比收尾,既呼应引用内容,又能让用户记住本质区别。相关问题的设计要延续事务主题,特别是隔离级别选择这种实用问题。</think>以下是数据库事务中不可重复的详细区别及对比: --- ### 一、核心概念对比 | **现象** | **本质问题** | **触发场景** | **示例** | |----------------|----------------------------|------------------------------------------|------------------------------------------| | **** | 取**未提交**的数据 | 事务A取事务B**修改中但未提交**的数据 | 事务B修改金额未提交,事务A到错误金额[^1][^3] | | **不可重复** | 同数据**多次取结果不同** | 事务A两次取之间,事务B**修改并提交**了数据 | 事务A首次金额100,事务B改为200提交后,事务A第二次金额变为200[^1][^3] | | **** | 同查询**返回行数不同** | 事务A两次查询之间,事务B**新增/删除**了数据 | 事务A首次查10条记录,事务B新增1条提交后,事务A第二次查得11条记录[^3][^5] | > **关键区别**: > 关注**数据是否提交**,不可重复关注**数据值是否变化**,关注**数据行数是否变化**[^2][^5]。 --- ### 二、隔离级别的解决能力 | **隔离级别** | | 不可重复 | | |----------------------|------|------------|------| | **READ UNCOMMITTED** | ❌ | ❌ | ❌ | | **READ COMMITTED** | ✅ | ❌ | ❌ | | **REPEATABLE READ** | ✅ | ✅ | ❌* | | **SERIALIZABLE** | ✅ | ✅ | ✅ | > *注:MySQL的`REPEATABLE READ`通过间隙锁可避免,但标准SQL中仍可能存在[^3][^4]。 --- ### 三、技术原理与示例 #### 1. **** ```sql -- 事务A BEGIN; UPDATE accounts SET balance = 200 WHERE id = 1; -- 未提交 -- 事务B(READ UNCOMMITTED) BEGIN; SELECT balance FROM accounts WHERE id = 1; -- 到200(数据) ``` > 事务B取到事务A的**中间状态**,若事务A回滚,数据将不一致[^1][^3]。 #### 2. **不可重复** ```sql -- 事务A BEGIN; SELECT balance FROM accounts WHERE id = 1; -- 返回100 -- 事务B提交更新 UPDATE accounts SET balance = 200 WHERE id = 1; COMMIT; -- 事务A再次查询 SELECT balance FROM accounts WHERE id = 1; -- 返回200(值变化) ``` > 同一事务内**相同查询**结果不同,因其他事务修改了数据[^3][^5]。 #### 3. **** ```sql -- 事务A(查询年龄<30的用户) BEGIN; SELECT * FROM users WHERE age < 30; -- 返回10条 -- 事务B新增用户并提交 INSERT INTO users (name, age) VALUES ('Bob', 25); COMMIT; -- 事务A再次查询 SELECT * FROM users WHERE age < 30; -- 返回11条(行数变化) ``` > 重点在于**数据行数变化**(新增/删除),而非单行数据修改[^5]。 --- ### 四、解决方式 1. **提升隔离级别** - `READ COMMITTED` 解决 - `REPEATABLE READ` 解决不可重复 - `SERIALIZABLE` 彻底解决所有问题(但性能最低)[^2][^4] 2. **MySQL特殊优化** 通过**间隙锁**(Gap Lock)在`REPEATABLE READ`级别阻止,例如锁定`age < 30`的范围阻止新增[^4]。 --- ### 五、生活化类比 - **** 👉 偷看别人未保存的文档草稿 - **不可重复** 👉 两次查看合同时发现金额被修改 - **** 👉 两次统计员工名单时发现人数变化 > 三者本质都是**事务间可见性控制不足**导致的数据一致性问题[^1][^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值