每行带有图像数据库 base64 的表导致死锁/块

use*_*567 1 sql-server sql-server-2014

我们有一张桌子,

CREATE TABLE [dbo].[MyTable](
    [MasterKey] [uniqueidentifier] NOT NULL,
    [DetailKey] [varchar](100) NULL,
    [JSON] [nvarchar](max) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

CREATE NONCLUSTERED INDEX [ix_MyTable_details] ON [dbo].[MyTable]
(
    [MasterKey] 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
Run Code Online (Sandbox Code Playgroud)

我们有以下查询导致太多死锁/块。

IF EXISTS(SELECT 1 from  [MyTable](nolock) where  [MasterKey]= @MasterKey AND [DetailKey] =  @DetailKey)
BEGIN

 UPDATE [MyTable]
           SET [JSON] = @JSON
    WHERE  [MasterKey]= @MasterKey AND [DetailKey] =  @DetailKey
END
ELSE
BEGIN
INSERT INTO [MyTable]
           ([MasterKey]
           ,[DetailKey]
           ,[JSON])
     VALUES
           (@MasterKey
           ,@DetailKey
           ,@JSON)
END
Run Code Online (Sandbox Code Playgroud)

请注意,不同的用户同时使用 MasterKey 密钥,但在窥视时间我们看到应用程序变得无响应。当我们看到日志时,我们发现了很多块/死锁。我们还可以在此表中看到 Sch-S 锁。

添加 MasterKey 和 DetailKey 作为主键可以解决这个问题还是我们能做什么?

请注意,有时 JSON 大小很大,因为它包含 base64 格式的图像。

Aar*_*and 7

通常,您应该始终更改此方法:

IF EXISTS ( /* scan the table with a where clause */ )
    UPDATE ( /* scan the table AGAIN with a where clause */ )
ELSE
    INSERT
Run Code Online (Sandbox Code Playgroud)

对此:

UPDATE ( /* scan the table JUST ONCE with a where clause */ )
IF @@ROWCOUNT = 0
  INSERT
Run Code Online (Sandbox Code Playgroud)

忘记你曾经学过第一种方法。这就像去杂货店检查他们是否有鸡蛋,然后回家拿钱包,然后再回到杂货店买鸡蛋。

此外,是的,聚集索引在许多情况下都可以提供帮助。是MasterKey真正的关键(例如是不是唯一的)?或者是MasterKey+DetailKey候选键的组合?在后一种情况下,以列作为键列的索引(是否聚集)将有助于减少查询在找到具有该MasterKey值的行(这会延长阻塞并可能导致死锁取决于其他因素)。


实际用例。首先,更改索引:

DROP INDEX [ix_MyTable_details] ON [dbo].[MyTable];

CREATE UNIQUE CLUSTERED INDEX cix_MyTable_details
  ON dbo.MyTable(MasterKey, DetailKey);
Run Code Online (Sandbox Code Playgroud)

然后将查询更改为:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;

UPDATE dbo.[MyTable]
  SET [JSON] = @JSON
  WHERE MasterKey = @MasterKey AND DetailKey = @DetailKey;

IF @@ROWCOUNT = 0
BEGIN
  INSERT dbo.[MyTable]
       ([MasterKey]
       ,[DetailKey]
       ,[JSON])
  VALUES
       (@MasterKey
       ,@DetailKey
       ,@JSON);
END

COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)