Answers:
提交的读取是隔离级别,可确保读取当前已提交的任何数据。它只是限制了读者看不到任何中间的,未提交的“脏”读物。它不保证如果事务重新发出读取,将找到相同的数据,则数据在读取后可以自由更改。
可重复读是一个较高的隔离级别,除了保证已提交读级别外,它还保证任何读取的数据都不能更改,如果事务再次读取相同的数据,它将发现原先读取的数据保持不变,可供阅读。
接下来的隔离级别,序列化,使得一个更强大的保障:除了所有重复的读取担保,这也保证了没有新的数据可以通过后续读取待观察。
假设您有一个表T,其中的C列中有一行,说它的值为'1'。考虑一下您有一个简单的任务,如下所示:
BEGIN TRANSACTION;
SELECT * FROM T;
WAITFOR DELAY '00:01:00'
SELECT * FROM T;
COMMIT;
这是一个简单的任务,它从表T发出两次读取,两次读取之间有1分钟的延迟。
如果遵循上述逻辑,您会很快意识到,尽管SERIALIZABLE事务使您的生活变得轻松,但它们始终完全阻止所有可能的并发操作,因为它们不需要任何人可以修改,删除或插入任何行。.Net System.Transactions
范围的默认事务隔离级别是可序列化的,这通常可以解释所导致的糟糕性能。
最后,还有SNAPSHOT隔离级别。SNAPSHOT隔离级别提供与可序列化相同的保证,但是不要求任何并发事务都不能修改数据。相反,它强迫每个读者看到自己的世界版本(这是自己的“快照”)。由于它不阻止并发更新,因此可以很容易地针对它进行编程以及可扩展性。但是,这种好处是有代价的:额外的服务器资源消耗。
补充读物:
从事务开始就维护数据库的状态。如果您在会话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
说得通?
根据我对这个线程的阅读和理解,答案很简单,而@ remus-rusanu答案则基于以下简单场景:
进程A和B有两个。进程B正在读取表X进程A正在写入表X进程B正在再次读取表X。
我对最初接受的解决方案的观察。
在RR(默认mysql)下-如果一个tx已打开并且SELECT已被激发,则另一个tx在提交前一个tx之前,不能删除属于先前READ结果集的任何行(实际上,新tx中的delete语句只会挂起) ,但是下一个TX可以毫无问题地删除表中的所有行。顺便说一句,前一个TX中的下一个READ仍将看到旧数据,直到将其提交为止。