【发布时间】:2016-02-15 22:01:35
【问题描述】:
我的客户在这两个语句之间经常出现死锁:
DELETE b
FROM employees a WITH (NOLOCK), employees_prs b WITH (NOLOCK)
WHERE a.employer_id = @id
AND a.id = b.employee_id
AND b.employee_prs = 1
INSERT INTO employees_prs (employee_id, employee_prs)
SELECT DISTINCT a.id, 1
FROM #tempdata01 b, employees a WITH (NOLOCK)
WHERE a.employer_id = @id
AND a.no_id = b.no_id
死锁图中的资源列表显示如下:
<resource-list>
<pagelock fileid="4" pageid="28897262" dbid="14" objectname="db01.dbo.employees_prs" id="lock373f60880" mode="IX" associatedObjectId="72057601182466048">
<owner-list>
<owner id="process352a02988_INSERT" mode="IX" />
</owner-list>
<waiter-list>
<waiter id="process11e0a1b88_DELETE" mode="U" requestType="wait" />
</waiter-list>
</pagelock>
<pagelock fileid="4" pageid="25066463" dbid="14" objectname="db01.dbo.employees_prs" id="lock61081f580" mode="U" associatedObjectId="72057601182466048">
<owner-list>
<owner id="process11e0a1b88_DELETE" mode="U" />
</owner-list>
<waiter-list>
<waiter id="process352a02988_INSERT" mode="IX" requestType="wait" />
</waiter-list>
</pagelock>
</resource-list>
我认为 DELETE 语句在一个页面上有一个 U 锁,但也在等待在另一个页面上获取另一个 U 锁。同样,INSERT 语句在一个页面上有一个 IX 锁,并一直等待在另一个页面上获取另一个 IX 锁。 正确吗?
如何解决死锁?
表的结构:
CREATE TABLE [dbo].[employees](
[id] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[employer] [int] NOT NULL,
[name] [varchar](30) NULL,
CONSTRAINT [employees_PK] PRIMARY KEY CLUSTERED
(
[id] ASC
)
)
CREATE TABLE [dbo].[employees_prs](
[employee_id] [int] NOT NULL,
[employee_prs] [tinyint] NOT NULL,
CONSTRAINT [PK_employees_prs] PRIMARY KEY CLUSTERED
(
[employee_id] ASC,
[employee_prs] ASC
)
)
【问题讨论】:
-
您应该做的第一件事是停止使用 NOLOCK 提示乱扔您的数据库。这不是一个神奇的快速按钮。事实上,由于您使用的是删除操作,因此您很可能会损坏您的索引。值得庆幸的是,该提示将在未来被禁止。 blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere 您还应该考虑使用更“最新”的 ANSI-92 样式连接。 sqlblog.com/blogs/aaron_bertrand/archive/2009/10/08/…
-
锁按语句的顺序一次获取一个,直到有足够的锁使表可以安全更改。我不确定,但看起来您应该能够在删除语句中交换表,以便插入和删除都从锁定employees_prs 开始。第一个这样做会阻止另一个继续获取锁,直到完成。
-
这些表之间没有 FK 我是否正确?你应该删除
NOLOCKs。此外,您没有以employee_prs开头的索引。这意味着您的delete语句需要扫描两个表。 -
@Lukos - 你应该这样回答。
-
@IvanStarostin 表之间有一个 FK。我知道 NOLOCK 在 DELETE 中没有意义,但我认为它无助于修复死锁,因为没有 NOLOCK 的执行计划是相同的。
标签: sql-server deadlock