[编写一些用于连接产品数据库的SQL Server更新查询时,我遇到一种情况,如果同时运行两个具有这些更新的事务,则一切都很好-但是当两个以上的事务试图更新同一个表时同时,其中一些陷入僵局。
我将问题分解为对表定义的“ UNIQUE NONCLUSTERED”约束。当我删除此约束时,所有事务将等待其所需的资源并完成而没有错误。
这是我的示例代码来重现该问题:
CREATE TABLE [dbo].[profiles]
(
[ProfileID] [int] IDENTITY(1,1) NOT NULL,
[ProfileName] [nvarchar](255) NOT NULL,
[GroupFK] [int] NULL
CONSTRAINT [PK_Profile]
PRIMARY KEY CLUSTERED ([ProfileID] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY],
-- !!! [UI_UniqueNameInGroup] seems problematic for me because i get deadlocks when executing more than two transactions at once !!!
CONSTRAINT [UI_UniqueNameInGroup]
UNIQUE NONCLUSTERED ([GroupFK] ASC, [ProfileName] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_99')
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_88')
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_77')
INSERT INTO profiles (ProfileName) VALUES ('PROFILE_66')
这些是我的交易示例:
BEGIN TRAN
UPDATE profiles
SET ProfileName = 'NewProfile_99'
WHERE ProfileID = 4
WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
BEGIN TRAN
UPDATE profiles
SET ProfileName = 'NewProfile_66'
WHERE ProfileID = 1
WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
BEGIN TRAN
UPDATE profiles
SET ProfileName = 'NewProfile_88'
WHERE ProfileID = 3
WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
BEGIN TRAN
UPDATE profiles
SET ProfileName = 'NewProfile_77'
WHERE ProfileID = 2
WAITFOR DELAY '00:00:05.000'
COMMIT TRAN
这是我的死锁图:
[请通过向我解释问题来帮助我-我不明白为什么仅在同时执行两个以上事务时才发生死锁。
我也很高兴为此找到一个可行的解决方案。
我身边是否有解决方案(无需更改数据库方案)?
更改约束[UI_UniqueNameInGroup]以仅允许行锁:
CONSTRAINT [UI_UniqueNameInGroup] UNIQUE NONCLUSTERED ([GroupFK] ASC, [ProfileName] ASC)
WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = OFF
) ON [PRIMARY]
这会引起问题,因为如果您拒绝页和行锁,那么您将强制SQL Server使用表锁,但是其他会话已经放置了表锁。