小编典典

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

sql

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

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)提示一起使用。是否可能不仅仅为了并行更新那两行就锁定整个表?


阅读 193

收藏
2021-04-07

共1个答案

小编典典

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

或者,您添加重试逻辑以防检测到死锁(SqlException.Number == 1205)。这更优雅,因为它不需要更深层的代码更改。但是死锁会对性能产生影响,因此只有在低死锁率时才这样做。

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

2021-04-07