Processus de blocage vide dans le rapport de processus bloqué


28

Je collecte des rapports de processus bloqués à l'aide d'événements étendus et, pour une raison quelconque, dans certains rapports, le blocking-processnœud est vide. Voici le xml complet:

<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>

La définition d'index pour l'index auquel appartient hobt_id est

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

Il n'y a pas de partitionnement impliqué, voici la définition de la table:

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

Aucun déclencheur ou clé étrangère n'est défini sur aucune des tables de la base de données entière.

La version exacte de SQL Server est:

Microsoft SQL Server 2012 (SP3-CU4) (KB3165264) - 11.0.6540.0 (X64)
23 juin 2016 17:45:11 Copyright (c) Microsoft Corporation Enterprise Edition: licence basée sur les cœurs (64 bits) sur Windows NT 6.3 ( Build 14393:) (Hyperviseur)

Les événements étendus sont assez simples, il suffit de consigner les rapports de processus bloqués:

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

La base de données est configurée dans Read Committed Snapshot Isolation et le degré maximal de parallélisme est défini sur 1. Il s'agit de la configuration du serveur:

+------------------------------------+-------+
|                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 |
+------------------------------------+-------+

J'ai exécuté une trace côté serveur pendant un certain temps et j'obtiens les mêmes nœuds vides dans un fichier de trace que j'utilise des événements étendus.
Ce rapport de processus bloqué a été capturé à l'aide d'une trace côté serveur sur un autre serveur exécutant également Dynamics AX, il n'est donc pas spécifique à ce serveur ou à cette version.

<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>

Quelqu'un at-il une explication à ces rapports? Qu'est-ce qui bloque la requête?

Existe-t-il un moyen de savoir ce qui se passait si je regarde les rapports après que les verrous ont disparu depuis longtemps?

Une chose qui pourrait être utile d'ajouter est que ces requêtes sont exécutées via sp_cursorprepareetsp_cursorexecute

Jusqu'à présent, je n'ai pas pu le reproduire, cela semble arriver au hasard mais très souvent.

Cela se produit sur plusieurs instances (de versions différentes) et plusieurs tables / requêtes, toutes liées à Dynamics AX.

Il n'y a aucun travail de maintenance d'index ou de base de données survenant en arrière-plan à ce moment.

En utilisant le code fourni dans la réponse de srutzky, j'ai pu capturer une journalisation liée à ce rapport de processus bloqué:

<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>

Cela se trouve dans les tables de journalisation pour la même ressource à cette époque: Gist en raison de la limite de caractères

Une enquête plus approfondie montre que juste avant et après le rapport avec un processus de blocage vide, j'ai des rapports pour le même ID de ressource qui ont des nœuds de processus de blocage:

<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>

En utilisant le nouveau script fourni par srutzky, de nouvelles données ont été collectées. Il est publié sur github en raison de la longueur maximale du message.

Étant donné que les données initialement publiées n'avaient pas les deux identifiants de session, de nouvelles données ont à nouveau été publiées sur github

Nouvelles données, y compris les connexions sur github

Réponses:


6

Je ne peux pas tester cette théorie pour le moment, mais sur la base des données de capture les plus récentes publiées sur GitHub , je dirais que la raison pour laquelle le <process>nœud est vide est qu'il nécessite une demande en cours d'exécution (de nombreux attributs se trouvent dans sys.dm_exec_requestset pas dans sys.dm_exec_sessions) et sans demande en cours d'exécution, il ne peut pas rapporter de détails, semblable à la façon dont faire un INNER JOINentre sys.dm_exec_requestset sys.dm_exec_sessionsexclura les lignes où une session est active mais est inactive en raison d'aucune demande en cours.

En regardant l'ensemble de données supérieur ( monitorLoopvaleurs: 1748823, 1748824, 1748825 et 1748827), nous pouvons voir ce qui suit:

  • le iddu blocked-processest le même dans chaque cas: process2552c1fc28 , et le seul attribut différent est waittime(naturellement).
  • les attributs des blocking-processnœuds montrent des différences à la fois lastbatchstartedetlastbatchcompleted
  • les attributs des blocking-processnœuds montrent des valeurs identiques pour spidetxactid

Alors, comment les SessionID et TransactionID du processus de blocage peuvent-ils être les mêmes sur 4 lots de requêtes différents? Facile, une transaction explicite a été lancée puis ces lots ont été exécutés. Et comme ce sont des lots séparés, il y a du temps entre leur soumission, auquel cas il n'y a pas de demande en cours, donc aucune information de processus à afficher (mais la session et la transaction sont toujours là).

Pour effectuer des recherches supplémentaires à ce sujet, vous pouvez capturer des informations utiles à partir de sys.dm_exec_requestset sys.dm_tran_locksen plaçant le T-SQL suivant dans une étape de travail de l'Agent SQL Server "Script Transaction-SQL (T-SQL)", en définissant la "Base de données" comme celui que vous recherchez (dans ce cas, c'est celui avec un ID de 6), et la planification de ce travail pour s'exécuter toutes les 10 secondes. Le T-SQL ci-dessous créera les deux tables dans cette même base de données si elles n'existent pas, puis remplira la table "Requêtes" si une requête se bloque elle-même ou si c'est une opération de suppression ou de mise à jour qui est bloquée . Si des demandes sont trouvées, il tentera de capturer:

  • Informations de session et de demande sur le processus de blocage (cette partie ne suppose pas qu'il existe une demande active, d'où RIGHT JOINau moins obtenir les informations de session)
  • Informations de connexion pour les processus bloqués et (espérons-le) bloquants.
  • les verrous actuels pour ces mêmes id_session (gardez à l'esprit que les informations de verrouillage ne sont pas garanties à 100% exactes car ces informations peuvent changer dans le temps entre l'exécution de ces deux instructions; néanmoins, les informations sont assez bonnes assez souvent pour être mérite d'être capturé). Cette section est actuellement commentée.

Étape de travail T-SQL de l'agent SQL Server:

-- !! 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
                                     );
 */

Je pense que vous devriez pouvoir reproduire cela en ouvrant un onglet de requête et en exécutant ce qui suit:

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

Ensuite, ouvrez un deuxième onglet de requête et exécutez ce qui suit:

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

PS Juste pour le dire, la seule chose qui n'a pas de sens est que les informations de demande et de session - dbo.tmpBlockingResearch_Requests- ne contiennent toujours jamais de lignes pour la session de blocage. Pourtant, je sais que la variable de table contient l'ID de session de blocage, car elle a tiré les verrous pour les deux SessionID. Cela pourrait indiquer un scénario dans lequel une transaction est autorisée à rester ouverte après la fermeture de la «connexion» du client, mais la connexion est toujours maintenue en raison du pool de connexions.


@TomV J'ai passé en revue les dernières données de recherche et j'ai une théorie assez solide. J'ai mis à jour ma réponse en conséquence, y compris en ajoutant une section à mes requêtes de recherche, veuillez donc remplacer l'étape de travail SQL par les nouvelles requêtes ici (j'ai également commenté la requête "verrous" car nous n'avons pas vraiment besoin de ces données pour le moment et c'est beaucoup de données). Je suggère de tronquer / supprimer les tables de recherche existantes pour recommencer à zéro.
Solomon Rutzky

@TomV Ok. Et j'ai mis à jour ma requête de repro pour qu'elle soit une MISE À JOUR au lieu d'un SELECT, elle devrait donc être plus représentative de votre situation de toute façon. J'ai également ajouté une note à la fin sur les lignes manquantes dans le tableau des demandes. Espérons que la nouvelle table Connexions confirmera au moins l'existence continue du SessionID bloquant. (PS, j'ai commencé à nettoyer mes commentaires ci-dessus).
Solomon Rutzky

Votre travail est actif. Il faudra que je trouve un peu de temps pour tester la repro et l'analyser la semaine prochaine
Tom V - Team Monica

Salut Salomon. 2 nouveaux exemples ont été postés sur github. Malheureusement, je n'ai pas pu déclencher un processus de blocage vide BPR en utilisant le cas de repro fourni.
Tom V - Team Monica

J'ai jeté un coup d'œil très rapide car je n'ai pas beaucoup de temps. Il semble que les informations sur les connexions montrent que l'ID de session de blocage est toujours actif, mais il ne figure pas dans le tableau des sessions. Je peux tester cela plus tard, mais je suis à peu près sûr que cela indique le regroupement de connexions (la connexion est toujours là) et la connexion étant fermée entre les commandes, mais une transaction est clairement ouverte (puisque le transaction_id est toujours le même que nous l'avons vu la dernière fois).
J'y

4

Des transactions bloquées peuvent se produire en raison des escalades de verrous.

Ceci est expliqué dans l'article du support Microsoft:

Comment résoudre les problèmes de blocage causés par l'escalade de verrous dans SQL Server

... L'
escalade de verrous ne provoque pas la plupart des problèmes de blocage. Pour déterminer si l'escalade de verrous se produit au moment où vous rencontrez des problèmes de blocage, démarrez une trace du Générateur de profils SQL qui inclut l'événement Lock: Escalation. Si vous ne voyez aucun événement Lock: Escalation, l'escalade de verrous ne se produit pas sur votre serveur et les informations contenues dans cet article ne s'appliquent pas à votre situation.

Si une escalade de verrous se produit, vérifiez que le verrou de table escaladé bloque d'autres utilisateurs
...

Vérifiez les événements étendus (fichier physique) pour les événements d' escalade de verrous qui se sont produits avant l' événement de processus bloqué .

Expliquant

Il y a un article sur le blog Microsoft qui va plus en détail:

Escalade et blocage des verrous SQL Server

...
Étape 2: collecter les événements d'escalade de verrous et de rapports de processus bloqués.

Les événements d'escalade de verrous et de rapports de processus bloqués ne sont pas automatiquement capturés par SQL Server. Afin de savoir si ces événements se produisent, nous devons dire à SQL Server de les enregistrer. Notre équipe utilise l'outil Performance Analyzer pour Microsoft Dynamics pour recueillir ces informations. Consultez cet article de Rod Hansen pour plus d'informations sur l'outil et comment collecter les détails de blocage avec lui. Si vous souhaitez simplement utiliser SQL Server Profiler, les événements que vous devez collecter sont indiqués ci-dessous: ...

Après avoir capturé les escalades de verrous et les processus bloqués, vous devez déterminer si les escalades de verrous sont la cause première des processus bloqués:

...
Étape 3: passez en revue la trace dans SQL Server Profiler.

Il existe deux indicateurs principaux qui vous indiqueront si le blocage est lié à l'escalade de verrous.

Tout d'abord, vous voyez une série d'événements d'escalade de verrous précédant immédiatement les événements de rapport de processus bloqués. Voici un exemple tiré d'une trace produite par l'outil Performance Analyzer pour Microsoft Dynamics. C'est une chose à rechercher dans la trace, mais cela ne signifie pas à lui seul que l'escalade de verrous est à l'origine du blocage. ...

et plus loin

Pour vérifier que le blocage est bien lié à l'escalade de verrous, vous devez consulter les détails du rapport de processus bloqué. Dans la section TextData, recherchez waitresource (voir la capture d'écran ci-dessous). Si waitresource commence par OBJECT, nous savons que l'instruction bloquée attend la libération d'un verrou de niveau table avant de pouvoir continuer. Si waitresource commence par KEY ou PAG au lieu de OBJECT, alors l' escalade de verrous n'est pas impliquée dans ce bloc spécifique . L'escalade de verrous augmentera toujours la portée d'un verrou vers OJBECT, peu importe où il commence

Solution

(uniquement si les correspondances mentionnées ci-dessus)

La solution est apparemment d'activer l'indicateur de trace 1224 qui désactivera l'escalade des verrous:

Escalade et blocage des verrous SQL Server

Si vous voyez ces deux choses ensemble, il y a fort à parier que l'escalade de verrous est à l'origine du blocage et vous bénéficierez probablement de l'implémentation de l'indicateur de trace 1224 de SQL Server.

Indicateurs de trace SQL Server pour Dynamics AX

L'indicateur de trace 1224 désactive l'escalade des verrous en fonction du nombre de verrous. L'activation de cet indicateur de trace peut réduire la probabilité de blocage en raison de l'escalade des verrous - quelque chose que j'ai vu avec un certain nombre d'implémentations AX. Le scénario le plus courant où cela devient un problème est quand il est nécessaire que Master Planning s'exécute pendant la journée

Répondre

En fin de compte, il se pourrait que l'escalade de verrous soit la cause première des processus bloqués.


Solution alternative (nœud de processus vide)

Après une enquête plus approfondie de certains rapports bloqués_processus, l'explication alternative suivante peut être faite.

Les événements étendus capturent les rapports de processus bloqués qui ne sont liés à aucun autre processus à l'époque.

Ergo: Ils doivent être bloqués pour une raison différente

Je vous suggère de capturer un laps de temps de types d'attente à partir de la vue sys.dm_os_wait_stats sur votre serveur SQL et de corréler les nombres avec les blocs_process_reports qui se produisent pendant vos mesures. Paul Randall a un bon script: envoyez-moi vos statistiques d'attente et obtenez mes conseils et 30 jours de Pluralsight gratuits en retour

Le script capture les compteurs actuels, attend 23 heures (peut être modifié), recapture à nouveau les compteurs actuels et les compare pour vous donner les 95% de types d'attente les plus fréquents. Vous pouvez essayer cela pendant disons 1 heure et avoir le fichier XEL à portée de main.

Vous pouvez trouver un type d'attente (par exemple LCK_M_SH,…) qui vous indique que votre stockage est lent à écrire. Ou que vous avez d'autres frais généraux (par exemple CX_PACKET_WAITS,….). Quelque chose ralentit vos mises à jour. Vous pouvez ensuite voir si les sys.dm_os_wait_stats se rapportent aux blocs_process_reports avec les nœuds vides.

Il existe des cas où un SPID bloqué est bloqué par le même SPID:

La colonne bloquée dans la table sysprocesses est remplie pour les attentes de verrouillage après avoir installé SQL Server 2000 SP4

Lorsqu'un SPID attend un verrou de page d'E / S, vous pouvez remarquer que la colonne bloquée signale brièvement que le SPID se bloque. Ce comportement est un effet secondaire de la façon dont les verrous sont utilisés pour les opérations d'E / S sur les pages de données. Lorsqu'un thread émet une demande d'E / S, le SPID qui émet la demande d'E / S acquiert un verrou sur la page. Toutes les opérations d'E / S SQL Server 2000 sont asynchrones. Par conséquent, le SPID tentera d'acquérir un autre verrou sur la même page si le SPID qui a émis la demande d'E / S doit attendre la fin de la demande. Ce deuxième verrou est bloqué par le premier verrou. Par conséquent, la colonne bloquée signale que le SPID se bloque. Une fois la demande d'E / S terminée, le premier verrou est libéré. Ensuite, la deuxième demande de verrouillage est accordée.

Autre réponse

C'est une autre indication que vous pourriez avoir des problèmes d'E / S. Ces problèmes entraînent des «processus bloqués» mais sans SPID étranger associé. Les événements étendus peuvent ne pas signaler le processus / SPID dans un nœud distinct.


Je pourrais être mal interprété, mais ces informations ne prouvent-elles pas que le problème n'est pas l' escalade des verrous? Une section citée dit "look at the blocked process report details.", et le XML le plus élevé dans la question est le rapport de processus bloqué. Ensuite, la même section citée indique, "If waitresource starts with KEY or PAG instead of OBJECT, then lock escalation isn’t involved in that specific block."et le XML de rapport de processus bloqué s'affiche waitresource="KEY: 6:72057..... Cela signifie donc que "l'escalade des verrous n'est pas impliquée" ici.
Solomon Rutzky

Non, vous ne l'avez PAS mal interprété. La section fournie dans la question est un problème sur ce serveur. Ma réponse est une approche globale des problèmes qui pourraient survenir en raison du blocage et de l'escalade des verrous. Si vous pouvez résoudre certains problèmes majeurs (bloqués_processus_reports pour le verrouillage de niveau OBJET), les problèmes plus petits (bloqués_processus_reports à d'autres niveaux) peuvent se résoudre d'eux-mêmes. C'est la raison pour laquelle j'ai également ajouté une deuxième réponse alternative.
John aka hot2use
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.