Os profissionais de banco de dados são rotineiramente confrontados com problemas de desempenho do banco de dados, como indexação imprópria e código mal escrito em instâncias SQL de produção. Suponha que você atualizou uma transação e o SQL Server relatou a seguinte mensagem de deadlock. Para DBAs que estão começando, isso pode ser um choque.
Neste artigo, exploraremos os deadlocks do SQL Server e as melhores maneiras de evitá-los.
O que é um impasse do SQL Server?
O SQL Server é um banco de dados altamente transacional. Por exemplo, suponha que você esteja dando suporte ao banco de dados de um portal de compras online no qual recebe novos pedidos de clientes 24 horas por dia. Vários usuários provavelmente estão realizando a mesma atividade ao mesmo tempo. Nesse caso, seu banco de dados deve seguir as propriedades de Atomicidade, Consistência, Isolamento, Durabilidade (ACID) para ser consistente, confiável e proteger a integridade dos dados.
A imagem abaixo descreve as propriedades ACID em um banco de dados relacional.
Para seguir as propriedades ACID, o SQL Server usa mecanismos de bloqueio, restrições e log de gravação antecipada. Vários tipos de bloqueio incluem:bloqueio exclusivo (X), bloqueio compartilhado (S), bloqueio de atualização (U), bloqueio de intenção (I), bloqueio de esquema (SCH) e bloqueio de atualização em massa (BU). Esses bloqueios podem ser adquiridos no nível de chave, tabela, linha, página e banco de dados.
Suponha que você tenha dois usuários, John e Peter, conectados ao banco de dados do cliente.
- João quer atualizar os registros do cliente com [customerid] 1.
- Ao mesmo tempo, Peter quer recuperar o valor para o cliente que tem [customerid] 1.
Nesse caso, o SQL Server usa os seguintes bloqueios para John e Peter.
Fechaduras para John
- É necessário um bloqueio de intenção exclusiva (IX) na tabela do cliente e na página que contém o registro.
- Além disso, é necessário um bloqueio exclusivo (X) na linha que John deseja atualizar. Ele impede que qualquer outro usuário modifique os dados da linha até que o processo A libere seu bloqueio.
Fechaduras para Peter
- Ele adquire um bloqueio de intenção compartilhada (IS) na tabela do cliente e na página que contém o registro de acordo com a cláusula where.
- Ele tenta usar um bloqueio compartilhado para ler a linha. Esta linha já tem um bloqueio exclusivo para John.
Nesse caso, Peter precisa esperar até que John termine seu trabalho e libere a fechadura exclusiva. Essa situação é conhecida como bloqueio.
Agora, suponha que em outro cenário, John e Peter tenham os seguintes bloqueios.
- João tem um bloqueio exclusivo na tabela de clientes para o ID de cliente 1.
- Peter tem um bloqueio exclusivo na tabela de pedidos para o ID de cliente 1.
- John requer um bloqueio exclusivo na tabela de pedidos para concluir sua transação. Peter já tem um cadeado exclusivo na tabela de pedidos.
- Peter exige um bloqueio exclusivo na mesa do cliente para concluir sua transação. John já tem um cadeado exclusivo na mesa do cliente.
Nesse caso, nenhuma das transações pode prosseguir porque cada transação requer um recurso mantido pela outra transação. Essa situação é conhecida como deadlock do SQL Server.
Mecanismos de monitoramento de deadlock do SQL Server
O SQL Server monitora periodicamente situações de deadlock usando o thread de monitor de deadlock. Isso verifica os processos envolvidos em um deadlock e identifica se uma sessão se tornou uma vítima de deadlock. Ele usa um mecanismo interno para identificar o processo da vítima de deadlock. Por padrão, a transação com a menor quantidade de recursos necessários para a reversão é considerada vítima.
O SQL Server elimina a sessão da vítima para que outra sessão possa adquirir o bloqueio necessário para concluir sua transação. Por padrão, o SQL Server verifica a situação de deadlock a cada 5 segundos usando o monitor de deadlock. Se ele detectar um deadlock, poderá reduzir a frequência de 5 segundos para 100 milissegundos, dependendo da ocorrência do deadlock. Ele redefine novamente o encadeamento de monitoramento para 5 segundos se não ocorrerem deadlocks frequentes.
Assim que o SQL Server matar um processo como vítima de deadlock, você receberá a seguinte mensagem. Nesta sessão, o processo ID 69 foi uma vítima de deadlock.
Os impactos do uso de instruções de prioridade de deadlock do SQL Server
Por padrão, o SQL Server marca a transação com a reversão mais barata como vítima de deadlock. Os usuários podem definir a prioridade de deadlock em uma transação usando a instrução DEADLOCK_PRIORITY.
SET DEADLOCK_PRIORITY
Ele usa os seguintes argumentos:
- Baixo:é equivalente à prioridade de impasse -5
- Normal:é a prioridade de impasse padrão 0
- Alta:é a prioridade de impasse 5 mais alta.
Também podemos definir valores numéricos para a prioridade de deadlock de -10 a 10 (total de 21 valores).
Vejamos alguns exemplos de declarações de prioridade de deadlock.
Exemplo 1:
Sessão 1 com prioridade de deadlock:Normal (0)> Sessão 2 com prioridade de deadlock:Baixa (-5)
Vítima de impasse: Sessão 2
Exemplo 2:
Sessão 1 com prioridade de deadlock:Normal (0)
Vítima de impasse: Sessão 1
Exemplo 3
Sessão 1 com prioridade de deadlock:-3> Sessão 2 com prioridade de deadlock:-7
Exemplo 4:
Sessão 1 com prioridade de deadlock:-5
Vítima de impasse: Sessão 1
Deadlocks do SQL Server usando gráficos de deadlock
Um gráfico de deadlock é uma representação visual dos processos de deadlock, seus locks e a vítima do deadlock. Podemos habilitar os sinalizadores de rastreamento 1204 e 1222 para capturar informações detalhadas de deadlock em um formato XML e gráfico. Podemos usar o evento estendido system_health padrão para obter os detalhes do impasse. Uma maneira rápida e fácil de interpretar o deadlock é por meio de um gráfico de deadlock. Vamos simular uma condição de deadlock e visualizar seu gráfico de deadlock correspondente.
Para esta demonstração, criamos a tabela Clientes e Pedidos e inserimos alguns registros de amostra.
CREATE TABLE Customer
(ID INT IDENTITY(1,1), CustomerName VARCHAR(20))
GO
CREATE TABLE Orders
(OrderID INT IDENTITY(1,1), ProductName VARCHAR(50))
GO
INSERT INTO Customer(CustomerName) VALUES ('Rajendra')
Go 100
S INSERT INTO Orders(ProductName) VALUES ('Laptop')
Go 100
Em seguida, abrimos uma nova janela de consulta e ativamos o sinalizador de rastreamento globalmente.
Rastreamento DBCC(1222,-1)
Assim que habilitamos o sinalizador de rastreamento de deadlock, iniciamos duas sessões e executamos a consulta na ordem abaixo:
- A primeira sessão inicia uma transação para atualizar a tabela de clientes para o ID de cliente 1.
- A segunda sessão inicia uma transação para atualizar a tabela de pedidos para o ID de pedido 10.
- A primeira sessão tenta atualizar a tabela de pedidos para o mesmo ID de pedido 10. A segunda sessão já bloqueia esta linha. A sessão 1 está bloqueada devido aos bloqueios mantidos pela sessão 2.
- Agora, para a sessão 2, queremos atualizar a tabela de clientes para o ID de cliente 1. Isso gera uma situação de impasse em que as sessões ID 63 e ID 65 não podem progredir.
Neste exemplo, o SQL Server escolhe uma vítima de deadlock (ID de sessão 65) e elimina a transação. Vamos buscar o gráfico de deadlock da sessão de evento estendido system_health.
SELECT XEvent.query('(event/data/value/deadlock)[1]') AS DeadlockGraph
FROM (
SELECT XEvent.query('.') AS XEvent
FROM (
SELECT CAST(target_data AS XML) AS TargetData
FROM sys.dm_xe_session_targets st
INNER JOIN sys.dm_xe_sessions s
ON s.address = st.event_session_address
WHERE s.NAME = ‘system_health’
AND st.target_name = ‘ring_buffer’
) AS Data
CROSS APPLY TargetData.nodes('RingBufferTarget/event[@name="xml_deadlock_report"]
') AS XEventData(XEvent)
) AS source;
Essa consulta nos fornece um XML de deadlock que requer um DBA experiente para interpretar as informações.
Salvamos esse XML de deadlock usando a extensão .XDL e quando abrimos o arquivo XDL no SSMS, obtemos o gráfico de deadlock mostrado abaixo.
Este gráfico de deadlock fornece as seguintes informações:
- Nós de processo: Na forma oval, você obtém informações relacionadas ao processo.
- Nós de recursos: Os nós de recursos (quadrado) fornecem informações sobre os objetos envolvidos nas transações junto com os bloqueios. Neste exemplo, ele mostra bloqueios RID porque não temos índices para ambas as tabelas.
- Bordas: Uma borda conecta o nó do processo e o nó do recurso. Ele mostra o proprietário do recurso e o modo de bloqueio de solicitação.
Ele representa uma vítima de deadlock riscando a oval no gráfico de deadlock.
Você pode capturar informações de deadlock do SQL Server das seguintes maneiras:
- Perfil do SQL Server
- Eventos estendidos do SQL Server
- Registros de erros do SQL Server
- Rastreamentos padrão no SQL Server
5 tipos de deadlocks no SQL Server
1) Impasse de pesquisa de favoritos
A pesquisa de favoritos é um impasse comumente encontrado no SQL Server. Isso ocorre devido a um conflito entre a instrução select e as instruções DML (insert, update e delete). Normalmente, o SQL Server escolhe a instrução select como vítima de deadlock porque ela não causa alterações nos dados e a reversão é rápida. Para evitar a pesquisa de favoritos, você pode usar um índice de cobertura. Você também pode usar uma dica de consulta NOLOCK nas instruções select, mas ela lê dados não confirmados.
2) Impasse de varredura de intervalo
Às vezes, usamos um nível de isolamento SERIALIZABLE no nível do servidor ou no nível da sessão. É um nível de isolamento restritivo para controle de simultaneidade e pode criar bloqueios de varredura de intervalo em vez de bloqueios de nível de página ou linha. No nível de isolamento SERIALIZABLE, os usuários não podem ler os dados se estiverem modificados, mas aguardando serem confirmados em uma transação. Da mesma forma, se uma transação lê dados, outra transação não pode modificá-los. Ele fornece a simultaneidade mais baixa, portanto, devemos usar esse nível de isolamento em requisitos de aplicativos específicos.
3) Impasse de restrição em cascata
O SQL Server usa a relação pai-filho entre tabelas usando as restrições de chave estrangeira. Nesse cenário, se atualizarmos ou excluirmos um registro da tabela pai, serão necessários os bloqueios na tabela filho para evitar registros órfãos. Para eliminar esses deadlocks, você deve sempre modificar os dados em uma tabela filho primeiro, seguidos pelos dados pai. Você também pode trabalhar diretamente com a tabela pai usando as opções DELETE CASCADE ou UPDATE CASCADE. Você também deve criar índices apropriados nas colunas de chave estrangeira.
4) Impasse de paralelismo intra-consulta
Depois que um usuário envia uma consulta ao mecanismo de consulta SQL, o otimizador de consultas cria um plano de execução otimizado. Ele pode executar a consulta em ordem serial ou paralela, dependendo do custo da consulta, do grau máximo de paralelismo (MAXDOP) e do limite de custo para paralelismo.
Em um modo de paralelismo, o SQL Server atribui vários threads. Às vezes, para uma consulta grande em modo de paralelismo, esses threads começam a bloquear uns aos outros. Eventualmente, ele se converte em deadlocks. Nesse caso, você precisa revisar o plano de execução e seu MAXDOP e limite de custo para configurações de paralelismo. Você também pode especificar o MAXDOP no nível da sessão para solucionar o cenário de conflito.
5) Impasse de ordem de objeto reverso
Nesse tipo de deadlock, várias transações acessam objetos em uma ordem diferente no T-SQL. Isso causa o bloqueio entre os recursos para cada sessão e o converte em um deadlock. Você sempre deseja acessar objetos em uma ordem lógica para que isso não leve a uma situação de deadlock.
Maneiras úteis de evitar e minimizar impasses do SQL Server
- Tente manter as transações curtas; isso evitará bloqueios em uma transação por um longo período de tempo.
- Acessar objetos de maneira lógica semelhante em várias transações.
- Crie um índice de cobertura para reduzir a possibilidade de um impasse.
- Crie índices para corresponder às colunas de chave estrangeira. Dessa forma, você pode eliminar deadlocks devido à integridade referencial em cascata.
- Defina prioridades de deadlock usando a variável de sessão SET DEADLOCK_PRIORITY. Se você definir a prioridade de deadlock, o SQL Server encerrará a sessão com a prioridade de deadlock mais baixa.
- Utilize o tratamento de erros usando os blocos try-catch. Você pode interceptar o erro de deadlock e executar novamente a transação no caso de uma vítima de deadlock.
- Mude o nível de isolamento para ISOLAMENTO DE INSTANTÂNEO COMPROMETIDO LIDO ou ISOLAMENTO DE INSTANTÂNEO. Isso altera o mecanismo de bloqueio do SQL Server. No entanto, você deve ter cuidado ao alterar o nível de isolamento, pois isso pode afetar negativamente outras consultas.
Considerações de impasse do SQL Server
Os deadlocks são um mecanismo natural no SQL Server para evitar que a sessão retenha bloqueios e aguarde outros recursos. Você deve capturar consultas de impasse e otimizá-las para que não entrem em conflito umas com as outras. É importante capturar o bloqueio por um curto período e liberá-lo, para que outras consultas possam usá-lo efetivamente.
Os deadlocks do SQL Server acontecem e, embora o SQL Server lide internamente com situações de deadlock, você deve tentar minimizá-los sempre que possível. Algumas das melhores maneiras de eliminar impasses são criando um índice, aplicando alterações no código do aplicativo ou inspecionando cuidadosamente os recursos em um gráfico de impasses. Para obter mais dicas sobre como evitar bloqueios de SQL, confira nossa postagem:Como evitar bloqueios de SQL com ajuste de consulta.