代码,注意值的顺序不同。因此,它在锁定行之间交替:
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)提示一起使用。是否可能不仅仅为了并行更新那两行就锁定整个表?
您的两个语句以不同的顺序获取行锁。这是死锁的典型案例。您可以通过确保所采用的锁定顺序始终处于某种全局顺序(例如,按ID顺序)来解决此问题。您可能应该将这两个UPDATE语句合并为一个,并在将客户端上的ID列表排序之前,将其发送到SQL Server。对于许多典型的UPDATE计划,它实际上可以正常工作(尽管不能保证)。
UPDATE
或者,您添加重试逻辑以防检测到死锁(SqlException.Number == 1205)。这更优雅,因为它不需要更深层的代码更改。但是死锁会对性能产生影响,因此只有在低死锁率时才这样做。
SqlException.Number == 1205
如果您的并行处理生成大量更新,则可以将INSERT所有这些更新放入一个临时表中(可以同时进行),完成后,您执行一个大操作UPDATE,将所有单独的更新记录复制到主表中。您只需在示例查询中更改联接源。
INSERT