非重复读取和幻像读取有什么区别?

非重复读取和幻像读取之间有什么区别?

我已阅读维基百科的隔离(数据库系统)文章 ,但我有一些疑问。 在下面的例子中,会发生什么: 不可重复读取幻像读取

交易A

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

OUTPUT:

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

交易B

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

交易A

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

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

从维基百科 (这有很好的和详细的例子):

发生不可重复的读取时,在事务过程中,行被检索两次,行内的值在读取之间不同。

当在事务过程中执行两个完全相同的查询,并且由第二个查询返回的行的集合与第一个查询所返回的集合不同时,会发生幻像读取。

简单的例子:

  • 用户A运行相同的查询两次。
  • 在这之间,用户B运行一个事务并提交。
  • 不可重复读取:用户A查询的行第二次有不同的值。
  • 幻像读取:查询中的所有行在前后具有相同的值, 但不同的行被选中 (因为B已经删除或插入了一些)。 例如: select sum(x) from table; 将返回一个不同的结果,即使没有任何受影响的行本身已被更新,如果行已被添加或删除。

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

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

在你的例子中,你不会有一个幻读,因为你只从一个单一的行(由主键标识)select。 你可以有不可重复的读取,所以如果这是一个问题,你可能想要一个隔离级别,防止这种情况。 在Oracle中,事务A也可以发出SELECT FOR UPDATE,然后事务B不能改变行,直到A完成。

我喜欢想一个简单的方法是:

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

当您的事务从另一个事务读取已提交的UPDATES时,不可重复的读取。 现在,同一行的值与交易开始时的值不同。

幻影读取是相似的,但是当从提交的INSERTS读取和/或从另一个事务删除 。 自开始交易以来,有新的行或行已经消失。

脏读类似于不可重复和幻读,但涉及读UNCOMMITTED数据,并且在读取另一个事务的UPDATE,INSERT或DELETE时发生,另一个事务尚未提交数据。 它正在阅读“正在进行”的数据,这些数据可能不完整,可能永远不会被提交。

在具有不可重复读取的系统中,事务A的第二个查询的结果将反映事务B中的更新 – 它将看到新的数量。

在允许幻像读取的系统中,如果事务B要插入 ID = 1的新行,则在执行第二个查询时,事务A将看到新行; 即幻影读取是不可重复读取的特例。

这两种隔离级别的实现是有区别的。
对于“不可重复读取”,需要行locking。
对于“幻像读取”,需要作用域locking,即使是表locking。
我们可以通过使用两阶段locking协议来实现这两个级别。

被接受的答案表明最重要的是所谓的两者之间的区别实际上并不重要。

如果“一行被检索两次,并且行之间的值在不同的读取之间”不同,那么它们不是同一行(在正确的RDB中不是相同的元组说话),然后确实也是这样的情况:“第二个查询返回的行不同于第一个“。

至于“应该使用哪个隔离级别”的问题,你的数据对于某个人来说是至关重要的,在某个地方,Serializable是你唯一合理的select。

脏读:从其他事务中读取未被发现的数据。

不可重复读取:从其他事务的UPDATE查询中读取COMMITED数据。

幻像读取:从其他事务的INSERT或DELETE查询中读取COMMITED数据。

请注意,在某些使用情况下,UPDATES可能是一个比较常见的工作,而不是实际的INSERT或DELETES。在这种情况下,不可重复读取的危险仍然存在 – 在这些情况下,幻像读取是不可能的。 这就是为什么UPDATE与INSERT-DELETE的处理方式不同,有关的exception也有不同的命名。

处理INSERT-DELETES还有一个额外的处理成本,而不仅仅是处理UPDATES。

隔离级别TRANSACTION_READ_UNCOMMITTED防止什么。 它的零隔离级别。

隔离级别TRANSACTION_READ_COMMITTED只防止一个,即。 脏读。

隔离级别TRANSACTION_REPEATABLE_READ防止两个exception:脏读和不可重复的读取。

隔离级别TRANSACTION_SERIALIZABLE可防止所有三种exception:脏读,不可重复读取和幻像读取。

那为什么不直接设置事务SERIALIZABLE?

那么,对上述问题的答案是:SERIALIZABLE设置使交易非常缓慢,这是我们不想要的。

事实上交易时间消耗是以下的比率:

SERIALIZABLE> REPEATABLE_READ> READ_COMMITTED> READ_UNCOMMITTED。

所以READ_UNCOMMITTED设置是最快的。

实际上,我们需要分析用例并确定一个隔离级别,以便优化交易时间并防止大部分exception。

请注意,数据库默认情况下具有REPEATABLE_READ设置。