Impasse de atualização do índice do SQL Server

13

Eu tenho 2 consultas que, quando executadas ao mesmo tempo, estão causando um impasse.

Consulta 1 - atualize uma coluna incluída em um índice (índice1):

update table1 set column1 = value1 where id = @Id

Pega o X-Lock na tabela1 e tenta um X-Lock no índice1.

Consulta 2:

select columnx, columny, etc from table1 where {some condition}

Pega um S-Lock no índice1 e tenta um S-Lock na tabela1.

Existe uma maneira de evitar o impasse, mantendo as mesmas consultas? Por exemplo, posso de alguma forma usar um X-Lock no índice na transação de atualização antes da atualização para garantir que o acesso à tabela e ao índice esteja na mesma ordem - o que deve impedir o conflito?

O nível de isolamento é Leitura confirmada. Bloqueios de linha e página são ativados para os índices. É possível que o mesmo registro esteja participando das duas consultas - não posso dizer pelo gráfico de deadlock, pois ele não mostra os parâmetros.

Gráfico de deadlock

Dale K
fonte

Respostas:

11

Existe uma maneira de evitar o impasse, mantendo as mesmas consultas?

O gráfico de deadlock mostra que esse deadlock específico era um deadlock de conversão associado a uma pesquisa de marcador (neste caso, uma pesquisa RID):

Gráfico de deadlock

Como observa a pergunta, o risco geral de conflito surge porque as consultas podem obter bloqueios incompatíveis nos mesmos recursos em ordens diferentes. A SELECTconsulta precisa acessar o índice antes da tabela devido à pesquisa do RID, enquanto a UPDATEconsulta modifica a tabela primeiro e depois o índice.

Eliminar o impasse requer a remoção de um dos ingredientes do impasse. A seguir, são apresentadas as principais opções:

  1. Evite a pesquisa do RID, fazendo a cobertura do índice não clusterizado. Provavelmente, isso não é prático no seu caso, porque a SELECTconsulta retorna 26 colunas.
  2. Evite a pesquisa do RID, criando um índice em cluster. Isso envolveria a criação de um índice em cluster na coluna Proposal. Vale a pena considerar, embora pareça que essa coluna seja do tipo uniqueidentifier, que pode ou não ser uma boa opção para um índice em cluster, dependendo de problemas mais amplos.
  3. Evite usar bloqueios compartilhados ao ler, ativando as opções READ_COMMITTED_SNAPSHOTou do SNAPSHOTbanco de dados. Isso exigiria testes cuidadosos, especialmente com relação a comportamentos de bloqueio projetados. O código de acionamento também exigiria testes para garantir que a lógica funcione corretamente.
  4. Evite usar bloqueios compartilhados ao ler usando o READ UNCOMMITTEDnível de isolamento da SELECTconsulta. Todas as advertências usuais se aplicam.
  5. Evite a execução simultânea das duas consultas em questão usando um bloqueio de aplicativo exclusivo (consulte sp_getapplock ).
  6. Use as dicas de bloqueio de tabela para evitar simultaneidade. Este é um martelo maior que a opção 5, pois pode afetar outras consultas, não apenas as duas identificadas na pergunta.

De alguma forma, posso usar um X-Lock no índice na transação de atualização antes da atualização para garantir que a tabela e o acesso ao índice estejam na mesma ordem

Você pode tentar isso, envolvendo a atualização em uma transação explícita e executando uma SELECTcom uma XLOCKdica sobre o valor do índice não clusterizado antes da atualização. Isso depende de você saber com certeza qual é o valor atual no índice não clusterizado, acertar o plano de execução e antecipar corretamente todos os efeitos colaterais de usar esse bloqueio extra. Ele também conta com o mecanismo de bloqueio que não é inteligente o suficiente para evitar o bloqueio, se for considerado redundante .

Em resumo, embora isso seja viável em princípio, eu não o recomendo. É muito fácil perder algo ou ser mais esperto do que isso de maneiras criativas. Se você realmente deve evitar esses impasses (em vez de apenas detectá-los e tentar novamente), recomendamos que você procure as soluções mais gerais listadas acima.

Paul White 9
fonte
Olhando mais para o assunto, acho que deixá-lo inalterado é provavelmente o melhor. É um problema mais comum que eu percebi originalmente.
Dale K
1

Eu tenho um problema semelhante que ocorre ocasionalmente e aqui está a abordagem adotada.

  1. Adicione set deadlock priority low;à seleção. Isso fará com que essa consulta seja a vítima de deadlock quando ocorrer um deadlock.
  2. Configure a lógica de nova tentativa no seu aplicativo para tentar novamente a seleção automaticamente se falhar devido a um conflito (ou tempo limite), após aguardar / adormecer por um breve período de tempo para permitir que as consultas de bloqueio sejam concluídas.

Nota: se você selectfaz parte de uma transação explícita com várias instruções, é necessário tentar novamente toda a transação, e não apenas a declaração que falhou, ou pode obter resultados inesperados. Se este é um único select, então você está bem, mas se é uma declaração xde ndentro de uma transação, em seguida, apenas certifique-se de repetir todas as ndeclarações durante a nova tentativa.

BateTech
fonte
Obrigado - as consultas são automaticamente vítimas de conflito por padrão. E sim, já temos um mecanismo robusto de novas tentativas em vigor.
Dale K