SQL Server中“已提交读取”和“可重复读取”之间的区别

mmvthczy  于 2022-12-17  发布在  SQL Server
关注(0)|答案(8)|浏览(315)

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

sycxhyv7

sycxhyv71#

“已提交读”是一种隔离级别,它保证读取的任何数据在读取时都是“已提交”的。它只是限制读取器查看任何中间的、未提交的、“脏”的读取。它不保证如果事务处理重新发出读取,将找到“相同”的数据,数据在读取后可以自由更改。
可重复读取是更高的隔离级别,除了保证已提交读取级别之外,它还保证任何读取的数据 * 不能更改 *,如果事务再次读取相同的数据,它将发现先前读取的数据处于适当位置,未更改,并且可供读取。
下一个隔离级别是可序列化,它提供了更强的保证:除了可重复读取所保证的一切之外,它还保证后续读取无法看到 * 没有数据 *。
假设你有一个表T,表中有一列C,列C中有一行,列C的值为'1',假设你有一个简单的任务,如下所示:

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

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

  • 在READ COMMITTED下,第二次SELECT可以返回任何数据。2并发事务可以更新记录、删除记录、插入新记录。3第二次SELECT将总是看到新数据。
  • 在REPEATABLEREAD下,保证第二次SELECT至少显示从第一次SELECT返回的行 * 未改变 *。2并发事务可以在这一分钟内添加新行,但不能删除或改变现有行。
  • 在SERIALIZABLE读取下,第二次选择保证看到与第一次完全相同的行。2任何行都不能被改变、删除,也不能被并发事务插入新行。

如果你遵循上面的逻辑,你会很快意识到SERIALIZABLE事务,虽然它们可能会让你的生活变得轻松,但总是 * 完全阻塞 * 每一个可能的并发操作,因为它们要求任何人都不能修改、删除或插入任何行。.Net System.Transactions作用域的默认事务隔离级别是可序列化的,这通常解释了为什么性能如此糟糕。
最后,还有SNAPSHOT隔离级别。SNAPSHOT隔离级别提供了与可序列化相同的保证,但不是要求并发事务不能修改数据,而是强制每个读取器看到自己的世界版本(它自己的“快照”)。这使得它非常容易编程,并且非常可伸缩,因为它不阻止并发更新。然而,这种好处是有代价的:额外的服务器资源消耗。
补充内容如下:

pokxtpni

pokxtpni2#

可重复读取

数据库的状态从事务处理开始时就得到维护。如果在会话1中检索值,然后在会话2中更新该值,则在会话1中再次检索该值将返回相同的结果。读取是可重复的。

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

有道理吗?

628mspwn

628mspwn3#

简单的答案根据我的阅读和理解这个线程和@remus-rusanu答案是基于这个简单的场景:
有两个事务A和B。事务B正在阅读表X事务A正在写入表X事务B正在再次读取表X。

*读取未提交:事务B可以从事务A读取未提交的数据,并且可以根据B的写入操作看到不同的行。完全没有锁
*已读取并提交:事务B只能从事务A读取已提交的数据,并且可以根据仅提交的B写入操作看到不同的行。我们可以称之为简单锁吗?
*可重复读取:无论事务A执行什么操作,事务B都将读取相同的数据(行)。但事务A可以更改其他行。行级别块
*可序列化:事务B将读取与以前相同的行,事务A无法在表中进行读写操作。表级块
*快照:每个事务都有自己的副本,并且他们正在处理它。每个事务都有自己的视图

qvtsj1bj

qvtsj1bj4#

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

已提交读取(默认)

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

可重复读取

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

wvmv3b1j

wvmv3b1j5#

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

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

**可重复读取:**在此隔离级别中,事务T1将不考虑事务T2提交的更改。

yqlxgs2m

yqlxgs2m6#

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

wkyowqbh

wkyowqbh7#

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

frebpwbc

frebpwbc8#

我对最初接受的解决方案的观察。
在RR下(默认mysql)-如果一个tx是打开的,并且一个SELECT已经被触发,另一个tx不能删除任何属于先前READ结果集的行,直到先前的tx被提交(事实上,新tx中的delete语句将挂起),但是下一个tx可以毫无问题地删除表中的 * 所有行 *。顺便说一句,先前tx中的下一个READ仍然会看到旧数据,直到它被提交。

相关问题