不可重复读和幻影读的区别是什么?

我读过维基百科上的隔离(数据库系统)文章,但我有一些怀疑。在下面的例子中,将会发生什么:不可重复读取和幻影读取?

# # # #事务

SELECT ID, USERNAME, accountno, amount FROM USERS WHERE ID=1

# # # #输出:

1----MIKE------29019892---------5000

# # # #事务B

UPDATE USERS SET amount=amount+5000 where ID=1 AND accountno=29019892;
COMMIT;

# # # #事务

SELECT ID, USERNAME, accountno, amount FROM USERS WHERE ID=1

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


当前回答

公认的答案主要表明,两者之间所谓的区别实际上根本不重要。

如果“一行被检索了两次,并且行中的值在读取之间不同”,那么它们不是同一行(在正确的RDB说法中不是同一个元组),那么根据定义也确实是“第二个查询返回的行集合与第一个查询不同”的情况。

至于“应该使用哪个隔离级别”这个问题,您的数据对某人、某个地方越重要,Serializable就越有可能是惟一合理的选择。

其他回答

我喜欢用一种简单的方式来思考:

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

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

幻影读取与此类似,但是从另一个事务中读取已提交的insert和/或delete。自开始事务以来,有新行或已消失的行。

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

我认为非重复读取和幻影读取之间有一些区别。

不可重复是指有两个事务A和B,如果B能注意到A的修改,那么可能会发生脏读,所以我们让B在A提交后注意到A的修改。

有一个新的问题:我们让B在A提交后注意到A的修改,这意味着A修改了B所持有的行的值,有时B会再次读取该行,因此B将获得与我们第一次获得的不同的新值,我们称之为不可重复的,为了解决这个问题,我们让B在B启动时记住一些东西(因为我还不知道会记住什么)。

让我们考虑一下新的解决方案,我们可以注意到也有新的问题,因为我们让B记住了一些东西,所以不管A发生了什么,B都不会受到影响,但是如果B想要向表中插入一些数据,B检查表,确保没有记录,但是这个数据已经被A插入,所以可能会发生一些错误。我们称之为幻读。

不可重复读异常如下图所示:

Alice和Bob启动两个数据库事务。 Bob读取的post记录和标题列值是Transactions。 Alice将给定post记录的标题修改为ACID的值。 Alice提交她的数据库事务。 如果Bob重读post记录,他将观察到该表行的不同版本。

Phantom Read异常有以下三种情况:

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

因此,虽然非可重复读取适用于单行,但Phantom Read是关于满足给定查询过滤条件的记录范围。

不可重复读取和幻影读取都是由于一个事务T1看到了在T1完成之前提交的另一个事务T2的更改。不同之处在于,对于同一个逻辑行,不可重复读操作返回不同的值。(例如,如果主键是employee_id,那么在两个结果中,某个员工的工资可能不同。)幻影读取返回两组不同的行,但是对于出现在这两组中的每一行,列值都是相同的。

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

在允许幻影读取的系统中,如果事务B插入ID = 1的新行,事务a将在执行第二次查询时看到新行;即幻影读是不可重复读的一种特殊情况。