我的客户越来越这两个语句之间的经常性僵局:SQL服务器Pagelock死锁 - 插入和删除
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锁。 这是正确的吗?
我该如何解决死锁问题?
Table's结构:
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提示丢弃你的数据库。这不是一个快速按钮。事实上,由于您正在使用删除操作,您可能会损坏索引。谢天谢地,这个提示将来会被禁止。 http://blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere/你也应该考虑使用更多的“当前”ANSI-92风格的连接。 http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/08/bad-habits-to-kick-using-old-style-joins.aspx –
按顺序一次获取锁的声明,直到足够的锁使表可以安全地更改。我不确定,但看起来应该可以在delete语句中交换表,以便插入和删除两者都从锁定employees_prs开始。第一个这样做会阻止另一个持续锁定收购,直到收购完成。 – Lukos
我确定这些表格之间没有FK吗?你应该删除'NOLOCK's。另外你没有以'employee_prs'开头的索引。这意味着你的'delete'语句需要扫描两个表。 –