Eu estava procurando um impasse causado por duas instruções UPDATE simultâneas:
UPDATE [table]
SET [column] = 0
WHERE [unindexed_column] = @id
Meu entendimento é que, como a coluna WHERE
em que se baseia não é indexada, uma verificação completa da tabela é executada. Para cada linha, um bloqueio de atualização é adquirido. Se corresponder à WHERE
cláusula, ele atualizará para um bloqueio exclusivo e o manterá até que a instrução seja concluída.
O impasse ocorre quando a sessão A tem um bloqueio exclusivo na linha 2 e está tentando adquirir um bloqueio de atualização na linha 1, enquanto a sessão B tem um bloqueio exclusivo na linha 1 e está tentando adquirir um bloqueio de atualização na linha 2.
O motivo do impasse faz sentido, mas não entendo exatamente como é realizada a varredura da tabela que torna esse cenário possível. Se as duas consultas executarem a varredura na mesma ordem, parece que o pior cenário é que uma das consultas seja bloqueada ao adquirir um bloqueio de atualização até que a outra consulta seja concluída e libere seus bloqueios.
Como uma varredura de tabela é realizada? A ordem em que as linhas da tabela são verificadas é inconsistente? Se a instrução de atualização falhar em adquirir um bloqueio de atualização, ela irá para a próxima linha e tentará a linha anterior novamente mais tarde? O que, exatamente, torna esse impasse possível?
A ordem da varredura da tabela nunca é garantida. A ordem em que as linhas são bloqueadas também não é garantida. Além disso, o SQL Server tem escalonamento de bloqueio, então você não pode realmente dizer o que o mecanismo decide bloquear, linha, página ou tabela em si.
Assim, o impasse pode ocorrer mesmo se os valores de @id forem diferentes em sessões simultâneas, mas as linhas de interesse residirem nas mesmas páginas. No SQL Server 2008 e superior, você pode adicionar dica
ROWLOCK
que reduz as chances de escalonamento de bloqueio (mas, novamente, não é garantido). SeUPDATE
não conseguir adquirir o bloqueio, ele espera (LOCK_TIMEOUT
especifica quanto tempo vai esperar).