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

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

# # # #事务

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时,而另一个事务尚未提交数据。它正在读取“正在进行”的数据,这些数据可能不完整,并且可能永远不会实际提交。

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

读现象

脏读:从另一个事务读取未提交的数据 不可重复读取:从另一个事务的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设置。管理员和架构师可能倾向于将此设置作为默认设置,以展示更好的平台性能。

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