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> 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
8条答案
按热度按时间sycxhyv71#
“已提交读”是一种隔离级别,它保证读取的任何数据在读取时都是“已提交”的。它只是限制读取器查看任何中间的、未提交的、“脏”的读取。它不保证如果事务处理重新发出读取,将找到“相同”的数据,数据在读取后可以自由更改。
可重复读取是更高的隔离级别,除了保证已提交读取级别之外,它还保证任何读取的数据 * 不能更改 *,如果事务再次读取相同的数据,它将发现先前读取的数据处于适当位置,未更改,并且可供读取。
下一个隔离级别是可序列化,它提供了更强的保证:除了可重复读取所保证的一切之外,它还保证后续读取无法看到 * 没有新数据 *。
假设你有一个表T,表中有一列C,列C中有一行,列C的值为'1',假设你有一个简单的任务,如下所示:
这是一个简单任务,它从表T发出两次读操作,两次读操作之间有1分钟的延迟。
如果你遵循上面的逻辑,你会很快意识到SERIALIZABLE事务,虽然它们可能会让你的生活变得轻松,但总是 * 完全阻塞 * 每一个可能的并发操作,因为它们要求任何人都不能修改、删除或插入任何行。.Net
System.Transactions
作用域的默认事务隔离级别是可序列化的,这通常解释了为什么性能如此糟糕。最后,还有SNAPSHOT隔离级别。SNAPSHOT隔离级别提供了与可序列化相同的保证,但不是要求并发事务不能修改数据,而是强制每个读取器看到自己的世界版本(它自己的“快照”)。这使得它非常容易编程,并且非常可伸缩,因为它不阻止并发更新。然而,这种好处是有代价的:额外的服务器资源消耗。
补充内容如下:
pokxtpni2#
可重复读取
数据库的状态从事务处理开始时就得到维护。如果在会话1中检索值,然后在会话2中更新该值,则在会话1中再次检索该值将返回相同的结果。读取是可重复的。
已提交读取
在事务的上下文中,您将始终检索最近提交的值。如果在session1中检索值,在session2中更新该值,然后在session1中再次检索该值,则将获得在session2中修改的值。它读取最后提交的行。
有道理吗?
628mspwn3#
简单的答案根据我的阅读和理解这个线程和@remus-rusanu答案是基于这个简单的场景:
有两个事务A和B。事务B正在阅读表X事务A正在写入表X事务B正在再次读取表X。
*读取未提交:事务B可以从事务A读取未提交的数据,并且可以根据B的写入操作看到不同的行。完全没有锁
*已读取并提交:事务B只能从事务A读取已提交的数据,并且可以根据仅提交的B写入操作看到不同的行。我们可以称之为简单锁吗?
*可重复读取:无论事务A执行什么操作,事务B都将读取相同的数据(行)。但事务A可以更改其他行。行级别块
*可序列化:事务B将读取与以前相同的行,事务A无法在表中进行读写操作。表级块
*快照:每个事务都有自己的副本,并且他们正在处理它。每个事务都有自己的视图
qvtsj1bj4#
这是一个已经有公认答案的老问题,但我喜欢从它们如何改变SQL Server中的锁定行为的Angular 来考虑这两个隔离级别。这可能对那些像我一样调试死锁的人有帮助。
已提交读取(默认)
共享锁是在SELECT语句中获取的,然后在SELECT语句完成时释放。这就是系统保证不对未提交的数据进行脏读的方法。在SELECT语句完成之后和事务完成之前,其他事务仍然可以更改基础行。
可重复读取
在SELECT中获取共享锁,然后仅在事务完成后**才释放共享锁。这就是系统如何保证您读取的值在事务期间不会更改(因为它们在事务完成之前一直保持锁定状态)。
wvmv3b1j5#
试图用简单的图表来解释这个疑问。
**已提交读取:**此处在此隔离级别中,事务T1将阅读由事务T2提交的X的更新值。
**可重复读取:**在此隔离级别中,事务T1将不考虑事务T2提交的更改。
yqlxgs2m6#
我认为这张图片也可以很有用,当我想要快速记住隔离级别之间的差异时,它可以帮助我作为参考(感谢YouTube上的kudvenkat)
wkyowqbh7#
请注意,repeatable read中的 repeatable 与元组有关,而不是与整个表有关。在ANSC隔离级别中,可能会发生 phantom read 异常,这意味着使用相同的where子句读取表两次可能会返回不同的结果集。从字面上讲,它不是 repeatable。
frebpwbc8#
我对最初接受的解决方案的观察。
在RR下(默认mysql)-如果一个tx是打开的,并且一个SELECT已经被触发,另一个tx不能删除任何属于先前READ结果集的行,直到先前的tx被提交(事实上,新tx中的delete语句将挂起),但是下一个tx可以毫无问题地删除表中的 * 所有行 *。顺便说一句,先前tx中的下一个READ仍然会看到旧数据,直到它被提交。