2009-04-21 129 views
0

此SQL(從C#調用)偶爾會導致死鎖。 服務器沒有太多的負載,所以使用的方法是儘可能地鎖定。SqlServer,事務死鎖,什麼時候表被實際鎖定?

-- Lock to prevent race-conditions when multiple instances of an application calls this SQL: 
     BEGIN TRANSACTION 
-- Check that no one has inserted the rows in T1 before me, and that T2 is in a valid state (Test1 != null) 
      IF NOT EXISTS (SELECT TOP 1 1 FROM T1 WITH(HOLDLOCK, TABLOCKX) WHERE FKId IN {0}) AND 
      NOT EXISTS(SELECT TOP 1 1 FROM T2 WITH(HOLDLOCK, TABLOCKX) WHERE DbID IN {0} AND Test1 IS NOT NULL) 
      BEGIN 
-- Great! Im the first - go insert the row in T1 and update T2 accordingly. Finally write a log to T3 
       INSERT INTO T1(FKId, Status) 
       SELECT DbId, {1} FROM T2 WHERE DbId IN {0}; 

       UPDATE T2 SET LastChangedBy = {2}, LastChangedAt = GETDATE() WHERE DbId IN {0}; 

       INSERT INTO T3 (F1, FKId, F3) 
       SELECT {2}, DbId, GETDATE() FROM T2 WHERE DbId IN {0} ; 
      END; 

      -- Select status on the rows so the program can evaluate what just happened 
      SELECT FKId, Status FROM T1 WHERE FkId IN {0}; 

     COMMIT TRANSACTION 

我相信問題是需要鎖定多個表。

當表格實際上被鎖定時 - 當第一次使用表格時 - 或者在BEGIN TRANS一次鎖定所有表格時,我有點不確定嗎?

回答

1

鎖定是在您調用鎖定或選擇鎖定並在提交或回滾時釋放時完成的。

如果另一個過程先在T3中鎖定,然後在T1或T2中鎖定,則可能會遇到死鎖。然後兩個事務正在等待對方獲取資源,同時鎖定其他需要的事務。

您還可以避免表鎖和使用隔離級別可序列化。

+0

+1提到的可序列化模式。更細粒度的行級鎖不易受到死鎖的影響。 – 2009-05-27 19:45:29

+0

是的,表鎖實際上是一個非常糟糕的表現,但如果它是讓事情正常工作的唯一解決方案,那麼它就是一個解決方案。 – 2009-05-28 06:55:29

0

鎖定的問題在於,您確實需要查看您在同一時間鎖定的所有位置,無法將問題分離並分解爲許多較小的問題並單獨查看這些問題。

例如,如果某些其他代碼鎖定了相同的表,但沒有顯而易見,並且順序錯誤?這會導致僵局。

您需要分析發現死鎖時的服務器狀態,試圖找出此刻還在運行的其他服務器。只有這樣才能嘗試修復它。

3

使用表鎖可以增加發生死鎖的可能性......並非所有的死鎖都是由無序操作引起的......有些可能是由其他活動引起的(正如您找到的那樣),其他活動只會嘗試鎖定在完全鎖定的同一個表中的單個記錄,因此鎖定整個表會增加發生衝突的可能性。當使用可序列化的隔離級別時,範圍鎖放置在索引行上,這樣可以防止其他SQL操作的插入/刪除,其方式可能會由於來自同一過程的兩個併發操作而導致死鎖,即使它們被編碼以執行其操作ops以相同的順序...

在任何情況下,要找出導致死鎖的確切原因,請設置SQL Server跟蹤標誌1204和1222.這些會導致將詳細信息寫入SQL Server日誌有關每個僵局,包括涉及什麼陳述。

Here是一篇關於如何做到這一點的好文章。

(不要忘記當你完成後關閉這些標誌......)