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

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

# # # #事务

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就越有可能是惟一合理的选择。

其他回答

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

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

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

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

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

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是关于满足给定查询过滤条件的记录范围。

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

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

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

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

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