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

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

# # # #事务

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

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


当前回答

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

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

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

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

其他回答

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

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

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

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

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

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

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

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

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

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

摘自维基百科(其中有非常详细的例子):

不可重复读取发生在这样的情况下:在事务过程中,一行被检索了两次,并且两次读取之间行中的值不同。

and

在事务过程中,当执行两个相同的查询,而第二个查询返回的行集合与第一个查询不同时,就会发生幻影读。

简单的例子:

User A runs the same query twice. In between, User B runs a transaction and commits. Non-repeatable read: The A row that user A has queried has a different value the second time. Phantom read: All the rows in the query have the same value before and after, but different rows are being selected (because B has deleted or inserted some). Example: select sum(x) from table; will return a different result even if none of the affected rows themselves have been updated, if rows have been added or deleted.

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

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

在您的示例中,您不会进行幻影读取,因为您只从单行(由主键标识)进行选择。您可以使用不可重复的读取,因此如果这是一个问题,您可能希望有一个隔离级别来防止这种情况。在Oracle中,事务A也可以发出SELECT FOR UPDATE,那么事务B在A完成之前不能更改行。