Configurar
Tenho uma tabela simples:
CREATE TABLE [dbo].[StringData](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[DCStringID] [bigint] NOT NULL,
[TimeStamp] [datetime] NOT NULL,
[Hour] AS (dateadd(hour,datediff(hour,(0),[TimeStamp]),(0))) PERSISTED,
[Date] AS (CONVERT([date],[TimeStamp],(0))) PERSISTED,
[DCVoltage] [decimal](18, 2) NULL,
[DCCurrent] [decimal](18, 2) NULL,
[DCPower] AS (([DCVoltage]*[DCCurrent])/(1000)) PERSISTED,
[IsCompressed] [bit] NULL,
[TimeStamp15Minutes] AS (dateadd(minute,(datediff(minute,(0),[TimeStamp])/(15))*(15),(0))),
CONSTRAINT [PK_StringData1] PRIMARY KEY CLUSTERED
(
[TimeStamp] DESC,
[DCStringID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
E eu tenho um procedimento upsert chamado [InsertOrUpdateStringData]
ALTER PROCEDURE [dbo].[InsertOrUpdateStringData]
@DCStringID bigint, @TimeStamp datetime, @DCVoltage decimal(18,2), @DCCurrent decimal(18,2)
AS
/****** avoid parameter spoofing ******/
DECLARE @DCStringID_Value AS bigint; SET @DCStringID_Value = @DCStringID;
DECLARE @TimeStamp_Value AS datetime; SET @TimeStamp_Value = @TimeStamp
DECLARE @DCVoltage_Value AS decimal(18,2); SET @DCVoltage_Value = @DCVoltage
DECLARE @DCCurrent_Value AS decimal(18,2); SET @DCCurrent_Value = @DCCurrent
/************/
MERGE [StringData] AS TARGET
USING (VALUES (@DCStringID_Value, @TimeStamp_Value))
AS SOURCE ([DCStringID], [TimeStamp])
ON TARGET.[DCStringID] = SOURCE.[DCStringID] AND TARGET.[TimeStamp] = SOURCE.[TimeStamp]
WHEN MATCHED THEN
UPDATE
SET [DCVoltage] = @DCVoltage_Value,
[DCCurrent] = @DCCurrent_Value
WHEN NOT MATCHED THEN
INSERT ([DCStringID], [TimeStamp], [DCVoltage], [DCCurrent])
VALUES (@DCStringID_Value, @TimeStamp_Value, @DCVoltage_Value, @DCCurrent_Value);
Usando
Muitos encadeamentos de aplicativos diferentes estão usando o procedimento [InsertOrUpdateStringData] para inserir dados na tabela síncrona.
Os índices da tabela são bem usados e uma única execução é bastante rápida e leva cerca de 31ms.
Problema
Se outra operação de tabela não selecionada for executada (como inserção em massa), eles serão bloqueados por um tempo muito longo.
Infelizmente [sp_WhoIsActive] não está me mostrando se as chamadas de procedimento [InsertOrUpdateStringData] também estão bloqueando umas às outras. Mas parece que há um encadeamento de bloqueio porque é a única explicação para bloquear mais de 10.000 ms, como no meu exemplo.
Pergunta
Parece que meu procedimento upsert está bloqueando a tabela completa e retardando outras operações de inserção.
Há algo que eu possa fazer para otimizar minhas consultas para evitar bloqueios de longa data?
Atualização 1 - relacionada a Nic answere
O bloqueio ocorre em todas as operações não selecionadas, pois escrevi que a inserção em massa é apenas uma amostra.
Veja aqui uma chamada de mesclagem simples de bloqueio de 10 segundos (a sessão de bloqueio 176 é uma chamada [InsertOrUpdateStringData] ):
Eu também tentei @get_locks=1 aqui está o resultado. Talvez te ajude com mais detalhes.
Atualização 2 - relacionada ao Nic answere
Aqui está um exemplo:
sid 52 é bloqueado por sid 119 por mais de 40 segundos!? Mas o sid 119 parece não estar bloqueado. Eu realmente não entendo isso.
Como seu spid 119 está no estado [dormindo], recomendo que você verifique:
Além disso, talvez você possa ativar READ_COMMITTED_SNAPSHOT em seu banco de dados para evitar o bloqueio entre a operação de leitura e gravação.