Quais são as minhas opções de bloqueio para uma instrução Merge?

13

Eu tenho um procedimento armazenado que executa uma MERGEinstrução .

Parece que bloqueia a tabela inteira por padrão ao executar a mesclagem.

Estou chamando esse procedimento armazenado dentro de uma transação em que também estou fazendo outras coisas e gostaria que ele apenas bloqueasse as linhas afetadas.

Eu tentei a dica MERGE INTO myTable WITH (READPAST)e parecia travar menos. Mas havia um aviso no ms doc que dizia que ele poderia inserir chaves duplicadas, ignorando até a chave primária.

Aqui está o meu esquema de tabela:

CREATE TABLE StudentDetails
(
StudentID INTEGER PRIMARY KEY,
StudentName VARCHAR(15)
)
GO
INSERT INTO StudentDetails
VALUES(1,'WANG')
INSERT INTO StudentDetails
VALUES(2,'JOHNSON')
GO

CREATE TABLE StudentTotalMarks
(
Id INT IDENTITY PRIMARY KEY,
StudentID INTEGER REFERENCES StudentDetails,
StudentMarks INTEGER
)
GO
INSERT INTO StudentTotalMarks
VALUES(1,230)
INSERT INTO StudentTotalMarks
VALUES(2,255)
GO

Aqui está o meu procedimento armazenado:

CREATE PROCEDURE MergeTest 
    @StudentId int,
    @Mark int
AS  

WITH Params
AS
(
    SELECT @StudentId as StudentId,
        @Mark as Mark
)
    MERGE StudentTotalMarks AS stm
    USING Params p
    ON stm.StudentID = p.StudentId
    WHEN MATCHED AND stm.StudentMarks > 250 THEN DELETE
    WHEN MATCHED THEN UPDATE SET stm.StudentMarks = p.Mark
    WHEN NOT MATCHED THEN
        INSERT(StudentID,StudentMarks)
        VALUES(p.StudentId, p.Mark);
GO

Aqui está como eu estou observando o bloqueio:

begin tran
EXEC MergeTest 1, 1

E então em outra sessão:

EXEC MergeTest 2, 2

A segunda sessão aguarda a conclusão da primeira antes de continuar.

John Buchanan
fonte
1
WITH (READPAST)instrui o SQL Server a ignorar apenas as linhas bloqueadas por outras sessões. Você tem certeza que deseja fazer isso? Além disso, quantas linhas nesta tabela você está modificando? Mostre-nos o esquema da tabela (incluindo índices) e a MERGEinstrução que você está executando.
Nick Chammas
@NickChammas obrigado pela ajuda, atualizei a pergunta com os detalhes. Imagino READPAST seria ser ruim ...
John Buchanan

Respostas:

12

Você precisa dar ao processador de consultas um caminho de acesso mais eficiente para localizar StudentTotalMarksregistros. Conforme escrito, a consulta requer uma varredura completa da tabela com um predicado residual [StudentID] = [@StudentId]aplicado a cada linha:

Plano de digitalização

O mecanismo usa Ubloqueios (de atualização) ao ler como uma defesa básica contra uma causa comum de conflitos de conversão. Esse comportamento significa os segundos blocos de execução ao tentar obter um Ubloqueio na linha já bloqueada com um Xbloqueio (exclusivo) pela primeira execução.

O índice a seguir fornece um melhor caminho de acesso, evitando Ubloqueios desnecessários :

CREATE UNIQUE INDEX uq1 
ON dbo.StudentTotalMarks (StudentID) 
INCLUDE (StudentMarks);

O plano de consulta agora inclui uma operação de busca ativada StudentID = [@StudentId], portanto, os Ubloqueios são solicitados apenas nas linhas de destino:

Buscar Plano

O índice não é exigido para ser UNIQUEpara resolver o problema na mão (embora o INCLUDEé necessário para torná-lo um índice de cobertura para esta consulta).

Fazer StudentIDo PRIMARY KEYda StudentTotalMarksmesa também resolver o problema do caminho de acesso (e aparentemente redundante Idcoluna podia ser removido). Você deve sempre aplicar chaves alternativas com uma restrição UNIQUEou PRIMARY KEY(e evitar adicionar chaves substitutas sem sentido, sem uma boa razão).

Paul White 9
fonte