Processo de bloqueio vazio no relatório de processo bloqueado

28

Estou coletando relatórios de processos bloqueados usando Eventos Estendidos e, por algum motivo, em alguns relatórios, o blocking-processnó está vazio. Este é o xml completo:

<blocked-process-report monitorLoop="383674">
 <blocked-process>
  <process id="processa7bd5b868" taskpriority="0" logused="106108620" waitresource="KEY: 6:72057613454278656 (8a2f7bc2cd41)" waittime="25343" ownerId="1051989016" transactionname="user_transaction" lasttranstarted="2017-03-20T09:30:38.657" XDES="0x21f382d9c8" lockMode="X" schedulerid="7" kpid="15316" status="suspended" spid="252" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-03-20T09:39:15.853" lastbatchcompleted="2017-03-20T09:39:15.850" lastattention="1900-01-01T00:00:00.850" clientapp="Microsoft Dynamics AX" hostname="***" hostpid="1348" loginname="***" isolationlevel="read committed (2)" xactid="1051989016" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
   <executionStack>
    <frame line="1" stmtstart="40" sqlhandle="0x02000000f7def225b0edaecd8744b453ce09bdcff9b291f50000000000000000000000000000000000000000" />
    <frame line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" />
   </executionStack>
   <inputbuf>
(@P1 bigint,@P2 int)DELETE FROM DIMENSIONFOCUSUNPROCESSEDTRANSACTIONS WHERE ((PARTITION=5637144576) AND ((FOCUSDIMENSIONHIERARCHY=@P1) AND (STATE=@P2)))   </inputbuf>
  </process>
 </blocked-process>
 <blocking-process>
  <process />
 </blocking-process>
</blocked-process-report>

A definição de índice para o índice ao qual este hobt_id pertence é

CREATE UNIQUE CLUSTERED INDEX [I_7402FOCUSDIMENSIONHIERARCHYIDX] ON [dbo].[DIMENSIONFOCUSUNPROCESSEDTRANSACTIONS]
(
    [PARTITION] ASC,
    [FOCUSDIMENSIONHIERARCHY] ASC,
    [STATE] ASC,
    [GENERALJOURNALENTRY] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO

Não há particionamento envolvido, esta é a definição de tabela:

CREATE TABLE [dbo].[DIMENSIONFOCUSUNPROCESSEDTRANSACTIONS](
    [FOCUSDIMENSIONHIERARCHY] [bigint] NOT NULL DEFAULT ((0)),
    [GENERALJOURNALENTRY] [bigint] NOT NULL DEFAULT ((0)),
    [STATE] [int] NOT NULL DEFAULT ((0)),
    [RECVERSION] [int] NOT NULL DEFAULT ((1)),
    [PARTITION] [bigint] NOT NULL DEFAULT ((5637144576.)),
    [RECID] [bigint] NOT NULL,
 CONSTRAINT [I_7402RECID] PRIMARY KEY NONCLUSTERED 
(
    [RECID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[DIMENSIONFOCUSUNPROCESSEDTRANSACTIONS]  WITH CHECK ADD CHECK  (([RECID]<>(0)))
GO

Não há gatilhos ou chaves estrangeiras definidos em nenhuma das tabelas no banco de dados inteiro.

A compilação exata do SQL Server é:

Microsoft SQL Server 2012 (SP3-CU4) (KB3165264) - 11.0.6540.0 (X64)
23 de junho de 2016 17:45:11 Copyright (c) Microsoft Corporation Enterprise Edition: licenciamento baseado em núcleo (64 bits) no Windows NT 6.3 ( Compilação 14393:) (Hypervisor)

Os eventos estendidos são bastante simples, apenas registrando os relatórios de processo bloqueados:

CREATE EVENT SESSION [Dynperf_Blocking_Data] ON SERVER 
ADD EVENT sqlserver.blocked_process_report(
    ACTION(package0.collect_system_time,sqlserver.client_hostname,sqlserver.context_info)),
ADD EVENT sqlserver.lock_escalation(
    ACTION(package0.collect_system_time,sqlserver.client_hostname,sqlserver.context_info)),
ADD EVENT sqlserver.xml_deadlock_report(
    ACTION(package0.collect_system_time,sqlserver.client_hostname,sqlserver.context_info)) 
ADD TARGET package0.event_file(SET filename=N'F:\SQLTrace\Dynamics_Blocking.xel',max_file_size=(100),max_rollover_files=(10))
WITH (MAX_MEMORY=32768 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=5 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=PER_NODE,TRACK_CAUSALITY=ON,STARTUP_STATE=ON)
GO

O banco de dados é configurado em Isolamento de Captura Instantânea Confirmada de Leitura e o grau máximo de paralelismo é definido como 1. Esta é a configuração do servidor:

+------------------------------------+-------+
|                name                | value |
+------------------------------------+-------+
| access check cache bucket count    |     0 |
| access check cache quota           |     0 |
| Ad Hoc Distributed Queries         |     0 |
| affinity I/O mask                  |     0 |
| affinity mask                      |     0 |
| affinity64 I/O mask                |     0 |
| affinity64 mask                    |     0 |
| Agent XPs                          |     1 |
| allow updates                      |     0 |
| backup compression default         |     1 |
| blocked process threshold (s)      |     2 |
| c2 audit mode                      |     0 |
| clr enabled                        |     0 |
| common criteria compliance enabled |     0 |
| contained database authentication  |     0 |
| cost threshold for parallelism     |     5 |
| cross db ownership chaining        |     0 |
| cursor threshold                   |    -1 |
| Database Mail XPs                  |     1 |
| default full-text language         |  1033 |
| default language                   |     0 |
| default trace enabled              |     1 |
| disallow results from triggers     |     0 |
| EKM provider enabled               |     0 |
| filestream access level            |     0 |
| fill factor (%)                    |     0 |
| ft crawl bandwidth (max)           |   100 |
| ft crawl bandwidth (min)           |     0 |
| ft notify bandwidth (max)          |   100 |
| ft notify bandwidth (min)          |     0 |
| index create memory (KB)           |     0 |
| in-doubt xact resolution           |     0 |
| lightweight pooling                |     0 |
| locks                              |     0 |
| max degree of parallelism          |     1 |
| max full-text crawl range          |     4 |
| max server memory (MB)             | 65536 |
| max text repl size (B)             | 65536 |
| max worker threads                 |     0 |
| media retention                    |     0 |
| min memory per query (KB)          |  1024 |
| min server memory (MB)             |     0 |
| nested triggers                    |     1 |
| network packet size (B)            |  4096 |
| Ole Automation Procedures          |     0 |
| open objects                       |     0 |
| optimize for ad hoc workloads      |     1 |
| PH timeout (s)                     |    60 |
| precompute rank                    |     0 |
| priority boost                     |     0 |
| query governor cost limit          |     0 |
| query wait (s)                     |    -1 |
| recovery interval (min)            |     0 |
| remote access                      |     1 |
| remote admin connections           |     0 |
| remote login timeout (s)           |    10 |
| remote proc trans                  |     0 |
| remote query timeout (s)           |   600 |
| Replication XPs                    |     0 |
| scan for startup procs             |     1 |
| server trigger recursion           |     1 |
| set working set size               |     0 |
| show advanced options              |     1 |
| SMO and DMO XPs                    |     1 |
| transform noise words              |     0 |
| two digit year cutoff              |  2049 |
| user connections                   |     0 |
| user options                       |     0 |
| xp_cmdshell                        |     0 |
+------------------------------------+-------+

Executei um rastreamento do lado do servidor por um tempo e recebo os mesmos nós vazios em um arquivo de rastreamento que os eventos estendidos.
Este relatório de processo bloqueado foi capturado usando um rastreamento do lado do servidor em outro servidor também executando o Dynamics AX, portanto, não é específico para este servidor ou compilação.

<blocked-process-report monitorLoop="1327922">
 <blocked-process>
  <process id="processbd9839848" taskpriority="0" logused="1044668" waitresource="KEY: 5:72057597098328064 (1d7966fe609a)" waittime="316928" ownerId="3415555263" transactionname="user_transaction" lasttranstarted="2017-03-27T07:59:29.290" XDES="0x1c1c0c3b0" lockMode="U" schedulerid="3" kpid="25236" status="suspended" spid="165" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-03-27T07:59:47.873" lastbatchcompleted="2017-03-27T07:59:47.873" lastattention="2017-03-27T07:58:01.490" clientapp="Microsoft Dynamics AX" hostname="***" hostpid="11072" loginname="***" isolationlevel="read committed (2)" xactid="3415555263" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
   <executionStack>
    <frame line="1" stmtstart="236" stmtend="676" sqlhandle="0x020000004d6830193d42a167edd195c201f40bb772e9ece20000000000000000000000000000000000000000"/>
   </executionStack>
   <inputbuf>
(@P1 numeric(32,16),@P2 int,@P3 bigint,@P4 nvarchar(5),@P5 nvarchar(36),@P6 int,@P7 numeric(32,16),@P8 bigint,@P9 int)UPDATE PRODCALCTRANS SET REALCOSTAMOUNT=@P1,RECVERSION=@P2 WHERE (((((((PARTITION=@P3) AND (DATAAREAID=@P4)) AND (COLLECTREFPRODID=@P5)) AND (COLLECTREFLEVEL=@P6)) AND (LINENUM=@P7)) AND (RECID=@P8)) AND (RECVERSION=@P9))   </inputbuf>
  </process>
 </blocked-process>
 <blocking-process>
  <process/>
 </blocking-process>
</blocked-process-report>

Alguém tem uma explicação para esses relatórios? O que está bloqueando a consulta?

Existe alguma maneira de descobrir o que estava acontecendo se eu estiver olhando os relatórios depois que os bloqueios desaparecerem?

Uma coisa que pode ser útil adicionar é que essas consultas são executadas via sp_cursorprepareesp_cursorexecute

Até agora não consegui reproduzi-lo, parece acontecer aleatoriamente, mas com muita frequência.

Isso acontece em várias instâncias (de diferentes compilações) e em várias tabelas / consultas, todas relacionadas ao Dynamics AX.

Não há nenhum índice ou outros trabalhos de manutenção de banco de dados ocorrendo em segundo plano no momento.

Usando o código fornecido na resposta por srutzky , consegui capturar alguns logs relacionados a este relatório de processo bloqueado:

<blocked-process-report monitorLoop="1621637">
 <blocked-process>
  <process id="processd06909c28" taskpriority="0" logused="0" waitresource="KEY: 5:72057597585719296 (d2d87c26d920)" waittime="78785" ownerId="4436575948" transactionname="user_transaction" lasttranstarted="2017-04-13T07:39:17.590" XDES="0x3219d034e0" lockMode="U" schedulerid="3" kpid="133792" status="suspended" spid="106" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-04-13T07:39:17.657" lastbatchcompleted="2017-04-13T07:39:17.657" lastattention="1900-01-01T00:00:00.657" clientapp="Microsoft Dynamics AX" hostname="****" hostpid="11800" loginname="****" isolationlevel="read committed (2)" xactid="4436575948" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
   <executionStack>
    <frame line="1" stmtstart="72" stmtend="256" sqlhandle="0x0200000076a6a92ab1256af09321b056ab243f187342f9960000000000000000000000000000000000000000"/>
    <frame line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"/>
   </executionStack>
   <inputbuf>
(@P1 int,@P2 int,@P3 bigint,@P4 int)UPDATE PRODROUTEJOB SET JOBSTATUS=@P1,RECVERSION=@P2 WHERE ((RECID=@P3) AND (RECVERSION=@P4))   </inputbuf>
  </process>
 </blocked-process>
 <blocking-process>
  <process/>
 </blocking-process>
</blocked-process-report>

Isso é encontrado nas tabelas de log do mesmo recurso nessa época: Gist por causa do limite de caracteres

Uma investigação mais aprofundada mostra que, imediatamente antes e depois do relatório com um processo de bloqueio vazio, tenho relatórios para o mesmo recurso que possui nós do processo de bloqueio:

<blocked-process-report monitorLoop="1621636">
 <blocked-process>
  <process id="processd06909c28" taskpriority="0" logused="0" waitresource="KEY: 5:72057597585719296 (d2d87c26d920)" waittime="73765" ownerId="4436575948" transactionname="user_transaction" lasttranstarted="2017-04-13T07:39:17.590" XDES="0x3219d034e0" lockMode="U" schedulerid="3" kpid="133792" status="suspended" spid="106" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-04-13T07:39:17.657" lastbatchcompleted="2017-04-13T07:39:17.657" lastattention="1900-01-01T00:00:00.657" clientapp="Microsoft Dynamics AX" hostname="***" hostpid="11800" loginname="***" isolationlevel="read committed (2)" xactid="4436575948" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
   <executionStack>
    <frame line="1" stmtstart="72" stmtend="256" sqlhandle="0x0200000076a6a92ab1256af09321b056ab243f187342f9960000000000000000000000000000000000000000"/>
    <frame line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"/>
   </executionStack>
   <inputbuf>
(@P1 int,@P2 int,@P3 bigint,@P4 int)UPDATE PRODROUTEJOB SET JOBSTATUS=@P1,RECVERSION=@P2 WHERE ((RECID=@P3) AND (RECVERSION=@P4))   </inputbuf>
  </process>
 </blocked-process>
 <blocking-process>
  <process status="sleeping" spid="105" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2017-04-13T07:40:31.417" lastbatchcompleted="2017-04-13T07:40:31.423" lastattention="1900-01-01T00:00:00.423" clientapp="Microsoft Dynamics AX" hostname="**" hostpid="11800" loginname="**" isolationlevel="read committed (2)" xactid="4436165115" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
   <executionStack/>
   <inputbuf>
(@P1 bigint,@P2 nvarchar(5),@P3 bigint,@P4 bigint,@P5 nvarchar(11),@P6 int,@P7 nvarchar(21),@P8 datetime2)SELECT T1.REGDATETIME,T1.REGDATETIMETZID,T1.WORKERPILOT,T1.WORKER,T1.WRKCTRIDPILOT,T1.REGTYPE,T1.PROFILEDATE,T1.JOBID,T1.JOBIDABS,T1.MATCHRECIDSTARTSTOP,T1.JOBACTIVE,T1.RESNO,T1.STARTITEMS,T1.GOODITEMS,T1.SCRAPITEMS,T1.FINISHEDCODE,T1.TMPGOODITEMS,T1.TMPSCRAPITEMS,T1.SYSMRPUPDATEREQUEST,T1.ERROR,T1.ERRORTXT,T1.TMPSTARTITEMS,T1.AUTOSTAMP,T1.ERRORSPECIFICATION,T1.COSTCATEGORY,T1.ONCALLACTIVITY,T1.TERMINALID,T1.PDSCWGOODITEMS,T1.PDSCWSCRAPITEMS,T1.PDSCWSTARTITEMS,T1.RETAILTERMINALID,T1.MODIFIEDDATETIME,T1.RECVERSION,T1.PARTITION,T1.RECID FROM JMGTERMREG T1 WHERE (((PARTITION=@P1) AND (DATAAREAID=@P2)) AND (((((WORKER=@P3) OR ((WORKER=@P4) AND (WRKCTRIDPILOT=@P5))) AND (REGTYPE=@P6)) AND (JOBID=@P7)) AND (REGDATETIME&gt;=@P8))) ORDER BY T1.REGDATETIME   </inputbuf>
  </process>
 </blocking-process>
</blocked-process-report>

Usando o novo script fornecido por srutzky, novos dados foram coletados. É postado no github por causa do tamanho máximo da postagem.

Como os dados postados originalmente não tinham os dois IDs de sessão, alguns novos dados foram postados no github novamente

Novos dados, incluindo as conexões no github

Tom V - Equipe Monica
fonte

Respostas:

6

Não posso testar essa teoria no momento, mas com base nos dados de captura mais recentes publicados no GitHub , eu diria que o motivo pelo qual o <process>nó está vazio é que exige uma solicitação em execução no momento (muitos dos atributos são encontrados em sys.dm_exec_requestse não dentro sys.dm_exec_sessions) e sem uma solicitação em execução no momento, ela não pode relatar nenhum detalhe, semelhante a como fazer uma INNER JOINintermediação sys.dm_exec_requestse sys.dm_exec_sessionsexcluirá linhas em que uma Sessão está ativa, mas está ociosa devido a nenhuma solicitação atual.

Observando o conjunto de dados superior ( monitorLoopvalores: 1748823, 1748824, 1748825 e 1748827), podemos ver o seguinte:

  • o idde blocked-processé o mesmo em cada caso: process2552c1fc28 , e o único atributo que é diferente é o waittime(compreensivelmente).
  • os atributos dos blocking-processnós mostram diferenças em ambos lastbatchstartedelastbatchcompleted
  • os atributos dos blocking-processnós mostram valores idênticos para spidexactid

Então, como o SessionID e o TransactionID do processo de bloqueio podem ser os mesmos em 4 lotes de consultas diferentes? Fácil, uma transação explícita foi iniciada e esses lotes foram executados. E como esses lotes são separados, há um tempo entre eles serem enviados e, nesse momento, não há uma solicitação atual, portanto não há informações do processo a serem exibidas (mas a sessão e a transação ainda estão lá).

Para fazer pesquisas adicionais sobre isso, você pode capturar informações úteis de sys.dm_exec_requestse sys.dm_tran_lockscolocando o seguinte T-SQL em uma Etapa da Tarefa "Script de Transação-SQL (T-SQL)" SQL Server Agent, definindo o "Banco de Dados" como o aquele que você está pesquisando (nesse caso, é aquele com um ID 6) e agendando esse trabalho para ser executado a cada 10 segundos. O T-SQL abaixo criará as duas tabelas no mesmo banco de dados se elas não existirem e, em seguida, preencherá a tabela "Solicitações" se alguma solicitação estiver bloqueando a si mesma ou se uma operação de Exclusão ou Atualização estiver sendo bloqueada. . Se algum pedido for encontrado, ele tentará capturar:

  • Informações de Sessão e Solicitação sobre o processo de bloqueio (esta parte não pressupõe que haja uma Solicitação ativa, portanto, o RIGHT JOIN pelo menos para obter as informações da sessão)
  • Informações de conexão para os processos bloqueados e (espero) de bloqueio.
  • os bloqueios atuais para esses mesmos session_id (lembre-se de que as informações de bloqueio não são garantida a ser 100% exato como essa informação pode mudar no tempo entre essas duas declarações de execução; bom o suficiente ainda, a informação é muitas vezes suficiente para ser vale a pena capturar). Esta seção está atualmente comentada.

Etapa da tarefa T-SQL do SQL Server Agent:

-- !! Remember to set the "Database" for the T-SQL Job Step to
--    the DB that has database_id = 6 !!
SET NOCOUNT ON;
IF (OBJECT_ID(N'dbo.tmpBlockingResearch_Requests') IS NULL)
BEGIN
  -- Create requests capture table
  SELECT SYSDATETIME() AS [CaptureTime], req.*,
         ses.login_time, ses.[host_name], ses.[program_name], ses.host_process_id,
         ses.client_version, ses.client_interface_name, ses.security_id,
         ses.login_name, ses.nt_domain, ses.nt_user_name, ses.memory_usage,
         ses.total_scheduled_time, ses.endpoint_id, ses.last_request_start_time,
         ses.last_request_end_time, ses.is_user_process, ses.original_security_id,
         ses.original_login_name, ses.last_successful_logon, ses.last_unsuccessful_logon,
         ses.unsuccessful_logons, ses.authenticating_database_id
  INTO   dbo.tmpBlockingResearch_Requests
  FROM   sys.dm_exec_requests req
  INNER JOIN sys.dm_exec_sessions ses
          ON ses.[session_id] = req.[session_id]
  WHERE  1 = 0;
END;

IF (OBJECT_ID(N'dbo.tmpBlockingResearch_Connections') IS NULL)
BEGIN
  -- Create connections capture table
  SELECT SYSDATETIME() AS [CaptureTime], con.*
  INTO   dbo.tmpBlockingResearch_Connections
  FROM   sys.dm_exec_connections con
  WHERE  1 = 0;
END;

IF (OBJECT_ID(N'dbo.tmpBlockingResearch_Locks') IS NULL)
BEGIN
  -- Create locks capture table
  SELECT SYSDATETIME() AS [CaptureTime], loc.*
  INTO   dbo.tmpBlockingResearch_Locks
  FROM   sys.dm_tran_locks loc
  WHERE  1 = 0;
END;
---------------------------------
DECLARE @SessionIDs TABLE (SessionID SMALLINT NOT NULL,
                           BlockingSessionID SMALLINT NOT NULL);

INSERT INTO dbo.tmpBlockingResearch_Requests
OUTPUT inserted.[session_id], inserted.[blocking_session_id]
INTO   @SessionIDs ([SessionID], [BlockingSessionID])
  SELECT SYSDATETIME() AS [CaptureTime], req.*,
         ses.login_time, ses.[host_name], ses.[program_name], ses.host_process_id,
         ses.client_version, ses.client_interface_name, ses.security_id,
         ses.login_name, ses.nt_domain, ses.nt_user_name, ses.memory_usage,
         ses.total_scheduled_time, ses.endpoint_id, ses.last_request_start_time,
         ses.last_request_end_time, ses.is_user_process, ses.original_security_id,
         ses.original_login_name, ses.last_successful_logon, ses.last_unsuccessful_logon,
         ses.unsuccessful_logons, ses.authenticating_database_id
  FROM   sys.dm_exec_requests req
  INNER JOIN sys.dm_exec_sessions ses
          ON ses.[session_id] = req.[session_id]
  WHERE ses.[is_user_process] = 1
  AND   req.[database_id] = DB_ID()
  AND   (
          req.blocking_session_id IN (req.[session_id], -2, -3, -4)
    OR   (req.[command] IN (N'DELETE', N'UPDATE') AND req.[blocking_session_id] > 0)
        );

-- Get at least session info, if not also request info, on blocking process
INSERT INTO dbo.tmpBlockingResearch_Requests
  SELECT SYSDATETIME() AS [CaptureTime], req.*,
         ses.login_time, ses.[host_name], ses.[program_name], ses.host_process_id,
         ses.client_version, ses.client_interface_name, ses.security_id,
         ses.login_name, ses.nt_domain, ses.nt_user_name, ses.memory_usage,
         ses.total_scheduled_time, ses.endpoint_id, ses.last_request_start_time,
         ses.last_request_end_time, ses.is_user_process, ses.original_security_id,
         ses.original_login_name, ses.last_successful_logon, ses.last_unsuccessful_logon,
         ses.unsuccessful_logons, ses.authenticating_database_id
  FROM   sys.dm_exec_requests req
  RIGHT JOIN sys.dm_exec_sessions ses
          ON ses.[session_id] = req.[session_id]
  WHERE ses.[session_id] IN (SELECT DISTINCT [BlockingSessionID] FROM @SessionIDs);

-- If any rows are captured this time, try to capture their connection info
INSERT INTO dbo.tmpBlockingResearch_Connections
  SELECT SYSDATETIME() AS [CaptureTime], con.*
  FROM   sys.dm_exec_connections con
  WHERE  con.[session_id] IN (
                              SELECT [SessionID]
                              FROM @SessionIDs
                              UNION -- No "ALL" so it does DISTINCT
                              SELECT [BlockingSessionID]
                              FROM @SessionIDs
                             );

/*
-- If any rows are captured this time, try to capture their lock info
INSERT INTO dbo.tmpBlockingResearch_Locks
  SELECT SYSDATETIME() AS [CaptureTime], loc.*
  FROM   sys.dm_tran_locks loc
  WHERE  loc.[request_session_id] IN (
                                      SELECT [SessionID]
                                      FROM @SessionIDs
                                      UNION -- No "ALL" so it does DISTINCT
                                      SELECT [BlockingSessionID]
                                      FROM @SessionIDs
                                     );
 */

Eu acho que você deve conseguir reproduzir isso abrindo uma guia de consulta e executando o seguinte:

CREATE TABLE dbo.tmp (Col1 INT);
BEGIN TRAN;
INSERT INTO dbo.tmp (Col1) VALUES (1);

Em seguida, abra uma segunda guia de consulta e execute o seguinte:

UPDATE dbo.tmp
SET    Col1 = 2
WHERE  Col1 = 1;

PS Apenas para afirmar, a única coisa que não faz sentido é que as informações de solicitação e sessão - dbo.tmpBlockingResearch_Requests- ainda nunca contêm linhas para a sessão de bloqueio. No entanto, eu sei que a variável de tabela possui o ID da sessão de bloqueio, pois puxava os bloqueios para os dois IDs de sessão. Isso pode apontar para um cenário no qual uma transação pode permanecer aberta após o fechamento da "conexão" do cliente, mas a conexão ainda é mantida devido ao pool de conexões.

Solomon Rutzky
fonte
@ TomV Analisei os dados de pesquisa mais recentes e tenho uma teoria bastante sólida. Atualizei minha resposta de acordo, incluindo a adição de uma seção às minhas consultas de pesquisa; portanto, substitua a etapa da tarefa SQL pelas novas consultas aqui (também comentei a consulta "bloqueios", pois não precisamos realmente desses dados no momento e são muitos dados). Sugiro truncar / largar as tabelas de pesquisa existentes para começar do zero.
Solomon Rutzky
@TomV Ok. E atualizei minha consulta de reprodução para ser uma atualização em vez de uma SELECT, portanto, deve ser mais representativa da sua situação. Também adicionei uma observação no final sobre linhas ausentes na tabela de solicitações. Esperamos que a nova tabela Conexões pelo menos confirme a existência continuada do SessionID de bloqueio. (PS, comecei a limpar meus comentários acima).
Solomon Rutzky,
Seu trabalho está ativo. Eu vou ter de encontrar algum tempo para testar a repro e analisá-lo na próxima semana
Tom V - Team Monica
Oi Salomão. 2 novos exemplos foram publicados no github. Infelizmente, não consegui acionar um BPR vazio do processo de bloqueio usando o caso de reprodução fornecido.
Tom V - Team Monica
Deu uma olhada muito rápida, pois não tenho muito tempo. Parece que as informações do Connections mostram o ID da sessão de bloqueio ainda ativo, mas não está na tabela de sessões. Posso testar isso mais tarde, mas tenho certeza de que indica o pool de conexões (a conexão ainda está lá) e a conexão sendo fechada entre os comandos, mas uma transação está claramente aberta (já que o transaction_id é sempre o mesmo que vimos da última vez). Vai dar uma olhada mais tarde ..
Solomon Rutzky 06/07
4

Transações bloqueadas podem ocorrer devido a escalações de bloqueio.

Isso é explicado no artigo de suporte da Microsoft:

Como resolver problemas de bloqueio causados ​​pelo escalonamento de bloqueios no SQL Server

... A
escalação de bloqueio não causa a maioria dos problemas de bloqueio. Para determinar se a escalação de bloqueios está ocorrendo sempre que ocorrer um problema de bloqueio, inicie um rastreamento do SQL Profiler que inclua o evento Lock: Escalation. Se você não vir nenhum evento de bloqueio: escalonamento, o escalonamento de bloqueio não está ocorrendo no servidor e as informações neste artigo não se aplicam à sua situação.

Se a escalação do bloqueio estiver ocorrendo, verifique se o bloqueio da tabela escalada está bloqueando outros usuários
...

Verifique os Eventos Estendidos (arquivo físico) para eventos de escalação de bloqueios que ocorreram antes do processo bloqueado evento do .

Explicando

Há um artigo do Blog da Microsoft que entra em mais detalhes:

Escalação e bloqueio de bloqueio do SQL Server

...
Etapa 2: coletar eventos de escalonamento de bloqueio e relatório de processo bloqueado.

A escalação de bloqueio e os eventos de relatório de processo bloqueado não são capturados automaticamente pelo SQL Server. Para saber se esses eventos estão acontecendo, precisamos dizer ao SQL Server para gravá-los. Nossa equipe usa a ferramenta Analisador de Desempenho para Microsoft Dynamics para coletar essas informações. Confira este post de Rod Hansen para obter mais informações sobre a ferramenta e como coletar detalhes de bloqueio com ela. Se você deseja apenas usar o SQL Server Profiler, os eventos que você precisaria coletar são mostrados abaixo: ...

Depois de capturar as escalações de bloqueio e os processos bloqueados, é necessário determinar se as escalações de bloqueio são a causa principal dos processos bloqueados:

...
Etapa 3: Revise o rastreamento no SQL Server Profiler.

Existem dois indicadores principais que informam se o bloqueio está relacionado à escalação de bloqueios.

Primeiro, você vê uma série de eventos de escalação de bloqueios imediatamente antes dos eventos do relatório de processos bloqueados. Abaixo está um exemplo retirado de um rastreamento produzido pela ferramenta Performance Analyzer for Microsoft Dynamics. É uma coisa a procurar no rastreamento, mas isso por si só não significa que a escalação de bloqueios esteja causando o bloqueio. ...

e mais

Para verificar se o bloqueio está de fato relacionado à escalação de bloqueios, é necessário examinar os detalhes do relatório do processo bloqueado. Na seção TextData, procure waitresource (veja a captura de tela abaixo). Se waitresource começar com OBJECT, sabemos que a instrução bloqueada está aguardando que um bloqueio no nível da tabela seja liberado antes que possa prosseguir. Se waitresource começar com KEY ou PAG em vez de OBJECT, a escalação de bloqueios não estará envolvida nesse bloco específico . A escalação de bloqueios sempre aumentará o escopo de um bloqueio para OJBECT, independentemente de onde ele começa

Solução

(somente se as correspondências mencionadas acima)

Aparentemente, a solução é ativar o sinalizador de rastreamento 1224, que desativará a escalação de bloqueio:

Escalação e bloqueio de bloqueio do SQL Server

Se você vir essas duas coisas juntas, é uma aposta muito boa que a escalação de bloqueios esteja causando o bloqueio e você provavelmente se beneficiaria da implementação do sinalizador de rastreamento 1224 do SQL Server.

Sinalizadores de rastreamento do SQL Server para Dynamics AX

O sinalizador de rastreamento 1224 desativa a escalação de bloqueios com base no número de bloqueios. A ativação desse sinalizador de rastreamento pode reduzir a probabilidade de bloqueio devido à escalação de bloqueios - algo que já vi em várias implementações do AX. O cenário mais comum em que isso se torna um problema é quando há um requisito para que o planejamento mestre seja executado durante o dia

Responda

No final, pode ser que a escalação de bloqueios seja a causa raiz dos processos bloqueados.


Solução alternativa (nó do processo vazio)

Após uma investigação mais aprofundada de alguns relatórios de processo_ bloqueados, a seguinte explicação alternativa pode ser feita.

Os Eventos Estendidos estão capturando blocks_process_reports que não estão relacionados a nenhum outro processo no momento.

Ergo: eles devem ser bloqueados por um motivo diferente

Sugiro que você capture um período de tempo dos tipos de espera na exibição sys.dm_os_wait_stats no SQL Server e correlacione os números com os blocks_process_reports que ocorrem durante suas medições. Paul Randall tem um bom roteiro: envie-me suas estatísticas de espera e receba meu conselho e 30 dias de Pluralsight grátis em troca

Os scripts capturam os contadores atuais, aguardam 23 horas (podem ser modificadas), recapturam os contadores atuais novamente e os comparam para fornecer os 95% melhores tipos de espera. Você pode tentar isso por 1 hora e ter o arquivo XEL à mão.

Você pode encontrar um tipo de espera (por exemplo, LCK_M_SH,…) que esteja informando que seu armazenamento é lento por escrito. Ou que você tenha alguma outra sobrecarga (por exemplo, CX_PACKET_WAITS,….). Algo está atrasando suas atualizações. Você pode ver se os sys.dm_os_wait_stats se relacionam com o locked_process_reports com os nós vazios.

Há casos em que um SPID bloqueado está sendo bloqueado pelo mesmo SPID:

A coluna bloqueada na tabela sysprocesses é preenchida para espera de trava após a instalação do SQL Server 2000 SP4

Quando um SPID está aguardando uma trava de página de E / S, você pode observar que a coluna bloqueada relata brevemente que o SPID está se bloqueando. Esse comportamento é um efeito colateral da maneira como as travas são usadas para operações de E / S nas páginas de dados. Quando um thread emite uma solicitação de E / S, o SPID que emite a solicitação de E / S adquire uma trava na página. Todas as operações de E / S do SQL Server 2000 são assíncronas. Portanto, o SPID tentará adquirir outra trava na mesma página se o SPID que emitiu a solicitação de E / S precisar aguardar a conclusão da solicitação. Essa segunda trava é bloqueada pela primeira trava. Portanto, a coluna bloqueada informa que o SPID está se bloqueando. Quando a solicitação de E / S termina, a primeira trava é liberada. Em seguida, a segunda solicitação de trava é concedida.

Resposta Alternativa

Essa é mais uma indicação de que você pode estar tendo problemas de IO. Esses problemas resultam em "processos bloqueados", mas sem um SPID externo relacionado. Eventos estendidos podem não relatar o processo / SPID em um nó separado.

John aka hot2use
fonte
Eu poderia estar lendo isso errado, mas essas informações não provam que o problema não é uma escalação de bloqueio? Uma seção citada diz "look at the blocked process report details.", e o XML mais importante da questão é o relatório do processo bloqueado. Em seguida, a mesma seção citada diz, "If waitresource starts with KEY or PAG instead of OBJECT, then lock escalation isn’t involved in that specific block."e o XML do relatório de processo bloqueado é exibido waitresource="KEY: 6:72057..... Isso significa que "a escalação de bloqueio não está envolvida" aqui.
Solomon Rutzky
Não, você NÃO está interpretando mal isso. A seção fornecida na pergunta é um problema neste servidor. Minha resposta é uma abordagem global para problemas que podem ocorrer devido ao bloqueio e à escalação de bloqueios. Se você pode corrigir alguns problemas principais (relatórios_processos_processos para bloqueio no nível OBJECT), os problemas menores (relatórios_processos_processos em outros níveis) podem se resolver. Esta é a razão pela qual também adicionei uma segunda resposta alternativa.
John aka hot2use