【发布时间】:2011-08-08 19:45:26
【问题描述】:
我使用一个由两个简单查询组成的小事务:选择和更新:
SELECT * FROM XYZ WHERE ABC = DEF
和
UPDATE XYZ SET ABC = 123
WHERE ABC = DEF
当事务由两个线程启动时,经常发生这种情况,并且取决于隔离级别发生死锁(RepeatableRead,Serialization)。两个事务都尝试读取和更新完全相同的行。 我想知道为什么会这样。导致死锁的查询顺序是什么?我已经阅读了一些关于锁(共享、独占)以及每个隔离级别的锁持续多长时间,但我仍然不完全理解......
我什至准备了一个简单的测试,它总是会导致死锁。我查看了 SSMS 和 SQL Server Profiler 中的测试结果。我开始第一个查询,然后立即开始第二个。
第一个查询:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT ...
WAITFOR DELAY '00:00:04'
UPDATE ...
COMMIT
第二次查询:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT ...
UPDATE ...
COMMIT
现在我无法向您显示详细的日志,但它看起来或多或少像这样(我很可能在某处错过了 Lock:deadlock 等):
(1) SQL:BatchStarting: First query
(2) SQL:BatchStarting: Second query
(3) Lock:timeout for second query
(4) Lock:timeout for first query
(5) Deadlock graph
如果我对锁有很好的理解,在 (1) 中,第一个查询需要一个共享锁(执行 SELECT),然后进入睡眠状态并保持共享锁直到事务结束。在(2)中,第二个查询也使用共享锁(SELECT)但不能使用排他锁(UPDATE),而同一行上有共享锁,这会导致Lock:timeout。但我无法解释为什么会发生第二次查询超时。可能我不太了解整个过程。谁能给个解释?
我没有注意到使用 ReadCommitted 的死锁,但我担心它们可能会发生。 您推荐什么解决方案?
【问题讨论】:
-
不是你问的,但你为什么要先选择,然后更新?换句话说,为什么不使用只包含更新语句的事务呢?
-
我简化了我的问题。首先我检查最后修改日期,然后根据值做一些事情,然后更改日期。在这个事务中,有更多的查询,但上面的这些会导致死锁问题。我试图完全理解是什么原因,因为我以前从未听说过在互联网上发现的锁和信息不足以满足我:) 在我的情况下,脏读不是问题,所以我决定选择未提交的读取。我使用 realizable 只是为了看看会发生什么,一种实验 :)
-
应该是serializable而不是realizable :)我忘了说我用的是c#,不过没关系。
标签: sql sql-server-2008 transactions deadlock isolation-level