2013-11-27 72 views
2

代碼,注意值的順序是不同的。因此,它交替鎖定行之間:這個簡單的代碼會產生死鎖。包含簡單示例程序

static void Main(string[] args) 
     { 
      List<int> list = new List<int>(); 

      for(int i = 0; i < 1000; i++) 
       list.Add(i); 

      Parallel.ForEach(list, i => 
      { 
       using(NamePressDataContext db = new NamePressDataContext()) 
       { 
        db.ExecuteCommand(@"update EBayDescriptionsCategories set CategoryId = Ids.CategoryId from EBayDescriptionsCategories 
         join (values (7276, 20870),(240, 20870)) as Ids(Id,CategoryId) on Ids.Id = EBayDescriptionsCategories.Id"); 

        db.ExecuteCommand(@"update EBayDescriptionsCategories set CategoryId = Ids.CategoryId from EBayDescriptionsCategories 
         join (values (240, 20870),(7276, 20870)) as Ids(Id,CategoryId) on Ids.Id = EBayDescriptionsCategories.Id"); 
       } 

      }); 
     } 

表DEF:

CREATE TABLE [dbo].[EDescriptionsCategories](
    [CategoryId] [int] NOT NULL, 
    [Id] [int] NOT NULL, 
CONSTRAINT [PK_EDescriptionsCategories] PRIMARY KEY CLUSTERED 
(
    [Id] ASC 
) 

例外:

Transaction (Process ID 80) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction. 

的代碼只適用於WITH(TABLOCK)提示。是否有可能不鎖定整個表,只是爲了更新這兩行並行?

+0

我試着設置(HOLDLOCK),它應該做同樣的事情。除WITH(TABLOCK)外沒有任何工作。我認爲正在發生的是開始單獨鎖定每一行。因爲它們從不同的行開始,所以會發生死鎖。我想知道在更新開始之前是否可以鎖定所有引用的行。 – Dennis

+0

好吧,你以相反的順序更新這兩行,所以這些更新互相鎖定。你需要並行運行兩個在同一行上工作的查詢嗎? –

+0

這是一個練習嗎?您不會故意嘗試在生產中的同一事務中並行更新兩行嗎? – Rikalous

回答

1

你的兩條語句以不同的順序獲取行鎖。這是死鎖的經典案例。您可以通過確保所採用的鎖定順序始終處於某種全局順序(例如,按ID排序)來解決此問題。在將兩個UPDATE語句發送到SQL Server之前,您應該將兩個UPDATE語句合併爲一個並對客戶端上的ID列表進行排序。對於許多典型的UPDATE計劃,這實際上工作正常(但不保證,但)。

或者,如果檢測到死鎖,則添加重試邏輯(SqlException.Number == 1205)。這更優雅,因爲它不需要更深的代碼更改。但是死鎖具有性能影響,所以只有在低死鎖率時纔會這樣做。

如果你的並行處理會產生大量的更新,你可以INSERT所有這些更新到一個臨時表(可同時進行),當你做了你執行一個大UPDATE,所有副本的單獨的更新記錄到主表。您只需更改示例查詢中的連接源。

+0

我簡化了這個問題。在實際的應用程序中,它更新隨機的行集,這是我從不同線程無法控制的。所以我不能結合這兩個聲明。是否可以在更新之前鎖定我要更新的行。這將解決我的問題。 – Dennis

+0

@Dennis在這種情況下,U鎖和X鎖的行爲方式相同。他們互相阻攔。所以X鎖不會改變任何東西。問題是他們被採取的順序。如果您想要簡單修復,請執行我建議的重試或插入表。兩者都是完全安全的。 – usr

+0

我喜歡你對Id訂購的想法。我認爲這應該工作。因爲我自己創建ID,而不是從一些有序的索引。它將保留每行的U鎖直到我提交。對? – Dennis

0

代碼,注意值的順序是不同的。所以它在鎖定行之間交替

不,它不交替。它以兩種不同的順序獲取鎖。死鎖是保證

是否有可能不...更新這兩行並行?

不是這樣的。你要求的是死鎖的定義。有些事情要付出。解決方案必須來自您的業務邏輯,不應該嘗試處理來自不同事務的同一組ID。這意味着整個業務都是特定的。如果你無法做到這一點,那麼基本上你只是乞求僵局。有些事情你可以做,但沒有一個是防彈的,而且都是以很高的成本。鏈條上的問題更高。

+0

我簡化了這個問題。在實際的應用程序中,它更新隨機的行集,這是我從不同線程無法控制的。所以我不能結合這兩個聲明。是否可以在更新之前鎖定我要更新的行。這將解決我的問題。 – Dennis

0

同意其他答案關於鎖定。

更緊迫的問題是你希望從中獲得什麼?這些命令只有一條電纜走下去。

這樣做可能會使整體性能變差。更好的做法是平行計算計算,但序列化(也可能批處理)更新。

+0

我簡化了這個問題。在實際的應用程序中,它更新隨機的行集,這是我從不同線程無法控制的。所以我不能結合這兩個聲明。是否可以在更新之前鎖定我要更新的行。這將解決我的問題。 – Dennis

+0

我認爲你仍然會遇到死鎖,除非你重寫你的查詢以獲得一致的順序鎖。它仍然聽起來像真正的應用程序將受益於集中和序列化更新,如果碰撞很有可能。它可能會提高吞吐量並給出更一致的延遲。 –