读取提交和可重复读取之间的区别

时间:2010-10-27 15:35:35

标签: sql sql-server isolation-level

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

8 个答案:

答案 0 :(得分:483)

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

可重复读取是一个更高的隔离级别,除了保证读取提交级别之外,它还保证读取的任何数据都不能更改,如果事务再次读取相同的数据,它将找到先前读取的数据,保持不变并可供阅读。

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

假设你有一个表T,其中C列有一行,比如它的值为'1'。并且考虑一下如下的简单任务:

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

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

  • 在READ COMITTED下,第二个SELECT可以返回任何数据。并发事务可以更新记录,删除它,插入新记录。第二个选择将始终显示 new 数据。
  • 在REPEATABLE READ下,第二个SELECT保证看到第一次选择未更改时看到的行。在那一分钟内,并发事务可以添加新行,但不能删除或更改现有行。
  • 在SERIALIZABLE下读取第二个选择保证看到完全相同。没有行可以更改,也不能删除,并且并发事务也不能插入新行。

如果您遵循上述逻辑,您可以快速意识到SERIALIZABLE事务虽然可以让您轻松生活,但始终完全阻止每个可能的并发操作,因为它们要求任何人都无法修改,删除也不插入任何行。 .Net System.Transactions范围的默认事务隔离级别是可序列化的,这通常可以解释导致的糟糕性能。

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

补充读物:

答案 1 :(得分:59)

可重复读取

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

Read Committed

在事务的上下文中,您将始终检索最近提交的值。如果在session1中检索值,在session2中更新它,然后在session1again中检索它,您将获得在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

有道理吗?

答案 2 :(得分:19)

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

有两个流程A和B. 过程B正在阅读表X. 流程A写在表X中 进程B再次阅读表X.

  • ReadUncommitted :进程B可以从进程A读取未提交的数据,并且可以根据B写入查看不同的行。 完全无法锁定
  • ReadCommitted :进程B只能从进程A读取已提交的数据,并且它可以看到基于COMMITTED仅B写入的不同行。 我们可以称之为Simple Lock吗?
  • RepeatableRead :进程B将读取进程A正在执行的相同数据(行)。但是进程A可以更改其他行。 行级阻止
  • Serialisable :进程B将读取与以前相同的行,并且进程A无法在表中读取或写入。 表级阻止
  • 快照:每个进程都有自己的副本,他们正在处理它。 每个人都有自己的观点

答案 3 :(得分:12)

旧问题已经有了一个已接受的答案,但我想考虑这两个隔离级别是如何改变SQL Server中的锁定行为的。对于那些像我一样调试死锁的人来说,这可能会有所帮助。

READ COMMITTED(默认)

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

重复阅读

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

答案 4 :(得分:10)

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

  

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

Read Committed

  

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

enter image description here

答案 5 :(得分:0)

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

enter image description here

答案 6 :(得分:0)

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

答案 7 :(得分:-1)

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

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