Como parte da série de outono do Database Training Days da Quest, Brent Ozar, Microsoft Certified Master, apresentou um tutorial sobre "Como evitar impasses com o ajuste de consulta". O programa se concentrou nos três problemas de simultaneidade que ocorrem no SQL Server, três maneiras de corrigi-los e uma maneira que parece corrigi-los, mas na verdade não funciona.
Problemas de simultaneidade:bloqueio, bloqueio e impasses no SQL Server
O que são problemas de simultaneidade? Eles acontecem quando as consultas tentam evitar conflitos entre si sobre objetos de banco de dados, como tabelas. Eles estão:
- Bloqueio – As consultas fazem isso o tempo todo para evitar que outras consultas usem uma tabela ao mesmo tempo. Esta é uma operação normal de banco de dados.
- Bloqueando – Isso ocorre quando uma consulta tem um bloqueio normal, mas outra consulta tenta adquirir o mesmo bloqueio. A segunda consulta deve aguardar o tempo necessário para que a primeira consulta libere o bloqueio. Dependendo da natureza da primeira consulta, a segunda pode estar esperando muito pouco ou muito tempo. São essas longas esperas que realmente afetam o desempenho.
- Bloqueio – Deadlocks ocorrem quando uma consulta obtém um bloqueio, outra consulta obtém um bloqueio diferente e, em seguida, cada uma deseja adquirir o bloqueio da outra. O SQL Server resolve isso designando uma das consultas como vítima e eliminando-a para quebrar o impasse. Mesmo que uma das consultas possa prosseguir, isso também afeta o desempenho.
Corrigindo problemas de simultaneidade
Independentemente de você estar enfrentando bloqueios ou impasses no SQL Server, existem maneiras de corrigir problemas de simultaneidade. Brent apresentou esses três métodos e passou a maior parte do restante da sessão focando no segundo – corrigir código incorreto.
- Tenha índices suficientes para agilizar suas consultas, mas não tantos a ponto de tornar as consultas mais lentas, fazendo com que as consultas mantenham mais bloqueios por mais tempo
- Ajuste seu código transacional para que as consultas funcionem nas tabelas sempre na mesma ordem previsível
- Use o nível de isolamento certo para as necessidades do seu aplicativo
Ao entrar na parte prática do programa, Brent comentou sobre o uso de instruções NOLOCK para bloqueio e impasse. Ele alertou que o NOLOCK realmente não corrige esses problemas porque depende de “leituras sujas” – essencialmente, ignora os bloqueios de linha de outras consultas.
Em sua demonstração disso usando o banco de dados Stack Overflow, ele criou uma consulta simples que procurava e contava pessoas chamadas “Alex”. Em seguida, ele criou outra consulta que executaria uma atualização sobre pessoas que não chamado Alex—sem inserções ou exclusões de registros. Uma consulta não deve ter nada a ver com a outra. Mas, executá-los juntos leva a resultados diferentes no número de pessoas chamadas Alex. Isso ocorre porque o NOLOCK permite que você veja dados que não foram confirmados, levando a resultados aleatórios que você não pode prever. Isso só acontece em concorrência.
Claramente, é necessária uma correção melhor para bloquear e travar no SQL Server que não leve a resultados aleatórios e imprevisíveis.
Uma solução melhor para impasses SQL
Brent então demonstrou como corrigir um impasse alterando o código que o causa. Sua primeira demo mostrou uma situação simples envolvendo duas mesas para que o público pudesse ver um impasse em câmera lenta enquanto acontecia. Como o SQL Server procura por deadlocks a cada 5 segundos e mata a consulta mais fácil de reverter, conseguimos ver a vítima de deadlock emergir.
Em uma situação simples, o conselho mais geral se aplica, que é tocar as tabelas na mesma ordem todas as vezes ao construir consultas. Isso geralmente evitará que as consultas entrem em deadlock.
E quanto a consultas mais complexas? Para esse cenário, Brent usou uma situação mais realista que poderia surgir facilmente no Stack Overflow, onde duas pessoas estão votando positivamente nas perguntas uma da outra. Como os mesmos usuários estão envolvidos em ambas as transações, isso causa um deadlock.
Aqui, não é suficiente trabalhar em cada mesa na mesma ordem todas as vezes, mas também é necessário minimizar o número de vezes que cada mesa é tocada. Como Brent explicou, a correção pode envolver algum código feio que faz com que as consultas sejam bloqueadas, mas pelo menos não um impasse. Nesse caso, um bloco de curta duração que permite que ambas as consultas sejam executadas até a conclusão é melhor do que um deadlock que encerra uma delas.
Ninguém quer alterar o código em centenas de consultas, portanto, concentre-se nas que estão constantemente em deadlock, remova todas as linhas desnecessárias da transação e não tenha medo de introduzir um bloco para evitar um deadlock.