Confuso sobre UPDLOCK, HOLDLOCK

88

Ao pesquisar o uso de dicas de tabela , me deparei com estas duas perguntas:

As respostas a ambas as perguntas dizem que, ao usar (UPDLOCK, HOLDLOCK), outros processos não serão capazes de ler os dados daquela tabela, mas eu não vi isso. Para testar, criei uma tabela e iniciei duas janelas SSMS. Na primeira janela, executei uma transação que selecionei na tabela usando várias dicas de tabela. Enquanto a transação estava sendo executada, na segunda janela, executei várias instruções para ver quais seriam bloqueadas.

A mesa de teste:

CREATE TABLE [dbo].[Test](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [Value] [nvarchar](50) NULL,
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Da janela 1 do SSMS:

BEGIN TRANSACTION

SELECT * FROM dbo.Test WITH (UPDLOCK, HOLDLOCK)
WAITFOR DELAY '00:00:10'

COMMIT TRANSACTION

Na janela 2 do SSMS (executou um dos seguintes):

SELECT * FROM dbo.Test
INSERT dbo.Test(Value) VALUES ('bar')
UPDATE dbo.Test SET Value = 'baz' WHERE Value = 'bar'
DELETE dbo.Test WHERE Value= 'baz'

Efeito de diferentes dicas de tabela nas instruções executadas na janela 2:

           (UPDLOCK)       (HOLDLOCK)    (UPDLOCK, HOLDLOCK)    (TABLOCKX)
---------------------------------------------------------------------------
SELECT    not blocked      not blocked       not blocked         blocked
INSERT    not blocked        blocked           blocked           blocked
UPDATE      blocked          blocked           blocked           blocked
DELETE      blocked          blocked           blocked           blocked

Eu entendi mal as respostas dadas nessas perguntas ou cometi um erro em meus testes? Se não, por que você usaria (UPDLOCK, HOLDLOCK)vs. (HOLDLOCK)sozinho?


Mais explicações sobre o que estou tentando realizar:

Gostaria de selecionar linhas de uma tabela e evitar que os dados dessa tabela sejam modificados enquanto estou processando-os. Não estou modificando esses dados e gostaria de permitir que ocorram leituras.

Esta resposta diz claramente que (UPDLOCK, HOLDLOCK)bloqueará leituras (não o que eu quero). Os comentários a esta resposta implicam que é isso HOLDLOCKque impede leituras. Para tentar entender melhor os efeitos das dicas de tabela e ver se UPDLOCKsozinho faria o que eu queria, fiz o experimento acima e obtive resultados que contradizem essas respostas.

Atualmente, acredito que (HOLDLOCK)seja isso que devo usar, mas estou preocupado que possa ter cometido um erro ou esquecido algo que voltará a me morder no futuro, daí esta questão.

Jeff Ogata
fonte

Respostas:

102

Por que o UPDLOCK selecionaria o bloco? A Lock Compatibility Matrix mostra claramente Npara a contenção S / U e U / S, como em No Conflict .

Quanto à dica HOLDLOCK, os estados da documentação:

HOLDLOCK: É equivalente a SERIALIZABLE. Para obter mais informações, consulte SERIALIZABLE posteriormente neste tópico.

...

SERIALIZABLE: ... A varredura é realizada com a mesma semântica de uma transação em execução no nível de isolamento SERIALIZABLE ...

e o tópico Nível de isolamento da transação explica o que significa SERIALIZABLE:

Nenhuma outra transação pode modificar os dados que foram lidos pela transação atual até que a transação atual seja concluída.

Outras transações não podem inserir novas linhas com valores de chave que cairiam no intervalo de chaves lidas por quaisquer instruções na transação atual até que a transação atual seja concluída.

Portanto, o comportamento que você vê é perfeitamente explicado pela documentação do produto:

  • UPDLOCK não bloqueia SELECT nem INSERT simultâneos, mas bloqueia qualquer UPDATE ou DELETE das linhas selecionadas por T1
  • HOLDLOCK significa SERALIZABLE e portanto permite SELECTS, mas bloqueia UPDATE e DELETES das linhas selecionadas por T1, bem como qualquer INSERT no intervalo selecionado por T1 (que é a tabela inteira, portanto qualquer insert).
  • (UPDLOCK, HOLDLOCK): seu experimento não mostra o que bloquearia além do caso acima, ou seja, outra transação com UPDLOCK em T2 :
    SELECT * FROM dbo.Test WITH (UPDLOCK) WHERE ...
  • TABLOCKX sem necessidade de explicações

A verdadeira questão é o que você está tentando alcançar ? Jogar com dicas de bloqueio sem um entendimento absoluto de 110% da semântica de bloqueio está implorando por problemas ...

Após a edição OP:

Gostaria de selecionar linhas de uma tabela e evitar que os dados dessa tabela sejam modificados enquanto estou processando-os.

Você deve usar um dos níveis de isolamento de transação mais altos. READ REPEATABLE impedirá que os dados lidos sejam modificados. SERIALIZABLE impedirá que os dados lidos sejam modificados e que novos dados sejam inseridos. Usar níveis de isolamento de transação é a abordagem certa, ao contrário de usar dicas de consulta. Kendra Little tem um belo pôster explicando os níveis de isolamento .

Remus Rusanu
fonte
+1, e obrigado pela resposta detalhada. Vou atualizar minha pergunta para adicionar os detalhes de qual é o meu objetivo.
Jeff Ogata,
1
@Remus Rusanu você poderia explicar por que a abordagem certa é usar níveis de isolamento em vez de usar dicas de consulta? Eu tenho um procedimento onde eu só preciso bloquear duas tabelas de serem modificadas e estou usando TABLOCK, HOLDLOCK, devo realmente mudar para o nível de isolamento e bloquear todas as tabelas em minha transação?
Steve
Gostaria de uma explicação para TABLOCKX :)
niico
Observação: o link para a entrada do blog de Kendra Little retorna um 404. Não consigo encontrar nenhuma entrada datada de 2 de fevereiro de 2011, como o link sugere.
Bacon Bits de
21

UPDLOCK é usado quando você deseja bloquear uma linha ou linhas durante uma instrução de seleção para uma instrução de atualização futura. A atualização futura pode ser a próxima instrução da transação.

Outras sessões ainda podem ver os dados. Eles simplesmente não podem obter bloqueios incompatíveis com o UPDLOCK e / ou HOLDLOCK.

Você usa UPDLOCK quando deseja impedir que outras sessões alterem as linhas que você bloqueou. Isso restringe sua capacidade de atualizar ou excluir linhas bloqueadas.

Use HOLDLOCK quando quiser evitar que outras sessões alterem qualquer um dos dados que você está olhando. Isso restringe a capacidade de inserir, atualizar ou excluir as linhas que você bloqueou. Isso permite que você execute a consulta novamente e veja os mesmos resultados.

Scott Bruns
fonte
1
Obrigado, mas acho que você realmente não respondeu à minha pergunta: as respostas a essas perguntas estavam erradas ao declarar que o (UPDLOCK,HOLDLOCK)bloco era lido e há uma razão para usar em (UPDLOCK,HOLDLOCK)vez de apenas (HOLDLOCK)?
Jeff Ogata,
Minha segunda declaração responde à sua pergunta, eles estão errados. Outras sessões ainda podem ler os dados.
Scott Bruns,
Updlock, Holdlock não é o mesmo que holdlock. Updlock, holdlock bloqueia as linhas para atualização e serializa sua transação. Holdlock por si só serializa sua transação. Não bloqueia as linhas selecionadas para acesso posterior.
Scott Bruns,
"UPDLOCK é usado quando você deseja bloquear uma linha ou linhas durante uma instrução de seleção para uma instrução de atualização futura." Eu amo isso, já que XLOCK pode não funcionar em algum momento
Yiping