我认为上述隔离级别非常相似。有人可以用一些很好的例子来描述主要区别是什么吗?
8 回答
已提交读是一种隔离级别,可确保读取当前已提交的任何数据。它只是限制读者看到任何中间的、未提交的、“脏”的阅读。它不承诺如果事务重新发出读取,将找到相同的数据,数据在读取后可以自由更改。
可重复读是更高的隔离级别,即除了保证读提交级别之外,还保证读到的任何数据都不能改变,如果事务再次读到相同的数据,就会发现之前读到的数据在原地,不变,并且可以阅读。
下一个隔离级别,可序列化,提供了更强大的保证:除了所有可重复读取保证之外,它还保证后续读取不会看到新数据。
假设您有一个表 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 隔离级别提供与可序列化相同的保证,但不要求没有并发事务可以修改数据。相反,它迫使每个读者看到自己的世界版本(它是自己的“快照”)。这使得它非常容易编程并且非常可扩展,因为它不会阻止并发更新。然而,这种好处是有代价的:额外的服务器资源消耗。
补充内容如下:
可重复读取
从事务开始就维护数据库的状态。如果您在 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
说得通?
根据我对这个线程的阅读和理解,答案很简单,@remus-rusanu 的答案是基于这个简单的场景:
有两个事务 A 和 B。事务 B 正在读取表 X 事务 A 正在写入表 X 事务 B 正在再次读取表 X。
- ReadUncommitted:事务 B 可以从事务 A 中读取未提交的数据,并且它可以根据 B 写入看到不同的行。完全没有锁
- ReadCommitted:事务 B 只能从事务 A 中读取已提交的数据,并且它可以看到基于仅提交 B 写入的不同行。我们可以称之为简单锁吗?
- RepeatableRead:无论事务 A 正在做什么,事务 B 都将读取相同的数据(行)。但是事务 A 可以更改其他行。行级块
- 可序列化:事务 B 将读取与之前相同的行,而事务 A 无法在表中读取或写入。表级块
- 快照:每个事务都有自己的副本,他们正在处理它。每个人都有自己的看法
已经有一个公认答案的老问题,但我喜欢考虑这两个隔离级别如何改变 SQL Server 中的锁定行为。这可能对那些像我一样调试死锁的人有所帮助。
读已提交(默认)
在 SELECT 中获取共享锁,然后在 SELECT 语句完成时释放。这就是系统如何保证没有未提交数据的脏读。在您的 SELECT 完成之后和您的事务完成之前,其他事务仍然可以更改基础行。
可重复阅读
在 SELECT 中获取共享锁,然后仅在事务完成后释放。这就是系统如何保证您读取的值在事务期间不会更改(因为它们保持锁定直到事务完成)。
我认为这张图片也很有用,当我想快速记住隔离级别之间的差异时,它可以帮助我作为参考(感谢 youtube 上的kudvenkat)
请注意,repeatable in repeatable read 是针对一个元组,而不是整个表。在 ANSC 隔离级别中,可能会发生幻读异常,这意味着使用相同的 where 子句读取表两次可能会返回不同的返回不同的结果集。从字面上看,它是不可重复的。
我对最初接受的解决方案的观察。
在 RR(默认 mysql)下 - 如果一个 tx 已打开并且 SELECT 已被触发,则另一个 tx 不能删除属于先前 READ 结果集的任何行,直到前一个 tx 被提交(实际上新 tx 中的删除语句只会挂起) ,但是下一个 tx 可以毫无问题地从表中删除所有行。顺便说一句,在前一个 tx 中的下一个 READ 仍然会看到旧数据,直到它被提交。