2013-11-27 42 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

我认为你仍然会遇到死锁,除非你重写你的查询以获得一致的顺序锁。它仍然听起来像真正的应用程序将受益于集中和序列化更新,如果碰撞很有可能。它可能会提高吞吐量并给出更一致的延迟。 –