避免并发删除死锁

Posted

技术标签:

【中文标题】避免并发删除死锁【英文标题】:Avoid deadlock for concurrent delete 【发布时间】:2013-02-25 18:12:44 【问题描述】:

我有一个名为Products 的表,其中包含许多列。它是用于报告目的的临时表。多个用户请求将同时处理数据到该表。有单独的存储过程可以对该表进行 DML 操作。

表结构:

CREATE TABLE Products (
  instance uniqueidentifier,
  inserted datetime,
  col1, 
  col2,
  ...
)

inserted 列将填充 GETDATE() 以包含插入每一行的时间,instance 列将包含来自 newid() 的值。一个用户请求将有一个唯一的 id,但可能有数百万行。以下是将并发执行的查询,这会导致死锁。请指教

查询 1:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 
DELETE P 
FROM Products (NOLOCK) 
WHERE instance = 'XXXX-xxx-xxx-xx'

查询 2:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED 
DELETE P 
FROM Products (NOLOCK) 
WHERE inserted <= DATEADD(hh, -10, GETDATE())

注意:非聚集索引是在实例列上创建的。

请告诉我在这种情况下我可以使用哪种锁。

请注意,当我向表中插入 1000 万行时,我无法主键,因为它会消耗时间(这是一个事务;有 20 个并发事务)。 报告应尽快生成。我的程序有多个 35 个 DML 语句,大约有 15 个 DELETE 语句,例如带有其他列的实例列(DELETE FROM table WHERE instance = @instance AND col1 = @col1)。

【问题讨论】:

看看这个***.com/questions/9952137/… 【参考方案1】:

(1) 您应该停止使用read uncommitted 隔离。至少使用read committed

(2) 您可以尝试多种方法来避免死锁,例如确保您的不同事务以相同的顺序访问数据库对象等。这值得一读 - http://support.microsoft.com/kb/169960

(3) 为您的表禁用锁升级(更精细的锁因此更好的并发性,但更多的锁开销):

ALTER TABLE Products SET (lock_escalation = disable)

(4) 禁止页面锁定,并允许对索引进行行锁定(这意味着您不能对索引进行碎片整理,但您仍然可以重建它们):

ALTER INDEX [<YourIndex>] ON Product WITH (allow_row_locks = on, allow_page_locks = off)

【讨论】:

【参考方案2】:

首先,除了独占锁之外,您无法对这些删除语句使用任何锁。你的isolation level and NOLOCK hints are being ignored by Sql Server:

(Nolock) 仅适用于SELECT 语句。

两个建议:

instance 上的非聚集索引更改为聚集索引。但是,只有在您可以将 NEWID() 更改为 NEWSEQUENTIALID() 时才这样做。

其次,不要执行delete 来删除超过 10 小时的记录...考虑implementing rolling partitions。这将消除因清理与您的其他 delete 操作引起的任何争用。

【讨论】:

以上是关于避免并发删除死锁的主要内容,如果未能解决你的问题,请参考以下文章

死锁产生的原因及避免死锁的方法

深入了解Java并发——《Java Concurrency in Practice》10.避免活跃性危险

死锁面试题

《Java并发编程实战》第十章 避免活跃性危急 读书笔记

《Java并发编程实战》第十章 避免活跃性危急 读书笔记

mysql 开发进阶篇系列 14 锁问题(避免死锁,死锁查看分析)