这个简单的代码会产生死锁。包括简单示例程序

时间:2013-11-27 09:57:18

标签: c# sql sql-server-2012

代码,注意值的顺序是不同的。因此它在锁定行之间交替:

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)提示。有可能不是仅仅为了并行更新那两行来锁定整个表吗?

3 个答案:

答案 0 :(得分:1)

您的两个语句以不同的顺序获取行锁。这是死锁的经典案例。您可以通过确保所采取的锁定顺序始终处于某种全局顺序(例如,按ID排序)来解决此问题。您应该将两个UPDATE语句合并为一个,并在将其发送到SQL Server之前对客户端上的ID列表进行排序。对于许多典型的UPDATE计划,这实际上工作正常(但不保证)。

或者,如果检测到死锁(SqlException.Number == 1205),则添加重试逻辑。这更优雅,因为它不需要更深入的代码更改。但死锁具有性能影响,所以只能在低死锁率下执行此操作。

如果您的并行处理产生了大量更新,您可以INSERT将所有这些更新放入临时表(可以同时完成),并在完成后执行一个大的UPDATE复制所有个人更新记录到主表。您只需更改示例查询中的联接源。

答案 1 :(得分:0)

  

代码,注意值的顺序是不同的。所以它在锁定行之间交替

不,它不会交替。它以两种不同的顺序获取锁。死锁是保证

  

是否有可能不......并行更新这两行?

不喜欢它不是。您要求的是死锁的定义。需要付出一些代价。解决方案必须来自您的业务逻辑,不应尝试从不同的事务处理同一组ID。这意味着整个业务的具体情况。如果你无法做到这一点,那么基本上你只是在乞求僵局。有一些事情你可以做,但没有一个是防弹的,所有费用都很高。链条上的问题越来越严重。

答案 2 :(得分:0)

同意关于锁定的其他答案。

更迫切的问题是你希望从中获得什么?这些命令只有一根电缆向下移动。

这可能会让整体表现变得更糟。更好地并行执行计算,但序列化(并可能批量)更新。