301

我认为上述隔离级别非常相似。有人可以用一些很好的例子来描述主要区别是什么吗?

4

8 回答 8

689

已提交读是一种隔离级别,可确保读取当前已提交的任何数据。它只是限制读者看到任何中间的、未提交的、“脏”的阅读。它不承诺如果事务重新发出读取,将找到相同的数据,数据在读取后可以自由更改。

可重复读是更高的隔离级别,即除了保证读提交级别之外,还保证读到的任何数据都不能改变,如果事务再次读到相同的数据,就会发现之前读到的数据在原地,不变,并且可以阅读。

下一个隔离级别,可序列化,提供了更强大的保证:除了所有可重复读取保证之外,它还保证后续读取不会看到数据。

假设您有一个表 T,其中包含 C 列,其中有一行,假设它的值为“1”。并考虑您有一个简单的任务,如下所示:

BEGIN TRANSACTION;
SELECT * FROM T;
WAITFOR DELAY '00:01:00'
SELECT * FROM T;
COMMIT;

这是一个简单的任务,从表 T 发出两次读取,它们之间有 1 分钟的延迟。

  • 在 READ COMMITTED 下,第二个 SELECT 可能会返回任何数据。并发事务可以更新记录、删除记录、插入新记录。第二个选择将始终看到数据。
  • 在 REPEATABLE READ 下,第二个 SELECT 保证至少显示从第一个 SELECT 返回的行不变。在那一分钟内,并发事务可能会添加新行,但不能删除或更改现有行。
  • 在 SERIALIZABLE 读取下,第二个选择保证看到与第一个完全相同的行。并发事务不能更改、删除或插入新行。

如果你遵循上面的逻辑,你很快就会意识到 SERIALIZABLE 事务,虽然它们可能会让你的生活变得轻松,但总是完全阻塞所有可能的并发操作,因为它们要求没有人可以修改、删除或插入任何行。.Net 范围的默认事务隔离级别System.Transactions是可序列化的,这通常可以解释导致的糟糕性能。

最后,还有 SNAPSHOT 隔离级别。SNAPSHOT 隔离级别提供与可序列化相同的保证,但不要求没有并发事务可以修改数据。相反,它迫使每个读者看到自己的世界版本(它是自己的“快照”)。这使得它非常容易编程并且非常可扩展,因为它不会阻止并发更新。然而,这种好处是有代价的:额外的服务器资源消耗。

补充内容如下:

于 2010-10-27T17:44:53.313 回答
81

可重复读取

从事务开始就维护数据库的状态。如果您在 session1 中检索一个值,然后在 session2 中更新该值,在 session1 中再次检索它将返回相同的结果。读取是可重复的。

session1> BEGIN;
session1> SELECT firstname FROM names WHERE id = 7;
Aaron

session2> BEGIN;
session2> SELECT firstname FROM names WHERE id = 7;
Aaron
session2> UPDATE names SET firstname = 'Bob' WHERE id = 7;
session2> SELECT firstname FROM names WHERE id = 7;
Bob
session2> COMMIT;

session1> SELECT firstname FROM names WHERE id = 7;
Aaron

读已提交

在事务的上下文中,您将始终检索最近提交的值。如果您在 session1 中检索一个值,在 session2 中更新它,然后在 session1 中再次检索它,您将获得在 session2 中修改的值。它读取最后提交的行。

session1> BEGIN;
session1> SELECT firstname FROM names WHERE id = 7;
Aaron

session2> BEGIN;
session2> SELECT firstname FROM names WHERE id = 7;
Aaron
session2> UPDATE names SET firstname = 'Bob' WHERE id = 7;
session2> SELECT firstname FROM names WHERE id = 7;
Bob
session2> COMMIT;

session1> SELECT firstname FROM names WHERE id = 7;
Bob

说得通?

于 2013-01-18T07:23:13.170 回答
49

根据我对这个线程的阅读和理解,答案很简单,@remus-rusanu 的答案是基于这个简单的场景:

有两个事务 A 和 B。事务 B 正在读取表 X 事务 A 正在写入表 X 事务 B 正在再次读取表 X。

  • ReadUncommitted:事务 B 可以从事务 A 中读取未提交的数据,并且它可以根据 B 写入看到不同的行。完全没有锁
  • ReadCommitted:事务 B 只能从事务 A 中读取已提交的数据,并且它可以看到基于仅提交 B 写入的不同行。我们可以称之为简单锁吗?
  • RepeatableRead:无论事务 A 正在做什么,事务 B 都将读取相同的数据(行)。但是事务 A 可以更改其他行。行级块
  • 可序列化:事务 B 将读取与之前相同的行,而事务 A 无法在表中读取或写入。表级块
  • 快照:每个事务都有自己的副本,他们正在处理它。每个人都有自己的看法
于 2016-07-29T03:53:15.593 回答
19

已经有一个公认答案的老问题,但我喜欢考虑这两个隔离级别如何改变 SQL Server 中的锁定行为。这可能对那些像我一样调试死锁的人有所帮助。

读已提交(默认)

在 SELECT 中获取共享锁,然后在 SELECT 语句完成时释放。这就是系统如何保证没有未提交数据的脏读。在您的 SELECT 完成之后和您的事务完成之前,其他事务仍然可以更改基础行。

可重复阅读

在 SELECT 中获取共享锁,然后仅在事务完成后释放。这就是系统如何保证您读取的值在事务期间不会更改(因为它们保持锁定直到事务完成)。

于 2014-11-05T20:27:19.647 回答
18

试图用简单的图表来解释这个疑问。

已提交读取:在此隔离级别中,事务 T1 将读取事务 T2 提交的 X 的更新值。

读已提交

可重复读取:在此隔离级别下,Transaction T1 不会考虑 Transaction T2 提交的更改。

在此处输入图像描述

于 2018-02-15T11:35:32.540 回答
2

我认为这张图片也很有用,当我想快速记住隔离级别之间的差异时,它可以帮助我作为参考(感谢 youtube 上的kudvenkat

在此处输入图像描述

于 2018-11-16T10:20:39.617 回答
0

请注意,repeatable in repeatable read 是针对一个元组,而不是整个表。在 ANSC 隔离级别中,可能会发生幻读异常,这意味着使用相同的 where 子句读取表两次可能会返回不同的返回不同的结果集。从字面上看,它是不可重复的。

于 2019-04-14T12:37:56.830 回答
-1

我对最初接受的解决方案的观察。

在 RR(默认 mysql)下 - 如果一个 tx 已打开并且 SELECT 已被触发,则另一个 tx 不能删除属于先前 READ 结果集的任何行,直到前一个 tx 被提交(实际上新 tx 中的删除语句只会挂起) ,但是下一个 tx 可以毫无问题地从表中删除所有行。顺便说一句,在前一个 tx 中的下一个 READ 仍然会看到旧数据,直到它被提交。

于 2016-03-02T21:04:28.893 回答