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

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

# # # #事务

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

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


当前回答

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

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

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

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

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

其他回答

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

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

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

读现象

脏读:从另一个事务读取未提交的数据 不可重复读取:从另一个事务的UPDATE查询中读取COMMITTED数据 幻影读取:从另一个事务的INSERT或DELETE查询中读取COMMITTED数据

注意:来自另一个事务的DELETE语句,在某些情况下也有非常低的概率导致不可重复读取。不幸的是,当DELETE语句删除当前事务正在查询的同一行时,就会发生这种情况。但这种情况很少见,在每个表中都有数百万行的数据库中更不可能出现这种情况。在任何生产环境中,包含事务数据的表通常都有很大的数据量。

此外,我们还可以观察到,在大多数用例中,更新作业可能比实际的INSERT或delete作业更频繁(在这种情况下,仅存在不可重复读取的危险-在这些情况下不可能出现幻影读取)。这就是为什么update与INSERT-DELETE处理方式不同,导致的异常命名也不同。

处理insert - delete(而不仅仅是处理update)还需要额外的处理成本。


不同隔离级别的好处

READ_UNCOMMITTED什么也阻止不了。这是零 隔离级别 READ_COMMITTED只阻止一种,即脏读 REPEATABLE_READ防止两种异常:脏读和 不可重复读 SERIALIZABLE可以防止所有三种异常:脏读, 不可重复读取和幻影读取

那么为什么不始终设置事务SERIALIZABLE呢?好吧,上述问题的答案是:SERIALIZABLE设置使事务非常慢,这也是我们不希望看到的。

实际上,事务时间消耗的速率如下:

Serializable > repeatable_read > read_committed > read_uncommitted

所以READ_UNCOMMITTED设置是最快的。


总结

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

注意,数据库在默认情况下可能有REPEATABLE_READ设置。管理员和架构师可能倾向于将此设置作为默认设置,以展示更好的平台性能。

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

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

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

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

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

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

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

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