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

For*_*ore 223 sql sql-server isolation-level

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

Rem*_*anu 517

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

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

接下来的隔离级别,序列化,使得一个更强大的保障:除了一切可重复读的保证,同时也保证了没有的数据可以通过后续读取待观察.

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

BEGIN TRANSACTION;
SELECT * FROM T;
WAITFOR DELAY '00:01:00'
SELECT * FROM T;
COMMIT;
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

补充读物:

  • 我认为REPEATABLE READ上面有一个错误:你说现有的行不能删除也不能改变,但我认为它们可以被删除或更改,因为可重复读取只是读取"快照"而不是实际数据.从文档http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read:"同一事务中的所有一致读取读取第一次读取建立的快照." (22认同)
  • 感谢您不要删除您的评论.讨论有助于连接更多的点. (11认同)
  • @Cornstalks.是的,Phantom读取可以从删除(或插入)中发生.是的,幻像读取可以在可重复的读取隔离中发生(仅来自插入).不,Phantom读取删除不能发生在可重复的读取隔离中.测试一下.我所说的与您引用的文档并不矛盾. (5认同)
  • @Cornstalks NP.我只提到它,因为我不是100%肯定自己,不得不深入了解谁是对的!而且我不希望未来的读者误导.重新保留评论,可能最好保持建议.我敢肯定,对这个细节水平感兴趣的人会特别注意阅读所有评论!! (4认同)
  • @AndyBrown(和 Remus Rusanu):我愿意删除我原来的评论(以避免误导未来的读者),但我也愿意保留它(以保留这些评论的上下文)。我会保留它,但如果你认为最好删除它,我会的。 (3认同)
  • @Derek Litz我是对的,你说的是:数据可以从第三方改变,而交易正在进行,但读取仍然会看到"旧的"原始数据,好像没有采取改变一样放置(快照). (2认同)
  • @Cornstalk:测试你的假设真的*微不足道*。设置 tran 隔离可重复读取,开始 tran,读取行,不提交。从第二个会话尝试删除读取的行。让我们知道结果。 (2认同)

Haz*_*run 63

可重复阅读

从事务开始就维护数据库的状态.如果在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
Run Code Online (Sandbox Code Playgroud)

阅读已提交

在事务的上下文中,您将始终检索最近提交的值.如果在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
Run Code Online (Sandbox Code Playgroud)

说得通?

  • 我认为可重复读取将阻止第二个会话,直到第一个会话提交.所以这个例子是错误的. (8认同)
  • 在可重复读取的情况下,当会话1读取行时,它会放置一个共享锁,这将不允许任何独占锁(到会话2)进行更新,因此无法更新数据. (3认同)
  • 我认为 SQL Server 和 MySQL 在更新两个事务之间的共享行时表现不同 (2认同)

Mo *_*tar 20

根据我对这个帖子和@ 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无法在表中读取或写入.表级块
  • 快照:每个进程都有自己的副本,他们正在处理它.每个人都有自己的观点

  • RepeatableRead:答案指出“事务 A 可以更改...”与“行级块”不一致。这两件事不可能同时成立。如果存在行级块,则根据定义,事务 A 无法更改数据。 (2认同)

Chr*_*lum 13

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

READ COMMITTED(默认)

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

可重复阅读

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


vkr*_*a17 11

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

Read Committed:在此隔离级别,事务T1将读取事务T2提交的X的更新值.

阅读已提交

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

在此输入图像描述


Iva*_*čić 6

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

在此输入图像描述