不可重复读和幻像读有什么区别?

use*_*722 137 database oracle transactions transaction-isolation isolation-level

不可重复读和幻读之间有什么区别?

我已经阅读了维基百科隔离(数据库系统)文章,但我有一些疑问.在下面的例子中,会发生什么:不可重复的读取幻像读取

交易A.
SELECT ID, USERNAME, accountno, amount FROM USERS WHERE ID=1
Run Code Online (Sandbox Code Playgroud) OUTPUT:
1----MIKE------29019892---------5000
Run Code Online (Sandbox Code Playgroud) 交易B.
UPDATE USERS SET amount=amount+5000 where ID=1 AND accountno=29019892;
COMMIT;
Run Code Online (Sandbox Code Playgroud) 交易A.
SELECT ID, USERNAME, accountno, amount FROM USERS WHERE ID=1
Run Code Online (Sandbox Code Playgroud)

另一个疑问是,在上面的例子中,应该使用哪个隔离级别?为什么?

Thi*_*ilo 147

来自维基百科(其中有很多详细的例子):

发生不可重复的读取,当在事务过程中,行被检索两次并且行内的值在读取之间不同.

当在事务过程中执行两个相同的查询,并且第二个查询返回的行集合与第一个查询不同时,会发生幻像读取.

简单的例子:

  • 用户A运行相同的查询两次.
  • 在这两者之间,用户B运行一个事务并提交.
  • 不可重复读取:用户A查询的A行第二次具有不同的值.
  • 幻像读取:查询中的所有行在前后都具有相同的值,但是正在选择不同的行(因为B已删除或插入了一些).示例:select sum(x) from table;如果已添加或删除行,即使没有更新受影响的行本身也将返回不同的结果.

在上面的例子中,要使用哪个隔离级别?

您需要什么隔离级别取决于您的应用程序."更好"的隔离级别(例如降低的并发性)成本很高.

在您的示例中,您将不会进行幻像读取,因为您只选择单行(由主键标识).您可以进行不可重复的读取,因此如果这是一个问题,您可能希望具有阻止它的隔离级别.在Oracle中,事务A也可以发出SELECT FOR UPDATE,然后事务B在A完成之前不能更改行.

  • @serhio"non-repeatable"指的是你可以读取一次值并得到x作为结果,然后再次读取并得到y作为结果,所以你不能重复(不可重复)两个相同的结果对同一行的单独查询,因为该行值在读取之间更新. (10认同)
  • 我真的不明白这种语法的逻辑......当**重复**(并获得不同的值)时,会发生**不可重复的**读取!... (6认同)
  • 不同之处在于,不可重复读取为“同一逻辑行”返回不同的值。(例如,如果主键是employee_id,则某个员工在两个结果中可能有不同的薪水。)幻读返回两个不同的行集,但对于出现在这两个集中的每一行,列值是相同的。 (4认同)
  • 两者对我来说听起来都一样 (3认同)
  • 不同之处在于,当您执行 `count(*) from table` 并首先返回 `42` 然后返回 `43` 时,这不是不可重复读取,因为对于您第一次选择的 42 行,您返回第二次相同的数据。所以没有用不同的值检索两次的行。但这仍然是幻读,因为您又返回了一行。因此,所有行值都分别相同,但您现在正在选择不同的行。@sn.anurag (3认同)

Bat*_*ech 114

我想要考虑的一个简单方法是:

非可重复和幻像读取都与来自不同事务的数据修改操作有关,这些操作在事务开始后提交,然后由您的事务读取.

不可重复的读取是指您的事务从另一个事务读取提交的UPDATES.现在,同一行的值与您的事务开始时的值不同.

幻像读取类似,但是从提交的INSERTS读取和/或从另一个事务中读取DELETES时.自开始事务以来,有新的行或行已消失.

脏读类似于不可重复和幻像读取,但与读取UNCOMMITTED数据有关,并且在读取来自另一个事务的UPDATE,INSERT或DELETE,而另一个事务尚未提交数据时发生.它正在读取"进行中"数据,这些数据可能不完整,可能永远不会实际提交.

  • 它与事务隔离级别和并发性有关.使用默认隔离级别,您将不会获得脏读,并且在大多数情况下,您希望避免脏读.存在允许脏读的隔离级别或查询提示,在*某些情况下,为了实现更高的并发性,或者由于边缘情况(例如从另一个连接对进行中的事务进行故障排除)而需要进行折衷.肮脏阅读的想法不会为你通过"气味测试",这是一件好事,作为一般规则,它们应该被避免,但确实有目的. (4认同)
  • @DiponRoy很好的问题。如果使用可重复读(RR)隔离,则实现的锁定应防止在选定的行上发生删除。多年来,我对2个iso级别的定义有所不同,主要是幻像是在collection /#返回的行中进行了更改,而RR是在更改的同一行。我刚刚检查了更新的MS SQL文档,说删除会导致非RR(https://docs.microsoft.com/zh-cn/sql/odbc/reference/develop-app/transaction-isolation-levels?view=sql -server-2017),所以我认为也可以将RR类别中的删除分组 (2认同)
  • @anir yes插入和删除包含在脏读取中。示例:开始交易,在连接a上插入100条发票行中的2条,现在连接b在提交trx之前和添加其他98条行之前读取这两行,因此不包括发票的所有信息。这将是涉及插入的脏读。 (2认同)

Vla*_*cea 25

本文所述,不可重复读取异常如下所示:

在此输入图像描述

  1. Alice和Bob启动两个数据库事务.
  2. Bob's读取帖子记录和标题栏值是交易.
  3. Alice将给定帖子记录的标题修改为ACID的值.
  4. Alice提交她的数据库事务.
  5. 如果Bob重新读取帖子记录,他将观察该表行的不同版本.

这篇关于Phantom Read的文章中,您可以看到此异常可能发生如下:

在此输入图像描述

  1. Alice和Bob启动两个数据库事务.
  2. Bob's读取与帖子行关联的所有post_comment记录,标识符值为1.
  3. Alice添加了一个新的post_comment记录,该记录与标识符值为1的post行相关联.
  4. Alice提交她的数据库事务.
  5. 如果Bob重新读取post_id列值等于1的post_comment记录,他将观察到该结果集的不同版本.

因此,虽然不可重复读取适用于单个行,但是Phantom Read是一系列满足给定查询过滤条件的记录.

  • 极好的可视化@Vlad (3认同)
  • 这些异常之间没有包含操作。前者是关于范围扫描,而后者是关于单个记录。 (3认同)

小智 16

脏读:从另一个事务中读取未提供的数据.

不可重复读取:从另一个事务中读取来自UPDATE查询的COMMITED数据.

幻像读取:从另一个事务中读取来自INSERT或DELETE查询的COMMITED数据.

请注意,UPDATES在某些用例中可能是更频繁的工作,而不是实际的INSERT或DELETES - 在这种情况下,不可重复读取的危险仍然存在 - 在这些情况下不可能进行幻读.这就是为什么UPDATES与INSERT-DELETE的区别对待,有关的异常也有不同的命名.

处理INSERT-DELETES还需要额外的处理成本,而不仅仅是处理UPDATES.

隔离级别TRANSACTION_READ_UNCOMMITTED不会阻止任何操作.它的零隔离级别.

隔离级别TRANSACTION_READ_COMMITTED只能阻止一个,即.脏读.

隔离级别TRANSACTION_REPEATABLE_READ可防止两个异常:脏读和不可重复读.

隔离级别TRANSACTION_SERIALIZABLE可防止所有三个异常:脏读,不可重复读和幻读.

那么为什么不随时设置事务SERIALIZABLE?

好吧,上面问题的答案是:SERIALIZABLE设置使交易非常慢,我们再次不想要.

事实上,交易时间消耗的速度如下:

SERIALIZABLE> REPEATABLE_READ> READ_COMMITTED> READ_UNCOMMITTED.

所以READ_UNCOMMITTED设置是最快的.

实际上,我们需要分析用例并确定隔离级别,以便我们优化事务时间并防止大多数异常.

请注意,默认情况下,数据库具有REPEATABLE_READ设置.

  • UPDATE 或 DELETE 都可以用于不可重复读取 (2认同)

小智 10

  • 不可重复读(模糊读)是指一个事务至少读取同一行两次,但第一次和第二次读取同一行的数据不同,因为其他事务同时(并发)更新同一行的数据并提交。

  • 幻读是指一个事务至少读取同一个表两次,但第一次和第二次读取同一个表的行数不同,因为其他事务同时(并发)插入或删除行并提交。

我用MySQL2个命令提示符实验了不可重复读幻读

对于不可重复读幻读的实验,我设置READ COMMITTED隔离级别以发生不可重复读幻读

SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
Run Code Online (Sandbox Code Playgroud)

并且,我使用 和 创建了表格,person如下所示。idname

person桌子:

ID 姓名
1 约翰
2 大卫

首先,对于不可重复读取,我使用MySQL 查询执行了以下步骤

流动 交易1(T1) 交易2(T2) 解释
步骤1 BEGIN; T1开始。
第2步 BEGIN; T2开始。
步骤3 SELECT * FROM person WHERE id = 2;

2 大卫
T1 读取David
步骤4 UPDATE person SET name = 'Tom' WHERE id = 2; T2 更新DavidTom.
步骤5 COMMIT; T2 提交。
步骤6 SELECT * FROM person WHERE id = 2;

2 汤姆
T1 读取Tom而不是DavidT2 提交之后。

*发生不可重复读!!

步骤7 COMMIT; T1 提交。

其次,对于幻读,我使用MySQL 查询执行了以下步骤

流动 交易1(T1) 交易2(T2) 解释
步骤1 BEGIN; T1开始。
第2步 BEGIN; T2开始。
步骤3 SELECT * FROM person;

约翰一书
大卫二书
T1 读取 2 行。
步骤4 INSERT INTO person VALUES (3, 'Tom'); 3T2 将带有和 的行插入Tomperson表中。
步骤5 COMMIT; T2 提交。
步骤6 SELECT * FROM person;

约翰一书
大卫二书
汤姆三书
T2 提交后,T1 读取 3 行而不是 2 行。

*出现幻读!!

步骤7 COMMIT; T1 提交。


egr*_*dlo 7

这两种隔离级别之间的实现存在差异.
对于"不可重复读取",需要行锁定.
对于"幻读",需要使用范围锁定,甚至是表锁定.
我们可以使用两阶段锁定协议来实现这两个级别.


Jef*_*emp 5

在具有不可重复读取的系统中,事务 A 的第二次查询的结果将反映事务 B 中的更新——它将看到新的金额。

在允许幻读的系统中,如果事务 B 要插入ID = 1 的新行,事务 A 将在执行第二个查询时看到新行;即幻读是不可重复读的特例。