Detectando e encerrando deadlocks

Um deadlock acontece quando duas ou mais tarefas bloqueiam uma à outra permanentemente, sendo que cada uma tem o bloqueio de um recurso que a outra tarefa está tentando bloquear. O seguinte gráfico apresenta uma exibição de alto nível de um estado de deadlock em que:

  • A tarefa T1 tem um bloqueio no recurso R1 (indicado pela seta de R1 para T1) e solicitou um bloqueio no recurso R2 (indicado pela seta de T1 para R2).

  • A tarefa T2 tem um bloqueio no recurso R2 (indicado pela seta de R2 a T2) e solicitou um bloqueio no recurso R1 (indicado pela seta de T2 a R1).

  • Como nenhuma tarefa pode continuar até que um recurso esteja disponível e nenhum recurso pode ser liberado até que uma tarefa continue, ocorre um estado de deadlock.

Diagrama mostrando tarefas em um estado de deadlock

O Mecanismo de banco de dados do SQL Server detecta ciclos de deadlock automaticamente dentro do SQL Server. O Mecanismo de Banco de Dados escolhe uma das sessões como vítima de deadlock e a transação atual é encerrada com um erro para quebrar o deadlock.

Recursos que podem acarretar deadlock

Cada sessão de usuário pode ter uma ou mais tarefas sendo executadas em seu nome, sendo que cada tarefa pode adquirir ou aguardar para adquirir uma variedade de recursos. Os tipos de recursos a seguir podem causar bloqueio que pode resultar em um deadlock.

  • Bloqueios. A espera para adquirir bloqueios em recursos, como objetos, páginas, linhas, metadados e aplicativos pode causar deadlock. Por exemplo, a transação T1 tem um bloqueio compartilhado (S) na linha r1 e está esperando para obter um bloqueio exclusivo (X) em r2. A transação T2 tem um bloqueio compartilhado (S) na linha r1 e está esperando para obter um bloqueio exclusivo (X) em r1. Isso resulta em um ciclo de bloqueio no qual T1 e T2 esperam que uma libere os recursos bloqueados da outra.

  • Threads de trabalho Uma tarefa em fila aguardando um thread de trabalho pode causar um deadlock. Se a tarefa em fila possuir recursos que estão bloqueando todos os threads de trabalhado, haverá um deadlock. Por exemplo, a sessão S1 inicia uma transação e adquire um bloqueio compartilhado (S) na linha r1 e, depois, fica suspenso. As sessões ativas em execução em todos os threads de trabalhado disponíveis estão tentando adquirir bloqueios exclusivos (X) na linha r1. Como a sessão S1 não pode adquirir um thread de trabalho, ela não pode confirmar a transação e liberar o bloqueio na linha r1. Isso resulta em um deadlock.

  • Memória. Quando solicitações simultâneas estão esperando por concessões de memória que não podem ser satisfeitas com a memória disponível, pode ocorrer um deadlock. Por exemplo, duas consultas simultâneas, Q1 e Q2, são executadas como funções definidas pelo usuário que adquirem 10MB e 20MB de memória, respectivamente. Se cada consulta precisar de 30MB e a memória disponível total for de 20MB, Q1 e Q2 deverão esperar uma pela outra para liberar memória. Isso resulta em um deadlock.

  • Recursos relacionados à execução de consultas paralelas Threads de coordenação, produção ou consumo associados a uma porta de troca podem bloquear um ao outro causando um deadlock, normalmente ao incluir pelo menos um outro processo que não faz parte da consulta paralela. Além disso, quando uma consulta paralela começa a ser executada, o SQL Server determina o grau de paralelismo, ou o número de threads de trabalho, com base na carga de trabalho atual. Se a carga de trabalho do sistema for alterada inesperadamente, por exemplo, quando novas consultas forem executadas no servidor ou o sistema ficar sem threads de trabalho, poderá ocorrer um deadlock.

  • Recursos de vários conjuntos de resultados ativos (MARS). Esses recursos são usados para controlar a intercalação de várias solicitações ativas em MARS (consulte Ambiente de execução em lote e MARS).

    • Recurso do usuário. Quando um thread está esperando por um recurso que é potencialmente controlado por um aplicativo de usuário, o recurso é considerado como externo ou recurso de usuário e é tratado como um bloqueio.

    • Mutex de sessão. As tarefas que estão sendo executadas em uma sessão são intercaladas, ou seja, apenas uma tarefa pode ser executada na sessão em um determinado momento. Antes de a tarefa ser executada, deve ter acesso exclusivo ao mutex de sessão.

    • Mutex de transação. Todas as tarefas que estão sendo executadas em uma transação são intercaladas, ou seja, somente uma tarefa pode ser executada na transação em um determinado momento. Antes da tarefa ser executada, deve ter acesso exclusivo ao mutex de transação.

    Para que uma tarefa seja executada em MARS, ela deve adquirir o mutex da sessão. Se a tarefa estiver sendo executada em uma transação, deverá adquirir o mutex de transação. Isso garante que apenas uma tarefa esteja ativa em um determinado momento, sessão e transação. Quando os mutexes solicitados forem adquiridos, a tarefa poderá ser executada. Quando a tarefa termina, ou está no meio da solicitação, primeiro ela libera o mutex de transação e, depois, o mutex de sessão em ordem reversa de aquisição. Porém, podem ocorrer deadlocks com esses recursos. No exemplo de código a seguir, duas tarefas, solicitação de usuário U1 e solicitação de usuário U2, estão sendo executadas na mesma sessão.

    U1:    Rs1=Command1.Execute("insert sometable EXEC usp_someproc");
    U2:    Rs2=Command2.Execute("select colA from sometable");
    

    O procedimento armazenado que está sendo executado na solicitação U1 adquiriu o mutex de sessão. Se o procedimento armazenado levar muito tempo para ser executado, o Mecanismo de Banco de Dados presumirá que o procedimento armazenado está esperando uma entrada do usuário. A solicitação de usuário U2 está esperando pelo mutex de sessão, enquanto o usuário está esperando pelo conjunto de resultados de U2, e U1 está esperando por um recurso de usuário. Esse estado de deadlock é logicamente ilustrado como:

Diagrama lógico mostrando deadlock de processo do usuário.

Detecção de deadlock

Todos os recursos listados na seção anterior participam do esquema de detecção de deadlock Mecanismo de Banco de Dados. A detecção de deadlock é executada por um thread de monitor de bloqueio que periodicamente inicia uma pesquisa por todas as tarefas em uma instância do Mecanismo de Banco de Dados. Os seguintes pontos descrevem o processo de pesquisa:

  • O intervalo padrão é de 5 segundos.

  • Se o thread de monitor de bloqueio localizar deadlocks, o intervalo de detecção de deadlock diminuirá de 5 segundos para até 100 milissegundos, dependendo da frequência de deadlocks.

  • Se o thread de monitor de bloqueio parar de localizar deadlocks, o Mecanismo de Banco de Dados aumentará os intervalos entre pesquisas para 5 segundos.

  • Se um deadlock tiver sido detectado há pouco tempo, presume-se que os próximos threads que precisarem esperar por um bloqueio estejam entrando no ciclo de deadlock. O primeiro par de esperas de bloqueio, após a detecção de um deadlock, dispara imediatamente uma pesquisa de deadlock em vez de aguardar pelo próximo intervalo de detecção de deadlock. Por exemplo, se o intervalo atual for de 5 segundos, e um deadlock tiver sido detectado há pouco tempo, a próxima espera de bloqueio iniciará o detector de deadlock imediatamente. Se essa espera de bloqueio fizer parte de um deadlock, será detectada imediatamente em vez de durante a próxima pesquisa de deadlock.

Normalmente, o Mecanismo de Banco de Dados executa somente a detecção periódica de deadlock. Como o número de deadlocks encontrado no sistema geralmente é pequeno, a detecção periódica de deadlock ajuda a reduzir a sobrecarga de detecção de deadlock no sistema.

Quando o monitor de bloqueio inicia a pesquisa de deadlock para um determinado thread, ele identifica o recurso em que o thread está esperando. O monitor de bloqueio localiza o proprietário desse recurso em particular e, recursivamente, continua a pesquisa de deadlock para esses threads até encontrar um ciclo. Um ciclo identificado dessa maneira forma um deadlock.

Depois que um deadlock é detectado, o Mecanismo de Banco de Dados encerra esse deadlock escolhendo um dos threads como uma vítima de deadlock. O Mecanismo de Banco de Dados encerra o lote atual que está sendo executado para o thread, reverte a transação da vítima de deadlock e retorna um erro 1205 para o aplicativo. A reversão da transação da vítima de deadlock libera todos os bloqueios mantidos pela transação. Isso permite que as transações dos outros threads sejam desbloqueadas e prossigam. O erro 1205 da vítima de deadlock registra informações sobre os threads e recursos envolvidos em um deadlock no log de erros.

Por padrão, o Mecanismo de Banco de Dados escolhe a sessão que está executando a transação mais fácil de ser revertida como a vítima de deadlock. Alternativamente, um usuário pode especificar a prioridade de sessões em uma situação de deadlock usando a instrução SET DEADLOCK_PRIORITY. O DEADLOCK_PRIORITY pode ser definida como LOW, NORMAL ou HIGH ou, alternativamente, pode ser definido como qualquer valor de inteiro no intervalo (-10 a 10). A prioridade de deadlock assume NORMAL como padrão. Se duas sessões tiverem prioridades de deadlock diferentes, a sessão com a prioridade mais baixa será escolhida como a vítima de deadlock. Se ambas as sessões tiverem a mesma prioridade de deadlock, a sessão com a transação menos dispendiosa para ser revertida será escolhida. Se as sessões envolvidas no ciclo de deadlock tiverem a mesma prioridade de deadlock e o mesmo custo, a vítima será escolhida aleatoriamente.

Ao trabalhar com CLR, o monitor de deadlock detecta automaticamente um deadlock para recursos de sincronização (monitores, bloqueio de leitura/gravação ou junção de thread) acessados dentro dos procedimentos gerenciados. Entretanto, o deadlock é resolvido ao se lançar uma exceção no procedimento selecionado como a vítima de deadlock. É importante entender que a exceção não libera automaticamente os recursos pertencentes à vítima; os recursos devem ser liberados explicitamente. Em consonância com o comportamento de exceção, a exceção usada para identificar uma vítima de deadlock pode ser capturada e ignorada.

Ferramentas de informações sobre deadlock

Para exibir informações sobre deadlock, o Mecanismo de Banco de Dados fornece ferramentas de monitoração na forma de dois sinalizadores de rastreamento e o evento gráfico de deadlock no SQL Server Profiler.

Sinalizadores de rastreamento 1204 e 1222

Quando acontecem deadlocks, os sinalizadores de rastreamento 1204 e 1222 retornam informações captadas no log de erros SQL Server 2005. O sinalizador de rastreamento 1204 relata informações de deadlock formatadas por cada nó envolvido no deadlock. O sinalizador de rastreamento 1222 formata informações de deadlock, primeiro por processos e depois por recursos. É possível permitir que ambos os sinalizadores de rastreamento obtenham duas representações do mesmo evento de deadlock.

Além de definir as propriedades dos sinalizadores de rastreamento 1204 e 1222, a tabela a seguir também mostra suas semelhanças e diferenças.

Propriedade

Sinalizadores de rastreamento 1204 e 1222

Apenas sinalizador de rastreamento 1204

Apenas sinalizador de rastreamento 1222

Formato de saída

A saída captada no log de erros do SQL Server 2005.

Focado nos nós envolvidos no deadlock. Cada nó tem uma seção dedicada e a seção final descreve a vítima de deadlock.

Retorna informações em um formato parecido com XML que não está em conformidade com uma definição de esquema XML (XSD). O formato tem três seções principais. A primeira seção declara a vítima de deadlock. A segunda seção descreve cada processo envolvido no deadlock. A terceira seção descreve os recursos que são sinônimos com os nós no sinalizador de rastreamento 1204.

Identificando atributos

SPID:<x> ECID:<x>. Identifica o thread de ID de processo de sistema em casos de processos paralelos. A entrada SPID:<x> ECID:0, em que <x> é substituído pelo valor SPID, representa o thread principal. A entrada SPID:<x> ECID:<y>, em que <x> é substituído pelo valor SPID e <y> é maior que 0, representa os sub-threads para o mesmo SPID.

BatchID (sbid para o sinalizador de rastreamento 1222). Identifica o lote do qual a execução de código está solicitando ou mantendo um bloqueio. Quando vários conjuntos de resultados ativos (MARS) estão desabilitados, o valor BatchID é 0. Quando MARS estão habilitados, o valor de lotes ativos é 1 para n. Se não houver lotes ativos na sessão, BatchID será 0.

Mode. Especifica o tipo de bloqueio de um determinado recurso que é solicitado, concedido ou aguardado por um thread. O Mode pode ser IS (Tentativa Compartilhada), S (Compartilhado), U (Atualização), IX (Exclusivo da Tentativa), SIX (Exclusivo da Tentativa Compartilhado) e X (Exclusivo). Para obter mais informações, consulte Modos de bloqueio.

Line # (line para o sinalizador de rastreamento 1222). Lista o número de linha no lote atual de instruções que estava sendo executado quando o deadlock aconteceu.

Input Buf (inputbuf para o sinalizador de rastreamento 1222). Lista todas as instruções no lote atual.

Node. Representa o número de entrada na cadeia de deadlock.

Lists. O proprietário do bloqueio pode fazer parte destas listas:

  • Grant List. Enumera os proprietários atuais do recurso.

  • Convert List. Enumera os proprietários atuais que estão tentando converter seus bloqueios em um nível mais alto.

  • Wait List. Enumera as novas solicitações de bloqueio do recurso.

Statement Type. Descreve o tipo de instrução DML (SELECT, INSERT, UPDATE ou DELETE) em que os threads têm permissões.

Victim Resource Owner. Especifica o thread participante que o SQL Server escolhe como a vítima para quebrar o ciclo de deadlock. O thread escolhido e todos os sub-threads existentes são encerrados.

Next Branch. Representa os dois ou mais sub-threads do mesmo SPID envolvidos no ciclo de deadlock.

deadlock victim. Representa o endereço de memória físico da tarefa (consulte sys.dm_os_tasks (Transact-SQL)) selecionada como vítima de deadlock. Pode ser 0 (zero) no caso de um deadlock não resolvido. Uma tarefa que está sendo revertida não pode ser escolhida como vítima de deadlock.

executionstack. Representa o código Transact-SQL que está sendo executado no momento em que o deadlock ocorre.

priority. Representa a prioridade do deadlock. Em determinados casos, o Mecanismo de Banco de Dados pode optar por alterar a prioridade de deadlock para uma duração curta para obter uma simultaneidade melhor.

logused. Espaço de log usado pela tarefa.

owner id. A ID da transação que tem controle da solicitação.

status O estado da tarefa. Pode ser um dos seguintes valores:

  • pending. Esperando por um thread de trabalho.

  • runnable. Pronto para ser executado, mas esperando por um quantum.

  • running. Atualmente em execução no agendador.

  • suspended. A execução está suspensa.

  • done. A tarefa foi concluída.

  • spinloop. Esperando que um spinlock seja liberado.

waitresource. O recurso requerido pela tarefa.

waittime. O tempo em milissegundos de espera pelo recurso.

schedulerid. O agendador associado à essa tarefa. Consulte sys.dm_os_schedulers (Transact-SQL).

hostname. O nome da estação de trabalho.

isolationlevel. O nível de isolamento da transação atual.

Xactid. A ID da transação que tem controle da solicitação.

currentdb. A ID do banco de dados.

lastbatchstarted. A última vez em que um processo cliente iniciou uma execução em lote.

lastbatchcompleted. A última vez em que um processo cliente concluiu uma execução em lote.

clientoption1 e clientoption2. Defina as opções nessa conexão de cliente. Esse é um bitmask que inclui informações sobre opções normalmente controladas por instruções SET, como SET NOCOUNT e SET XACTABORT.

associatedObjectId. Representa a ID de HoBT (heap ou árvore B).

Atributos do recurso

RID. Identifica a única linha dentro de uma tabela na qual um bloqueio é mantido ou solicitado. O RID é representado como RID: db_id:file_id:page_no:row_no. Por exemplo, RID: 6:1:20789:0.

OBJECT. Identifica a tabela na qual um bloqueio é mantido ou solicitado. OBJECT é representado como OBJECT: db_id:object_id. Por exemplo, TAB: 6:2009058193.

KEY. Identifica o intervalo de chave dentro de um índice em que um bloqueio é mantido ou solicitado. KEY é representado como KEY: db_id:hobt_id (index key hash value). Por exemplo, KEY: 6:72057594057457664 (350007a4d329).

PAG. Identifica o recurso de página no qual um bloqueio é mantido ou solicitado. O PAG é representado como PAG: db_id:file_id:page_no. Por exemplo, PAG: 6:1:20789.

EXT. Identifica a estrutura de extensão. EXT é representado como EXT: db_id:file_id:extent_no. Por exemplo, EXT: 6:1:9.

DB. Identifica o bloqueio de banco de dados. DB é representado de um dos seguintes modos:

  • DB: db_id

  • DB: db_id[BULK-OP-DB], que identifica o bloqueio de banco de dados feito pelo banco de dados de backup.

  • DB: db_id[BULK-OP-DB], que identifica o bloqueio feito pelo log de backup daquele banco de dados específico.

APP. Identifica o bloqueio feito por um recurso de aplicativo. APP é representado por APP: lock_resource. Por exemplo, APP: Formf370f478.

METADATA. Representa os recursos de metadados envolvidos em um deadlock. Como METADATA tem muitos sub-recursos, o valor retornado depende do sub-recurso envolvido no deadlock. Por exemplo, METADATA.USER_TYPE retorna user_type_id =<integer_value>. Para obter mais informações sobre os recursos e sub-recursos de METADATA, consulte sys.dm_tran_locks (Transact-SQL).

HOBT. Representa um heap ou árvore B envolvida em um deadlock.

Nenhum exclusivo para esse sinalizador de rastreamento.

Nenhum exclusivo para esse sinalizador de rastreamento.

Exemplo do sinalizador de rastreamento 1204

O exemplo a seguir mostra a saída quando o sinalizador de rastreamento 1204 é ativado. Neste caso, a tabela em Node 1 é um heap sem índices, e a tabela em Node 2 é um heap com índices não clusterizados. A chave de índice em Node 2 é atualizada quando o deadlock ocorre.

Deadlock encountered .... Printing deadlock information
Wait-for graph

Node:1

RID: 6:1:20789:0               CleanCnt:3 Mode:X Flags: 0x2
 Grant List 0:
   Owner:0x0315D6A0 Mode: X        
     Flg:0x0 Ref:0 Life:02000000 SPID:55 ECID:0 XactLockInfo: 0x04D9E27C
   SPID: 55 ECID: 0 Statement Type: UPDATE Line #: 6
   Input Buf: Language Event: 
BEGIN TRANSACTION
   EXEC usp_p2
 Requested By: 
   ResType:LockOwner Stype:'OR'Xdes:0x03A3DAD0 
     Mode: U SPID:54 BatchID:0 ECID:0 TaskProxy:(0x04976374) Value:0x315d200 Cost:(0/868)

Node:2

KEY: 6:72057594057457664 (350007a4d329) CleanCnt:2 Mode:X Flags: 0x0
 Grant List 0:
   Owner:0x0315D140 Mode: X        
     Flg:0x0 Ref:0 Life:02000000 SPID:54 ECID:0 XactLockInfo: 0x03A3DAF4
   SPID: 54 ECID: 0 Statement Type: UPDATE Line #: 6
   Input Buf: Language Event: 
     BEGIN TRANSACTION
       EXEC usp_p1
 Requested By: 
   ResType:LockOwner Stype:'OR'Xdes:0x04D9E258 
     Mode: U SPID:55 BatchID:0 ECID:0 TaskProxy:(0x0475E374) Value:0x315d4a0 Cost:(0/380)

Victim Resource Owner:
 ResType:LockOwner Stype:'OR'Xdes:0x04D9E258 
     Mode: U SPID:55 BatchID:0 ECID:0 TaskProxy:(0x0475E374) Value:0x315d4a0 Cost:(0/380)

Exemplo do sinalizador de rastreamento 1222

O exemplo a seguir mostra a saída quando o sinalizador de rastreamento 1222 é ativado. Neste caso, uma tabela é um heap sem índices, e a outra tabela é um heap com um índice não clusterizado. Na segunda tabela, a chave de índice é atualizada quando o deadlock ocorre.

deadlock-list
 deadlock victim=process689978
  process-list
   process id=process6891f8 taskpriority=0 logused=868 
   waitresource=RID: 6:1:20789:0 waittime=1359 ownerId=310444 
   transactionname=user_transaction 
   lasttranstarted=2005-09-05T11:22:42.733 XDES=0x3a3dad0 
   lockMode=U schedulerid=1 kpid=1952 status=suspended spid=54 
   sbid=0 ecid=0 priority=0 transcount=2 
   lastbatchstarted=2005-09-05T11:22:42.733 
   lastbatchcompleted=2005-09-05T11:22:42.733 
   clientapp=Microsoft SQL Server Management Studio - Query 
   hostname=TEST_SERVER hostpid=2216 loginname=DOMAIN\user 
   isolationlevel=read committed (2) xactid=310444 currentdb=6 
   lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200
    executionStack
     frame procname=AdventureWorks2008R2.dbo.usp_p1 line=6 stmtstart=202 
     sqlhandle=0x0300060013e6446b027cbb00c69600000100000000000000
     UPDATE T2 SET COL1 = 3 WHERE COL1 = 1;     
     frame procname=adhoc line=3 stmtstart=44 
     sqlhandle=0x01000600856aa70f503b8104000000000000000000000000
     EXEC usp_p1     
    inputbuf
      BEGIN TRANSACTION
       EXEC usp_p1
   process id=process689978 taskpriority=0 logused=380 
   waitresource=KEY: 6:72057594057457664 (350007a4d329)   
   waittime=5015 ownerId=310462 transactionname=user_transaction 
   lasttranstarted=2005-09-05T11:22:44.077 XDES=0x4d9e258 lockMode=U 
   schedulerid=1 kpid=3024 status=suspended spid=55 sbid=0 ecid=0 
   priority=0 transcount=2 lastbatchstarted=2005-09-05T11:22:44.077 
   lastbatchcompleted=2005-09-05T11:22:44.077 
   clientapp=Microsoft SQL Server Management Studio - Query 
   hostname=TEST_SERVER hostpid=2216 loginname=DOMAIN\user 
   isolationlevel=read committed (2) xactid=310462 currentdb=6 
   lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200
    executionStack
     frame procname=AdventureWorks2008R2.dbo.usp_p2 line=6 stmtstart=200 
     sqlhandle=0x030006004c0a396c027cbb00c69600000100000000000000
     UPDATE T1 SET COL1 = 4 WHERE COL1 = 1;     
     frame procname=adhoc line=3 stmtstart=44 
     sqlhandle=0x01000600d688e709b85f8904000000000000000000000000
     EXEC usp_p2     
    inputbuf
      BEGIN TRANSACTION
        EXEC usp_p2    
  resource-list
   ridlock fileid=1 pageid=20789 dbid=6 objectname=AdventureWorks2008R2.dbo.T2 
   id=lock3136940 mode=X associatedObjectId=72057594057392128
    owner-list
     owner id=process689978 mode=X
    waiter-list
     waiter id=process6891f8 mode=U requestType=wait
   keylock hobtid=72057594057457664 dbid=6 objectname=AdventureWorks2008R2.dbo.T1 
   indexname=nci_T1_COL1 id=lock3136fc0 mode=X 
   associatedObjectId=72057594057457664
    owner-list
     owner id=process6891f8 mode=X
    waiter-list
     waiter id=process689978 mode=U requestType=wait

Evento gráfico de deadlock de designer de perfil

Este é um evento do SQL Server Profiler que apresenta uma representação gráfica das tarefas e recursos envolvidos em um deadlock. O exemplo a seguir mostra a saída do SQL Server Profiler quando o evento de gráfico de deadlock é ativado.

Diagrama de fluxo lógico mostrando deadlock de processo do usuário.

Para obter mais informações sobre a execução do gráfico de deadlock do SQL Server Profiler, consulte Analisando deadlocks com o SQL Server Profiler.