ChatGPT解决这个技术问题 Extra ChatGPT

“已提交读”和“可重复读”之间的区别

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

您应该扩展问题并为您所指的“隔离级别”添加标签(Java 等)。 “隔离级别”是一个有点模棱两可的术语,您显然是在寻求特定环境的答案。

r
raven

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

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

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

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

补充内容如下:

数据库引擎中的隔离级别

并发效应

选择基于行版本控制的隔离级别


我认为 REPEATABLE READ 上面有一个错误:您说现有行不能被删除或更改,但我认为它们可以被删除或更改,因为可重复读取只是读取“快照”而不是实际数据。来自文档 dev.mysql.com/doc/refman/5.0/en/…:“同一事务中的所有一致读取都读取第一次读取建立的快照。”
@Derek Litz 我说得对吗:当交易正在进行时,数据可以/可以从第三方更改,但读取仍会看到“旧”原始数据,就好像没有进行更改一样地点(快照)。
@玉米穗。是的,幻读可以从删除(或插入)中发生。是的,幻读可以在可重复读取隔离中发生(仅来自插入)。不,在可重复读取隔离中不能发生从删除中进行的幻读。测试一下。我所说的与您引用的文档并不矛盾。
@Cornstalks NP。我只提到它是因为我自己不是 100% 确定自己,必须深入研究以确定谁是对的!而且我不希望未来的读者被误导。重新保留评论,可能最好按照建议保留。我敢肯定,任何对这种精细细节感兴趣的人都会特别喜欢阅读所有评论!
感谢您不删除您的评论。讨论有助于连接更多的点。
B
BenMorel

可重复读取

从事务开始就维护数据库的状态。如果您在 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

说得通?


我在 SQL Server 2008 中使用“设置隔离级别可重复读取”尝试了可重复读取。创建了两个 sql 查询窗口。但没有奏效。为什么?
为什么第二个 session1 仍然会读出 Aaron? session2的事务不是完成并提交了吗?我知道这个老,但也许有人可以阐明一些。
我认为可重复读取将阻止第二个会话,直到第一个会话提交。所以这个例子是错误的。
在可重复读取的情况下,当会话 1 读取该行时,它会放置一个共享锁,这将不允许任何独占锁(会话 2)进行更新,因此无法更新数据。
我认为 SQL Server 和 MySQL 在更新两个事务之间的共享行时表现不同
M
Mo Zaatar

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

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

ReadUncommitted:事务 B 可以从事务 A 中读取未提交的数据,并且可以根据 B 写入看到不同的行。完全没有锁

ReadCommitted:事务 B 只能从事务 A 中读取已提交的数据,并且它可以看到基于仅提交 B 写入的不同行。我们可以称之为简单锁吗?

可重复读取:无论事务 A 正在做什么,事务 B 都将读取相同的数据(行)。但是事务 A 可以更改其他行。行级块

可序列化:事务 B 将读取与之前相同的行,而事务 A 无法在表中读取或写入。表级块

快照:每个事务都有自己的副本,他们正在处理它。每个人都有自己的看法


这是最好的
非常简明扼要。我会用“交易”代替“流程”这个词
C
Chris Gillum

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

读已提交(默认)

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

可重复阅读

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


C
Community

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

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

https://i.stack.imgur.com/cDh13.png

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

https://i.stack.imgur.com/MfpH0.png


i
ivpavici

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

https://i.stack.imgur.com/lPJlJ.png


不辞长做岭南人

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


S
Sanjeev Dhiman

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

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


您可能希望将其放在评论部分,以便回答者得到通知。这样,他将能够回应您的观察并在需要时进行更正。