我有一个存储过程,其中包含以下语句
INSERT INTO dbo.[ResultItems] (PGM,GRP,PGMGRPSEQ,ITMID,ITMDESC,BRAND,PackSize,IsSelected,UserId)
SELECT SRIS.PGM,SRIS.GRP,SRIS.PGMGRPSEQ,SRIS.ITMID,SRIS.ITMDESC,SRIS.BRAND,SRIS.PackSize,SRIS.IsSelected,SRIS.UserId
FROM @ItemResult SRIS
LEFT OUTER JOIN [dbo].[ResultItems] SRI (NOLOCK)
ON SRI.ITMID = SRIS.ITMID
AND SRI.PGM = SRIS.PGM
AND SRI.GRP = SRIS.GRP
AND SRI.PGMGRPSEQ = SRIS.PGMGRPSEQ
AND SRI.UserId=SRIS.UserId
WHERE SRI.ITMID IS NULL ----logic to avoid duplicate
GROUP BY SRIS.PGM,SRIS.GRP,SRIS.PGMGRPSEQ,SRIS.ITMID,SRIS.ITMDESC,SRIS.BRAND,SRIS.PackSize,SRIS.IsSelected,SRIS.UserId
UPDATE SRI
SET SRI.IsSelected = 1
FROM @ItemResult IST
INNER JOIN [dbo].ResultItems SRI (NOLOCK)
ON SRI.PGM = IST.[PGM]
AND SRI.GRP = IST.GRP
AND SRI.PGMGRPSEQ = IST.PGMGRPSEQ
AND SRI.ITMID = IST.ITMID
AND SRI.UserId=IST.UserId
WHERE SRI.UserId=@UserId
我在ResultItems表上有index
IF NOT EXISTS(select 1 from sys.sysindexes where name = 'IX_RESULTITEMS_USERID')
BEGIN
CREATE NONCLUSTERED INDEX IX_RESULTITEMS_USERID
ON [dbo].[ResultItems] ([UserId])
END
GO
五个并发用户正在调用此SP。 update语句在UserId上有一个过滤条件。每个用户将仅使用自己的用户ID执行sp。这就是我在UserId列上创建索引的原因。
期望是索引将有助于避免表扫描,并且不会出现死锁(因为每个用户都在寻找自己的记录)......但是十分之一的测试导致死锁。
我认为这是因为当存在大量数据时(每个用户超过20000条记录)升级到table scan
。
这里避免死锁的最佳方法是什么?
TABLE和INDEXES
CREATE TABLE [dbo].[ResultItems](
[SRIID] [int] IDENTITY(1,1) NOT NULL,
[PGM] [nvarchar](50) NULL,
[GRP] [nvarchar](50) NULL,
[PGMGRPSEQ] [nvarchar](50) NULL,
[ITMID] [nvarchar](18) NULL,
[ITMDESC] [nvarchar](255) NULL,
[BRAND] [nchar](40) NULL,
[PackSize] [nvarchar](max) NULL,
[IsSelected] [bit] NULL,
[UserId] [nvarchar](50) NULL,
CONSTRAINT [PK_SEARCH_RESULT_ITEMS] PRIMARY KEY CLUSTERED
(
[SRIID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_PGM_GRP_PGMGRPSEQ_ITMID_UserId] ON [dbo].[ResultItems]
(
[PGM] ASC,
[GRP] ASC,
[PGMGRPSEQ] ASC,
[ITMID] ASC,
[UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
CREATE NONCLUSTERED INDEX [IX_RESULTITEMS_USERID] ON [dbo].[ResultItems]
(
[UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
参考
答案 0 :(得分:1)
使用MERGE而不是INSERT然后更新。另外,摆脱NOLOCK
MERGE INTO dbo.[ResultItems] SRI
USING @ItemResult SRIS ON (
SRI.ITMID = SRIS.ITMID
AND SRI.PGM = SRIS.PGM
AND SRI.GRP = SRIS.GRP
AND SRI.PGMGRPSEQ = SRIS.PGMGRPSEQ
AND SRI.UserId=SRIS.UserId
)
WHEN MATCHED THEN UPDATE
SET SRI.IsSelected = 1
WHEN NOT MATCHED THEN
INSERT ( PGM, GRP, PGMGRPSEQ, ITMID, ITMDESC, BRAND, PackSize, IsSelected, UserId)
VALUES (SRIS.PGM,SRIS.GRP,SRIS.PGMGRPSEQ,SRIS.ITMID,SRIS.ITMDESC,SRIS.BRAND,SRIS.PackSize,SRIS.IsSelected,SRIS.UserId);
答案 1 :(得分:1)
我通过重写存储过程逻辑来解决该问题,以避免更新语句。更新发生在插入前的temporary table
。
其他想法是使用Dead-lock graph
进行分析并使用MERGE
进行查询。
其他参考
合并BUGS