锁升级的表死锁问题

问题描述

我有一张表,其中有1到5百万条记录成批进入,然后在它们上运行了一堆存储过程,这些存储过程更新和删除了该批中的记录。 所有这些存储过程都使用两个字段进行选择性选择,因此它们仅在该批处理中的记录上运行。 这两个字段都在非聚集索引中。 有时会同时运行多个批处理,并且由于锁升级,我不断在批处理之间发生死锁。 尝试找出是否有一种方法可以解决此问题,而无需进行完全重新设计就可以为每个批次使用专用表。禁用页面锁会带来更多麻烦吗?

其他信息:

表结构和索引的示例(实际的列多于此)

CREATE TABLE [dbo].[TempImport](
    [UID] [int] IDENTITY(1,1) NOT NULL,[EID] [int] NULL,[EXTID] [int] NULL,[COL1] [varchar](50) NULL,[COL2] [varchar](50) NULL,CONSTRAINT [PK_TempImport] PRIMARY KEY CLUSTERED 
(
    [UID] ASC
)WITH (PAD_INDEX = OFF,STATISTICS_norECOmpuTE = OFF,IGnorE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON,OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [IX_TempImport_Main] ON [dbo].[TempImport]
(
    [EID] ASC,[EXTID] ASC
)WITH (PAD_INDEX = OFF,SORT_IN_TEMPDB = OFF,DROP_EXISTING = OFF,ONLINE = OFF,OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

存储过程中的查询类型如下所示:

update TempImport set COL1 = 'foo' where EID = @EID and EXTID = @EXT and COL2='bar'

批处理完成时发生的最后一件事是这样的:

Delete from TempImport where EID = @EID and EXTID = @EXT

死锁通常涉及存储过程中的删除和更新。

请让我知道其他有用的信息

解决方法

一些潜在的建议

  • 是的,您可以将它们分成2000个批次(例如),以阻止行锁升级为表锁,但是您的运行量将达到万亿。可能不是一个好的解决方案。
  • 您可以修改更新过程(按照Brent Ozar's video re deadlocks I recommended in the comments),以仅一次更新每个表。作为建议,您也可以尝试将它们封装在事务中。这些可以消除死锁,但会增加阻塞(第二个操作必须等待第一个操作完成)。
  • 一种结构化方法是制作一个“加载”或“暂存”表,该表具有要操作的ID和相关数据(您可以将其视为队列)。当调用进行更新时,他们只需将其请求插入该队列中。然后,您有一个异步过程(一次只能被一个调用),该过程从队列中获取所有未完成的数据(并进行标记),进行相关处理,然后从暂存表中清除已处理的数据。 / li>

请注意,如果您有其他东西要访问/使用此表,那么它们也可能在此表上被阻塞或死锁,因此您的方法需要非常小心。