Analise e previna bloqueios no Banco de Dados SQL do Azure e no banco de dados SQL Fabric
Aplica-se a:Banco de Dados SQL do Azure
Banco de Dados SQL no Fabric
Este artigo ensina como identificar deadlocks, usar gráficos de deadlock e Query Store para identificar as consultas no deadlock e planejar e testar alterações para evitar que deadlocks voltem a ocorrer. Este artigo aplica-se à Base de Dados SQL do Azure e à Base de Dados SQL Fabric, que partilha muitas funcionalidades da Base de Dados SQL do Azure.
Este artigo se concentra em identificar e analisar impasses devido à contenção de bloqueio. Saiba mais sobre outros tipos de impasses em recursos que podem causar bloqueios em.
Como ocorrem os impasses
Cada novo banco de dados no Banco de Dados SQL do Azure tem a configuração de banco de dados RCSI (de isolamento de instantâneo confirmado de leitura) habilitada por padrão. O bloqueio de entre sessões de leitura de dados e sessões de gravação de dados é minimizado no RCSI, que usa o versionamento de linhas para aumentar a simultaneidade. No entanto, bloqueios e deadlocks ainda podem ocorrer em bases de dados no Banco de Dados SQL do Azure porque:
As consultas que modificam dados podem bloquear umas às outras.
As consultas podem ser executadas em níveis de isolamento que aumentam o bloqueio. Os níveis de isolamento podem ser especificados por meio de métodos de biblioteca de cliente, dicas de consultaou SET TRANSACTION ISOLATION LEVEL no Transact-SQL.
RCSI pode estar desativado, fazendo com que o banco de dados use bloqueios compartilhados (S) para proteger
SELECT
instruções executadas sob o nível de isolamento confirmado de leitura. Isso poderia aumentar os bloqueios e os impasses.
Um exemplo de impasse
Um impasse ocorre quando duas ou mais tarefas bloqueiam indefinidamente umas às outras porque cada tarefa tem um bloqueio sobre um recurso que as outras estão tentando bloquear. Um impasse também é chamado de dependência cíclica: no caso de um impasse de duas tarefas, a transação A tem uma dependência da transação B, e a transação B fecha o círculo por ter uma dependência da transação A.
Por exemplo:
Sessão A inicia uma transação explícita e executa uma instrução de atualização que adquire um bloqueio de atualização (U) numa linha da tabela
SalesLT.Product
que é convertida num bloqueio exclusivo (X).Sessão B executa uma instrução update que modifica a tabela
SalesLT.ProductDescription
. A instrução update se une à tabelaSalesLT.Product
para encontrar as linhas corretas a serem atualizadas.A sessão B adquire um bloqueio de atualização (U) em 72 linhas da tabela
SalesLT.ProductDescription
.Sessão B precisa de um bloqueio compartilhado em linhas na tabela
SalesLT.Product
, incluindo a linha que está bloqueada por Sessão A. Sessão B está bloqueada noSalesLT.Product
.
Sessão A continua sua transação e agora executa uma atualização na tabela
SalesLT.ProductDescription
. Sessão A é bloqueada pela Sessão B emSalesLT.ProductDescription
.
Todas as transações em um impasse aguardam indefinidamente, a menos que uma das transações participantes seja revertida, por exemplo, porque sua sessão foi encerrada.
O monitor de interbloqueio do mecanismo de banco de dados verifica periodicamente as tarefas que estão num interbloqueio. Se o monitor de deadlock detetar uma dependência cíclica, ele escolhe uma das tarefas como vítima e encerra sua transação com o erro 1205: Transaction (Process ID <N>) was deadlocked on lock resources with another process and is chosen as the deadlock victim. Rerun the transaction.
Quebrar o impasse dessa forma permite que a outra tarefa ou tarefas no deadlock concluam suas transações.
Observação
Saiba mais sobre os critérios para escolher uma vítima de deadlock na seção Deadlock process list deste artigo.
O aplicativo com a transação escolhida como vítima do deadlock deve repetir a transação, que geralmente é concluída após a conclusão da outra transação ou transações envolvidas no deadlock.
É uma prática recomendada introduzir um atraso curto e aleatório antes de tentar novamente para evitar encontrar o mesmo impasse novamente. Saiba mais sobre como criar lógica de repetição para erros transitórios.
Nível de isolamento padrão no Banco de Dados SQL do Azure
As novas bases de dados no Azure SQL Database ativam por padrão o Read Committed Snapshot (RCSI). O RCSI altera o comportamento do ler o nível de isolamento comprometido para usar de controle de versão de linha para fornecer consistência no nível da instrução sem o uso de bloqueios compartilhados (S) para instruções SELECT
.
Com o RCSI ativado:
- As instruções que leem dados não bloqueiam as instruções que modificam dados.
- As instruções que modificam dados não bloqueiam a leitura de dados.
O nível de isolamento de instantâneo também está ativado por padrão para novos bancos de dados no Azure SQL Database. O isolamento instantâneo é um nível de isolamento adicional baseada em linhas que garante consistência a nível de transações para dados e que usa versões de linha para selecionar linhas a serem atualizadas. Para usar o isolamento de instantâneo, as consultas ou conexões devem definir explicitamente seu nível de isolamento de transação como SNAPSHOT
. Apenas é possível quando o isolamento por instantâneo está habilitado para o banco de dados.
É possível identificar se o RCSI e/ou o isolamento de snapshot estão habilitados com o Transact-SQL. Conecte-se ao seu banco de dados no Banco de Dados SQL do Azure e execute a seguinte consulta:
SELECT name,
is_read_committed_snapshot_on,
snapshot_isolation_state_desc
FROM sys.databases
WHERE name = DB_NAME();
GO
Se o RCSI estiver habilitado, a coluna is_read_committed_snapshot_on
retornará o valor 1
. Se o isolamento de instantâneo estiver habilitado, a coluna snapshot_isolation_state_desc
retornará o valor ON
.
Se RCSI estiver desabilitado para um banco de dados no Banco de Dados SQL do Azure, investigue por que o RCSI foi desabilitado antes de reativá-lo. O código da aplicação pode esperar que as consultas que lêem dados sejam bloqueadas por consultas que gravam dados, resultando em resultados incorretos das condições de corrida quando o RCSI está ativado.
Interpretar eventos de deadlock
Um evento de deadlock é emitido depois que o gerenciador de deadlock no Banco de Dados SQL do Azure deteta um deadlock e seleciona uma transação como vítima. Em outras palavras, se configurares alertas para bloqueios, a notificação será acionada depois que um bloqueio individual for resolvido. Não há nenhuma ação do usuário que precise ser tomada para esse impasse. Os aplicativos devem ser gravados para incluir lógica de repetição para que continuem automaticamente após receber o erro 1205: Transaction (Process ID <N>) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
No entanto, é útil configurar alertas, pois os impasses podem voltar a ocorrer. Os alertas de deadlock permitem que você investigue se um padrão de deadlocks repetidos está acontecendo em seu banco de dados, caso em que você pode optar por tomar medidas para evitar que os deadlocks voltem a ocorrer. Saiba mais sobre alertas na secção Monitor e sobre o alerta de bloqueios na secção deste artigo.
Principais métodos para evitar impasses
A abordagem de menor risco para evitar que os deadlocks voltem a ocorrer é geralmente ajustar os índices não agrupados para otimizar as consultas envolvidas no deadlock.
O risco é baixo para essa abordagem porque o ajuste de índices não clusterizados não requer alterações no próprio código de consulta, reduzindo o risco de um erro do usuário ao reescrever Transact-SQL que faz com que dados incorretos sejam retornados ao usuário.
O ajuste eficaz do índice não clusterizado ajuda as consultas a encontrar os dados para ler e modificar com mais eficiência. Ao reduzir a quantidade de dados que uma consulta precisa acessar, a probabilidade de bloqueio é reduzida e os bloqueios muitas vezes podem ser evitados.
Em alguns casos, criar ou ajustar um índice agrupado pode reduzir bloqueios e deadlocks. Como o índice clusterizado está incluído em todas as definições de índice não clusterizado, criar ou modificar um índice clusterizado pode ser uma operação intensiva e demorada de E/S em tabelas maiores com índices não clusterizados existentes. Saiba mais sobre diretrizes de design de índice clusterizado.
Quando o ajuste do índice não é bem-sucedido na prevenção de deadlocks, outros métodos estão disponíveis:
Se o bloqueio ocorrer apenas quando um plano específico for escolhido para uma das consultas envolvidas no bloqueio, forçar um plano de consulta com o Query Store pode impedir que os bloqueios ocorram novamente.
Reescrever Transact-SQL para uma ou mais transações envolvidas no impasse também pode ajudar a evitar bloqueios. Dividir transações explícitas em transações menores requer codificação e testes cuidadosos para garantir a validade dos dados quando ocorrerem modificações simultâneas.
Saiba mais sobre cada uma dessas abordagens na seção Impedir que um impasse se repita deste artigo.
Monitorar e alertar sobre impasses
Neste artigo, usamos o banco de dados de exemplo AdventureWorksLT
para configurar alertas para deadlocks, causar um exemplo de deadlock, analisar o gráfico de deadlock para o deadlock de exemplo e testar alterações para evitar que o deadlock volte a ocorrer.
Usamos o cliente SQL Server Management Studio (SSMS) neste artigo, pois ele contém funcionalidade para exibir gráficos de deadlock em um modo visual interativo. Você pode usar outros clientes, como do Azure Data Studio, para acompanhar os exemplos, mas talvez só consiga exibir gráficos de deadlock como XML.
Criar o banco de dados AdventureWorksLT
Para acompanhar os exemplos, crie um novo banco de dados no Banco de Dados SQL do Azure e selecione dados de de exemplo como a fonte de dados .
Para obter instruções detalhadas sobre como criar AdventureWorksLT
com o portal do Azure, a CLI do Azure ou o PowerShell, selecione a abordagem de sua escolha em Guia de início rápido: criar um banco de dados único do Banco de Dados SQL do Azure.
Configurar alertas de deadlock no portal do Azure
Para configurar alertas para eventos de deadlock, siga as etapas no artigo Criar alertas para o Banco de Dados SQL do Azure e o Azure Synapse Analytics usando o portal do Azure.
Selecione Deadlocks como o nome do sinal para o alerta. Configure o do grupo Ação para notificá-lo usando o método de sua escolha, como o tipo de ação Email/SMS/Push/Voice.
Recolher gráficos de deadlock na Base de Dados SQL do Azure com Eventos Estendidos
Os gráficos de impasse são uma fonte rica de informações sobre os processos e bloqueios envolvidos em um impasse. Para recolher gráficos de impasse com Eventos Estendidos (XEvents) na Base de Dados SQL do Azure, capture o evento sqlserver.database_xml_deadlock_report
.
Você pode recolher gráficos de deadlock com XEvents usando o destino do buffer de anel ou um destino de arquivo de evento . As considerações para selecionar o tipo de destino apropriado estão resumidas na tabela a seguir:
Selecione o tipo de destino que você gostaria de usar:
O alvo do buffer de anel é conveniente e fácil de configurar, mas tem uma capacidade limitada, o que pode causar a perda de eventos mais antigos. O buffer de anel não persiste eventos para armazenamento e o alvo do buffer de anel é limpo quando a sessão XEvents é interrompida. Isso significa que quaisquer XEvents coletados não estarão disponíveis quando o mecanismo de base de dados for reiniciado por qualquer motivo, como uma comutação por erro. O destino do buffer de anel é mais adequado para necessidades de aprendizagem e de curto prazo se você não tiver a capacidade de configurar uma sessão XEvents para um destino de arquivo de evento imediatamente.
Este código de exemplo cria uma sessão de _XEvents_ que captura gráficos de deadlock na memória usando o alvo do anel de buffer . A memória máxima permitida para o destino do buffer circular é de 4 MB, e a sessão inicia automaticamente quando o banco de dados está disponível, como após uma transição em caso de falha.
Para criar e iniciar uma sessão XEvents para o evento sqlserver.database_xml_deadlock_report
que grava no destino do buffer de anel, conecte-se ao banco de dados e execute o seguinte Transact-SQL:
CREATE EVENT SESSION [deadlocks] ON DATABASE
ADD EVENT sqlserver.database_xml_deadlock_report
ADD TARGET package0.ring_buffer
WITH
(
STARTUP_STATE = ON,
MAX_MEMORY = 4 MB
);
GO
ALTER EVENT SESSION [deadlocks] ON DATABASE
STATE = START;
GO
Provocar um impasse no AdventureWorksLT
Observação
Este exemplo funciona na base de dados AdventureWorksLT
com o esquema e os dados predefinidos quando o RCSI está ativado. Consulte Criar o banco de dados AdventureWorksLT para obter instruções sobre como criar o banco de dados.
Para causar um impasse, você precisa conectar duas sessões ao banco de dados AdventureWorksLT
. Referimo-nos a estas sessões como Sessão A e Sessão B.
Na Sessão A, execute o seguinte código Transact-SQL. O código inicia uma transação explícita e executa uma única instrução que atualiza a tabela SalesLT.Product
. Para tal, a transação adquire um bloqueio de atualização (U) numa linha na tabela SalesLT.Product
que é convertido para um bloqueio exclusivo (X). Deixamos a transação em aberto.
BEGIN TRANSACTION;
UPDATE SalesLT.Product
SET SellEndDate = SellEndDate + 1
WHERE Color = 'Red';
Agora, no Sessão B, execute o seguinte Transact-SQL. Este código não inicia explicitamente uma transação. Em vez disso, ele opera em modo de transação de confirmação automática. Esta declaração atualiza a tabela SalesLT.ProductDescription
. A atualização elimina um bloqueio de atualização (U) em 72 linhas na tabela SalesLT.ProductDescription
. A consulta se une a outras tabelas, incluindo a tabela SalesLT.Product
.
UPDATE SalesLT.ProductDescription
SET Description = Description
FROM SalesLT.ProductDescription AS pd
INNER JOIN SalesLT.ProductModelProductDescription AS pmpd
ON pd.ProductDescriptionID = pmpd.ProductDescriptionID
INNER JOIN SalesLT.ProductModel AS pm
ON pmpd.ProductModelID = pm.ProductModelID
INNER JOIN SalesLT.Product AS p
ON pm.ProductModelID = p.ProductModelID
WHERE p.Color = 'Silver';
Para concluir esta atualização, Sessão B necessita de um bloqueio partilhado (S) sobre as linhas da tabela SalesLT.Product
, incluindo a linha que está bloqueada por Sessão A. Sessão B está bloqueada em SalesLT.Product
.
Volte à sessão A. Execute a instrução seguinte Transact-SQL. Isso executa uma segunda instrução UPDATE
como parte da transação aberta.
UPDATE SalesLT.ProductDescription
SET Description = Description
FROM SalesLT.ProductDescription AS pd
INNER JOIN SalesLT.ProductModelProductDescription AS pmpd
ON pd.ProductDescriptionID = pmpd.ProductDescriptionID
INNER JOIN SalesLT.ProductModel AS pm
ON pmpd.ProductModelID = pm.ProductModelID
INNER JOIN SalesLT.Product AS p
ON pm.ProductModelID = p.ProductModelID
WHERE p.Color = 'Red';
A segunda instrução de atualização na Sessão A é bloqueada pela Sessão B no SalesLT.ProductDescription
.
Sessão A e Sessão B estão agora a bloquear-se mutuamente. Nenhuma transação pode prosseguir, pois cada uma precisa de um recurso bloqueado pela outra.
Após alguns segundos, o monitor de deadlock identifica que as transações na Sessão A e na Sessão B estão a bloquear-se mutuamente e que nenhuma delas pode progredir. Você deve ver um impasse ocorrer, com Sessão A escolhida como a vítima do impasse. É apresentada uma mensagem de erro na Sessão A com um texto semelhante ao seguinte:
Msg 1205, Level 13, State 51, Line 7
Transaction (Process ID 91) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
Sessão B é concluída com êxito.
Se você configurar alertas de deadlock no portal do Azure, deverá receber uma notificação logo após o deadlock ocorrer.
Exibir gráficos de deadlock de uma sessão do XEvents
Se você configurar uma sessão XEvents para coletar deadlocks e um deadlock ocorrer após o início da sessão, você poderá exibir uma exibição gráfica interativa do gráfico de deadlock e o XML para o gráfico de deadlock.
Diferentes métodos estão disponíveis para obter informações de deadlock para o destino do buffer de anel e destinos de arquivo de evento. Selecione o destino que você usou para sua sessão XEvents:
Se configurar uma sessão de XEvents gravando no buffer circular, poderá consultar informações sobre deadlocks com o seguinte Transact-SQL. Antes de executar a consulta, substitua o valor de @tracename
pelo nome da sessão XEvents.
DECLARE @tracename AS sysname = N'deadlocks';
WITH ring_buffer
AS (SELECT CAST (target_data AS XML) AS rb
FROM sys.dm_xe_database_sessions AS s
INNER JOIN sys.dm_xe_database_session_targets AS t
ON CAST (t.event_session_address AS BINARY (8)) = CAST (s.address AS BINARY (8))
WHERE s.name = @tracename
AND t.target_name = N'ring_buffer'),
dx
AS (SELECT dxdr.evtdata.query('.') AS deadlock_xml_deadlock_report
FROM ring_buffer
CROSS APPLY rb.nodes('/RingBufferTarget/event[@name=''database_xml_deadlock_report'']') AS dxdr(evtdata))
SELECT d.query('/event/data[@name=''deadlock_cycle_id'']/value').value('(/value)[1]', 'int') AS [deadlock_cycle_id],
d.value('(/event/@timestamp)[1]', 'DateTime2') AS [deadlock_timestamp],
d.query('/event/data[@name=''database_name'']/value').value('(/value)[1]', 'nvarchar(256)') AS [database_name],
d.query('/event/data[@name=''xml_report'']/value/deadlock') AS deadlock_xml,
LTRIM(RTRIM(REPLACE(REPLACE(d.value('.', 'nvarchar(2000)'), CHAR(10), ' '), CHAR(13), ' '))) AS query_text
FROM dx
CROSS APPLY deadlock_xml_deadlock_report.nodes('(/event/data/value/deadlock/process-list/process/inputbuf)') AS ib(d)
ORDER BY [deadlock_timestamp] DESC;
GO
Exibir e salvar um gráfico de deadlock em XML
A visualização de um gráfico de deadlock em formato XML permite copiar a inputbuffer
de Transact-SQL instruções envolvidas no deadlock. Pode também analisar impasses num formato baseado em texto.
Se você tiver usado uma consulta Transact-SQL para retornar informações do gráfico de deadlock, para exibir o XML do gráfico de deadlock, selecione o valor na coluna deadlock_xml
de qualquer linha para abrir o XML do gráfico de deadlock em uma nova janela no SSMS.
O XML para este exemplo de gráfico de deadlock é:
<deadlock>
<victim-list>
<victimProcess id="process24756e75088" />
</victim-list>
<process-list>
<process id="process24756e75088" taskpriority="0" logused="6528" waitresource="KEY: 8:72057594045202432 (98ec012aa510)" waittime="192" ownerId="1011123" transactionname="user_transaction" lasttranstarted="2022-03-08T15:44:43.490" XDES="0x2475c980428" lockMode="U" schedulerid="3" kpid="30192" status="suspended" spid="89" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2022-03-08T15:44:49.250" lastbatchcompleted="2022-03-08T15:44:49.210" lastattention="1900-01-01T00:00:00.210" clientapp="Microsoft SQL Server Management Studio - Query" hostname="LAPTOP-CHRISQ" hostpid="16716" loginname="chrisqpublic" isolationlevel="read committed (2)" xactid="1011123" currentdb="8" currentdbname="AdventureWorksLT" lockTimeout="4294967295" clientoption1="671096864" clientoption2="128056">
<executionStack>
<frame procname="unknown" queryhash="0xef52b103e8b9b8ca" queryplanhash="0x02b0f58d7730f798" line="1" stmtstart="2" stmtend="792" sqlhandle="0x02000000c58b8f1e24e8f104a930776e21254b1771f92a520000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
UPDATE SalesLT.ProductDescription SET Description = Description
FROM SalesLT.ProductDescription as pd
JOIN SalesLT.ProductModelProductDescription as pmpd on
pd.ProductDescriptionID = pmpd.ProductDescriptionID
JOIN SalesLT.ProductModel as pm on
pmpd.ProductModelID = pm.ProductModelID
JOIN SalesLT.Product as p on
pm.ProductModelID=p.ProductModelID
WHERE p.Color = 'Red' </inputbuf>
</process>
<process id="process2476d07d088" taskpriority="0" logused="11360" waitresource="KEY: 8:72057594045267968 (39e18040972e)" waittime="2641" ownerId="1013536" transactionname="UPDATE" lasttranstarted="2022-03-08T15:44:46.807" XDES="0x2475ca80428" lockMode="S" schedulerid="2" kpid="94040" status="suspended" spid="95" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2022-03-08T15:44:46.807" lastbatchcompleted="2022-03-08T15:44:46.760" lastattention="1900-01-01T00:00:00.760" clientapp="Microsoft SQL Server Management Studio - Query" hostname="LAPTOP-CHRISQ" hostpid="16716" loginname="chrisqpublic" isolationlevel="read committed (2)" xactid="1013536" currentdb="8" currentdbname="AdventureWorksLT" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
<executionStack>
<frame procname="unknown" queryhash="0xef52b103e8b9b8ca" queryplanhash="0x02b0f58d7730f798" line="1" stmtstart="2" stmtend="798" sqlhandle="0x020000002c85bb06327c0852c0be840fc1e30efce2b7c8090000000000000000000000000000000000000000">
unknown </frame>
</executionStack>
<inputbuf>
UPDATE SalesLT.ProductDescription SET Description = Description
FROM SalesLT.ProductDescription as pd
JOIN SalesLT.ProductModelProductDescription as pmpd on
pd.ProductDescriptionID = pmpd.ProductDescriptionID
JOIN SalesLT.ProductModel as pm on
pmpd.ProductModelID = pm.ProductModelID
JOIN SalesLT.Product as p on
pm.ProductModelID=p.ProductModelID
WHERE p.Color = 'Silver'; </inputbuf>
</process>
</process-list>
<resource-list>
<keylock hobtid="72057594045202432" dbid="8" objectname="9e011567-2446-4213-9617-bad2624ccc30.SalesLT.ProductDescription" indexname="PK_ProductDescription_ProductDescriptionID" id="lock2474df12080" mode="U" associatedObjectId="72057594045202432">
<owner-list>
<owner id="process2476d07d088" mode="U" />
</owner-list>
<waiter-list>
<waiter id="process24756e75088" mode="U" requestType="wait" />
</waiter-list>
</keylock>
<keylock hobtid="72057594045267968" dbid="8" objectname="9e011567-2446-4213-9617-bad2624ccc30.SalesLT.Product" indexname="PK_Product_ProductID" id="lock2474b588580" mode="X" associatedObjectId="72057594045267968">
<owner-list>
<owner id="process24756e75088" mode="X" />
</owner-list>
<waiter-list>
<waiter id="process2476d07d088" mode="S" requestType="wait" />
</waiter-list>
</keylock>
</resource-list>
</deadlock>
Para salvar o gráfico de deadlock como um arquivo XML:
- Selecione Arquivo e Salvar como....
- Deixe o valor de Guardar como tipo como o predefinido Ficheiros XML (*.xml)
- Defina o Nome do arquivo para o nome de sua escolha.
- Selecione Salvar.
Salvar um gráfico de deadlock como um arquivo XDL que pode ser exibido interativamente no SSMS
A visualização de uma representação interativa de um gráfico de deadlock pode ser útil para obter uma visão geral rápida dos processos e recursos envolvidos em um deadlock e identificar rapidamente a vítima de deadlock.
Para salvar um gráfico de deadlock como um arquivo que pode ser exibido graficamente pelo SSMS:
Selecione o valor de qualquer linha na coluna
deadlock_xml
para abrir o XML do gráfico de impasse numa nova janela no SSMS.Selecione Arquivo e Guardar como....
Defina Salvar como tipo para Todos os arquivos.
Defina o Nome do arquivo para o nome de sua escolha, com a extensão definida como
.xdl
.Selecione Salvar.
Feche o ficheiro selecionando o X no separador na parte superior da janela, ou selecionando Ficheiroe, em seguida, Fechar.
Reabra o ficheiro no SSMS selecionando Arquivo, depois Abrire, em seguida, Ficheiro. Selecione o arquivo que você salvou com a extensão
.xdl
.O gráfico de deadlock agora é exibido no SSMS com uma representação visual dos processos e recursos envolvidos no deadlock.
Analisar um impasse da Base de Dados SQL do Azure
Um gráfico de deadlock normalmente tem três nós:
Lista de vítimas. O identificador do processo da vítima de deadlock.
Lista de processos. Informações sobre todos os processos envolvidos no impasse. Os gráficos de deadlock usam o termo 'processo' para representar uma sessão executando uma transação.
Lista de recursos. Informações sobre os recursos envolvidos no impasse.
Ao analisar um impasse, é útil percorrer esses nós.
Lista de vítimas de impasse
A lista de vítimas de deadlock mostra o processo que foi escolhido como a vítima de deadlock. Na representação visual de um gráfico de deadlock, os processos são representados por ovais. O processo de vítima de impasse tem um "X" desenhado sobre o oval.
Na visualização XML de um gráfico de deadlock, o nó victim-list
fornece uma ID para o processo que foi vítima do deadlock.
No nosso exemplo de deadlock, o ID do processo da vítima é process24756e75088
. Podemos usar esse ID ao examinar os nós de lista de processos e lista de recursos para saber mais sobre o processo da vítima e os recursos que ele estava bloqueando ou solicitando para bloquear.
Lista de processos de bloqueio
A lista de processos de impasse é uma fonte rica de informações sobre as transações envolvidas no impasse.
A representação gráfica do grafo de deadlock mostra apenas um subconjunto das informações contidas no XML do grafo de deadlock. As elipses no gráfico de impasse representam o processo e mostram informações, incluindo:
ID da sessão, também conhecido como SPID.
Prioridade de bloqueio da sessão. Se duas sessões tiverem prioridades de bloqueio diferentes, a sessão com a prioridade mais baixa será escolhida como a vítima do impasse. Neste exemplo, ambas as sessões têm a mesma prioridade de bloqueio.
A quantidade de log de transações usado pela sessão em bytes. Se ambas as sessões tiverem a mesma prioridade de deadlock, o monitor de deadlock escolhe a sessão que é menos custosa de reverter como vítima de deadlock. O custo é determinado comparando o número de bytes de log gravados até esse ponto em cada transação.
Em nosso exemplo de deadlock,
session_id
89 usou uma quantidade menor de log de transações e foi selecionado como a vítima do deadlock.
Além disso, você pode visualizar o do buffer de entrada para a última instrução executada em cada sessão antes do deadlock passando o mouse sobre cada processo. O buffer de entrada aparece em uma dica de ferramenta.
Informações adicionais para processos estão disponíveis na visualização XML do gráfico de impasse, incluindo:
Informações de identificação da sessão, como o nome do cliente, o nome do host e o nome de login.
O hash do plano de consulta para o último comando executado por cada sessão antes do deadlock. O hash do plano de consulta é útil para recuperar mais informações sobre a consulta do Repositório de Consultas.
No nosso exemplo de impasse:
Podemos ver que ambas as sessões foram executadas usando o cliente SSMS sob o login
chrisqpublic
.O hash do plano de consulta, da última instrução executada antes do deadlock por nossa vítima de deadlock, é
0x02b0f58d7730f798
. Podemos ver o texto desta instrução no buffer de entrada.O hash do plano de consulta, da última instrução executada pela outra sessão em nosso deadlock, também é
0x02b0f58d7730f798
. Podemos ver o texto desta instrução no buffer de entrada. Nesse caso, ambas as consultas têm o mesmo hash de plano de consulta porque as consultas são idênticas, exceto por um valor literal usado como um predicado de igualdade.
Usamos esses valores posteriormente neste artigo para encontrar informações adicionais no Query Store.
Limitações do buffer de entrada na lista de processos de deadlock
Existem algumas limitações a ter em conta relativamente às informações do buffer de entrada na lista de processos de deadlock.
O texto da consulta pode ser truncado no buffer de entrada. O buffer de entrada é limitado aos primeiros 4.000 caracteres da instrução que está sendo executada.
Além disso, algumas declarações envolvidas no impasse podem não ser incluídas no gráfico de impasse. Em nosso exemplo, Sessão A executou duas instruções de atualização em uma única transação. Apenas a segunda instrução de atualização, sendo esta a que causou o deadlock, é incluída no diagrama de deadlock. A primeira declaração de atualização executada pela Sessão A desempenhou um papel no impasse ao bloquear a Sessão B. O buffer de entrada, query_hash
, e as informações relacionadas para a primeira instrução executada pela Sessão A não estão incluídos no gráfico de deadlock.
Para identificar o Transact-SQL completo executado em uma transação de várias instruções envolvida em um deadlock, você precisa encontrar as informações relevantes no procedimento armazenado ou no código do aplicativo que executou a consulta ou executar um rastreamento usando Extended Events para capturar instruções completas executadas por sessões envolvidas em um deadlock enquanto ele ocorre. Se uma instrução envolvida no deadlock estiver truncada e apenas Transact-SQL parcial aparecer no buffer de entrada, você poderá encontrar o Transact-SQL da instrução no Repositório de Consultas com o Plano de Execução.
Lista de recursos de impasse
A lista de recursos de deadlock mostra quais recursos de bloqueio são de propriedade e aguardados pelos processos no deadlock.
Os recursos são representados por retângulos na representação visual do impasse:
Observação
Os nomes de bases de dados são representados como GUIDs (uniqueidentifier) em gráficos de interbloqueio para bases de dados no Azure SQL Database. Este é o physical_database_name
para a base de dados listada no sys.databases e no sys.dm_user_db_resource_governance vistas de gestão dinâmica.
Neste exemplo de impasse:
A vítima do impasse, a que nos referimos como Sessão A,:
Possui um bloqueio exclusivo (X) sobre uma chave no índice
PK_Product_ProductID
na tabelaSalesLT.Product
.Solicita um bloqueio de atualização (U) em uma chave no índice de
PK_ProductDescription_ProductDescriptionID
na tabelaSalesLT.ProductDescription
.
O outro processo, a que chamámos Sessão B:
Detém um bloqueio de atualização (U) numa chave no índice
PK_ProductDescription_ProductDescriptionID
na tabelaSalesLT.ProductDescription
.Solicita um bloqueio partilhado (S) numa chave no índice
PK_ProductDescription_ProductDescriptionID
na tabelaSalesLT.ProductDescription
.
Podemos ver as mesmas informações no XML do gráfico de deadlock no nó da lista de recursos .
Localizar planos de execução de consulta no Repositório de Consultas
Muitas vezes, é útil examinar os planos de execução de consulta para instruções envolvidas no deadlock. Esses planos de execução geralmente podem ser encontrados no Query Store usando o hash do plano de consulta da lista de processos da visualização XML do gráfico de deadlock.
Esta consulta Transact-SQL procura planos de consulta correspondentes ao hash do plano de consulta que encontramos para o nosso exemplo de deadlock. Conecte-se ao banco de dados de usuário no Banco de Dados SQL do Azure para executar a consulta.
DECLARE @query_plan_hash AS BINARY (8) = 0x02b0f58d7730f798;
SELECT qrsi.end_time AS interval_end_time,
qs.query_id,
qp.plan_id,
qt.query_sql_text,
TRY_CAST (qp.query_plan AS XML) AS query_plan,
qrs.count_executions
FROM sys.query_store_query AS qs
INNER JOIN sys.query_store_query_text AS qt
ON qs.query_text_id = qt.query_text_id
INNER JOIN sys.query_store_plan AS qp
ON qs.query_id = qp.query_id
INNER JOIN sys.query_store_runtime_stats AS qrs
ON qp.plan_id = qrs.plan_id
INNER JOIN sys.query_store_runtime_stats_interval AS qrsi
ON qrs.runtime_stats_interval_id = qrsi.runtime_stats_interval_id
WHERE query_plan_hash = @query_plan_hash
ORDER BY interval_end_time, query_id;
GO
Talvez não seja possível obter um plano de execução de consulta a partir do Repositório de Consultas, dependendo das suas configurações de CLEANUP_POLICY ou QUERY_CAPTURE_MODE. Nesse caso, muitas vezes pode-se obter as informações necessárias exibindo o plano de execução estimado para a consulta.
Procure padrões que aumentem o bloqueio
Ao examinar os planos de execução de consultas envolvidos em deadlocks, procure padrões que possam contribuir para bloqueios e deadlocks.
Varreduras de tabela ou de índice. Quando consultas que modificam dados são executadas sob o RCSI, a seleção de linhas a serem atualizadas é feita usando uma varredura de bloqueio, na qual um bloqueio de atualização (U) é feito na linha de dados à medida que os valores de dados são lidos. Se a linha de dados não atender aos critérios de atualização, o bloqueio de atualização será liberado e a próxima linha será bloqueada e verificada.
Ajustar índices para ajudar as consultas de modificação a encontrar linhas de forma mais eficiente reduz o número de bloqueios de atualização emitidos. Isso reduz as chances de bloqueio e impasses.
Exibições indexadas que fazem referência a mais de uma tabela. Quando você modifica uma tabela referenciada em um modo de exibição indexado, o mecanismo de banco de dados também deve manter o modo de exibição indexado. Isso requer a remoção de mais bloqueios e pode levar a um aumento de bloqueios e interbloqueios. As vistas indexadas também podem causar que as operações de atualização sejam executadas internamente sob o nível de isolamento de leitura confirmada.
Modificações em colunas referenciadas em restrições de chave estrangeira. Quando você modifica colunas em uma tabela que são referenciadas em uma restrição de
FOREIGN KEY
, o mecanismo de banco de dados deve procurar linhas relacionadas na tabela de referência. As versões de linha não podem ser usadas para essas leituras. Nos casos em que as atualizações ou exclusões em cascata estão habilitadas, o nível de isolamento pode ser escalado para serializável durante a duração da instrução para proteger contra inserções fantasmas.Bloquear dicas. Procure pistas nas tabelas que especificam níveis de isolamento que exigem mais bloqueios. Essas dicas incluem
HOLDLOCK
(que é equivalente a serializável),SERIALIZABLE
,READCOMMITTEDLOCK
(que desativa o RCSI) eREPEATABLEREAD
. Além disso, dicas comoPAGLOCK
,TABLOCK
,UPDLOCK
eXLOCK
podem aumentar os riscos de bloqueio e deadlock.Se essas dicas estiverem em vigor, pesquise por que elas foram implementadas. Essas dicas podem prevenir situações de concorrência e garantir a validade dos dados. Talvez seja possível deixar essas dicas no lugar e evitar futuros impasses usando um método alternativo na seção Impedir que um impasse volte a ocorrer deste artigo, se necessário.
Observação
Saiba mais sobre o comportamento ao modificar dados usando o controle de versão de linha no Guia de bloqueio de transações e controle de versão de linha.
Ao examinar o código completo de uma transação, seja em um plano de execução ou no código de consulta do aplicativo, procure padrões problemáticos adicionais:
Interação do usuário em transações. A interação do usuário dentro de uma transação explícita de várias declarações aumenta significativamente a duração das transações. Isso torna mais provável que essas transações se sobreponham e que ocorram bloqueios e impasses.
Da mesma forma, manter uma transação aberta e consultar um banco de dados ou sistema não relacionado durante a transação aumenta significativamente as chances de bloqueios e deadlocks.
Transações acessando objetos em diferentes ordens. Os impasses são menos prováveis de ocorrer quando transações explícitas simultâneas de várias instruções seguem os mesmos padrões e acessam objetos na mesma ordem.
Evitar que um impasse se repita
Há várias técnicas disponíveis para evitar que os impasses voltem a ocorrer, como a otimização de índices, a imposição de planos com o Repositório de Consultas e a modificação de consultas Transact-SQL.
Revise o índice agrupado da tabela. A maioria das tabelas se beneficia de índices agrupados, mas muitas vezes, as tabelas são implementadas como pilhas por acidente.
Uma maneira de verificar se há um índice clusterizado é usando o procedimento armazenado do sistema sp_helpindex. Por exemplo, podemos exibir um resumo dos índices na tabela
SalesLT.Product
executando a seguinte instrução:EXECUTE sp_helpindex 'SalesLT.Product'; GO
Analise a coluna
index_description
. Uma tabela pode ter apenas um índice clusterizado. Se um índice clusterizado foi implementado para a tabela, oindex_description
contém a palavraclustered
.Se nenhum índice clusterizado estiver presente, a tabela será um heap. Nesse caso, analise se a tabela foi criada intencionalmente como um heap para resolver um problema de desempenho específico. Considere a implementação de um índice agrupado com base nas diretrizes de design de índice agrupado .
Em alguns casos, criar ou ajustar um índice clusterizado pode reduzir ou eliminar o bloqueio em deadlocks. Em outros casos, você pode empregar uma técnica adicional, como as outras nesta lista.
Criar ou modificar índices não clusterizados. O ajuste de índices não clusterizados pode ajudar suas consultas de modificação a encontrar os dados a serem atualizados mais rapidamente, o que reduz o número de bloqueios de atualização necessários.
No nosso exemplo de deadlock, o plano de execução de consulta encontrado no Repositório de Consultas contém uma análise de índice clusterizado em relação ao índice
PK_Product_ProductID
. O gráfico de deadlock indica que a espera por um bloqueio partilhado (S) sobre este índice é um elemento do deadlock.Essa verificação de índice está sendo executada porque nossa consulta de atualização precisa modificar uma exibição indexada chamada
vProductAndDescription
. Como mencionado na seção Procurar padrões que aumentem o bloqueio deste artigo, as visualizações indexadas que fazem referência a várias tabelas podem aumentar o bloqueio e a probabilidade de bloqueios.Se criarmos o seguinte índice não clusterizado no banco de dados
AdventureWorksLT
que "cobre" as colunas deSalesLT.Product
referenciadas pelo modo de exibição indexado, isso ajuda a consulta a localizar linhas com muito mais eficiência:CREATE INDEX IX_Product_ProductID_Name_ProductModelID ON SalesLT.Product(ProductID, Name, ProductModelID); GO
Depois de criar esse índice, o impasse não ocorre mais.
Quando os deadlocks envolverem modificações em colunas referenciadas em restrições de chave estrangeira, certifique-se de que os índices na tabela de referência do
FOREIGN KEY
suportem a localização eficiente de linhas relacionadas.Embora os índices possam melhorar drasticamente o desempenho da consulta em alguns casos, os índices também têm custos gerais e de gerenciamento. Revise diretrizes gerais de design de índice para ajudar a avaliar o benefício dos índices antes de criar índices, especialmente índices amplos e índices em tabelas grandes.
Avaliar o valor das vistas indexadas. Outra opção para evitar que o nosso exemplo de deadlock se repita é eliminar a exibição indexada
SalesLT.vProductAndDescription
. Se esse modo de exibição indexado não estiver sendo usado, isso reduzirá a sobrecarga de manter o modo de exibição indexado ao longo do tempo.Use o isolamento de instantâneo. Em alguns casos, definir o nível de isolamento da transacção para instantâneo em uma ou mais transações envolvidas num deadlock pode impedir que o bloqueio e os deadlocks voltem a ocorrer.
É mais provável que essa técnica seja bem-sucedida quando usada em instruções
SELECT
quando instantâneo de leitura confirmada está desativado num banco de dados. Quando o instantâneo de leitura confirmada é desativado, as consultasSELECT
que usam o nível de isolamento de leitura confirmada exigem bloqueios partilhados (S). O uso do isolamento de instantâneos nessas transações elimina a necessidade de bloqueios compartilhados, o que pode prevenir bloqueios e impasses.Em bases de dados onde o isolamento por instantâneo de leitura confirmada está ativado, nas consultas
SELECT
não são necessários bloqueios compartilhados (S), portanto, é mais provável que ocorram deadlocks entre transações que estão a modificar dados. Nos casos em que ocorrem impasses entre várias transações modificando dados, o isolamento instantâneo pode resultar em conflito de atualização em vez de um impasse. Da mesma forma, isso requer que uma das transações tente novamente sua operação.Forçar um plano com o Repositório de Consultas. Você pode achar que uma das consultas no deadlock tem vários planos de execução, e o deadlock só ocorre quando um plano específico é usado. Você pode evitar que o bloqueio ocorra novamente ao forçar um plano no Repositório de Consultas.
Modificar o código Transact-SQL. Talvez seja necessário modificar Transact-SQL para evitar que o deadlock ocorra novamente. A modificação de Transact-SQL deve ser feita com cuidado e as alterações devem ser rigorosamente testadas para garantir que os dados estejam corretos quando as modificações são executadas simultaneamente. Ao reescrever o Transact-SQL, considere:
Ordenar instruções em transações para acederem a objetos na mesma ordem.
Separar as transações em transações menores quando possível.
Usando dicas de consulta, se necessário, para otimizar o desempenho. Você pode aplicar dicas sem alterar o código do aplicativo usando o Repositório de Consultas.
Encontre mais maneiras de minimizar os impasses no guia Deadlocks.
Observação
Em alguns casos, você pode ajustar a prioridade de bloqueio de uma ou mais sessões envolvidas em um impasse se for importante para uma das sessões ser concluída com sucesso sem tentar novamente, ou quando uma das consultas envolvidas no impasse não for crítica e deve ser sempre escolhida como vítima. Embora isso não impeça que o impasse volte a ocorrer, pode reduzir o efeito de impasses futuros.
Encerrar uma sessão XEvents
Você pode sair de uma sessão XEvents coletando informações de deadlock em execução em bancos de dados críticos por longos períodos. Se você usar um destino de arquivo de evento, isso poderá resultar em arquivos grandes se ocorrerem vários deadlocks. Você pode excluir ficheiros de blob do Armazenamento do Azure para um rastreamento ativo, com exceção do ficheiro que está a ser gravado no momento.
Quando pretender remover uma sessão de XEvents, o comando Transact-SQL para eliminar a sessão é o mesmo, independentemente do tipo de destino selecionado.
Para remover uma sessão XEvents, execute o seguinte Transact-SQL. Antes de executar o código, substitua o nome da sessão pelo valor apropriado.
ALTER EVENT SESSION [deadlocks] ON DATABASE
STATE = STOP;
GO
DROP EVENT SESSION [deadlocks] ON DATABASE;
GO
Usar o Gerenciador de Armazenamento do Azure
O Azure Storage Explorer é uma aplicação autónoma que simplifica o trabalho com destinos de ficheiros de eventos armazenados em blobs no Armazenamento do Azure. Você pode usar o Gerenciador de Armazenamento para:
Crie um contêiner de blob para armazenar dados de sessão XEvent.
Obtenha o de assinatura de acesso compartilhado (SAS) para um contêiner de blob.
Conforme mencionado em Recolher gráficos de deadlock no Banco de Dados SQL do Azure com Eventos Estendidos, são necessárias as permissões de leitura, escrita e listagem.
Remova qualquer caractere de
?
à esquerda doQuery string
para usar o valor como segredo ao criar uma credencial com escopo de banco de dados.
Veja e descarregue arquivos de eventos estendidos de um container de blobs.
Baixe o Gerenciador de Armazenamento do Azure.
Conteúdo relacionado
- Compreender e resolver problemas de bloqueio
- Guia de Bloqueio de Transações e Versionamento de Linhas
- Guia de Deadlocks
- DEFINIR O NÍVEL DE ISOLAMENTO DE TRANSAÇÃO
- Base de Dados SQL do Azure: melhorando a otimização de desempenho com ajuste automático
- Forneça um desempenho consistente com o SQL do Azure
- Lógica de repetição para erros transitórios