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

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

# # # #事务

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插入ID = 1的新行,事务a将在执行第二次查询时看到新行;即幻影读是不可重复读的一种特殊情况。

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

不可重复是指有两个事务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完成之前不能更改行。

不可重复读是一个隔离级别,幻影读(通过其他事务读取已提交的值)是一个概念(读的类型,例如脏读或快照读)。不可重复读隔离级别允许幻影读,但不允许脏读或快照读。

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