Partager via


Guide des interblocages

S’applique à :SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceAzure Synapse AnalyticsAnalytics Platform System (PDW)Base de données SQL dans Microsoft Fabric

Cet article traite des interblocages dans le moteur de base de données en profondeur. Les interblocages sont causés par des verrous concurrents et simultanés dans la base de données, souvent dans des transactions en plusieurs étapes. Pour plus d’informations sur les transactions et les verrous, consultez le guide de verrouillage des transactions et de contrôle de version des lignes.

Pour plus d’informations sur l’identification et la prévention des interblocages dans Azure SQL Database et SQL Database dans Fabric, consultez Analyser et empêcher les interblocages dans Azure SQL Database et SQL Database dans Fabric.

Comprendre les interblocages

Un interblocage se produit lorsque deux tâches ou plus se bloquent mutuellement de façon permanente. Dans ce cas, chaque tâche place un verrou sur une ressource que la ou les autres tâches essaient de verrouiller. Par exemple:

  • La transaction A obtient un verrou partagé sur la ligne 1.

  • La transaction B obtient un verrou partagé sur la ligne 2.

  • La transaction A demande un verrou exclusif sur la ligne 2, mais elle est bloquée jusqu’à la fin de la transaction B qui libérera le verrou partagé sur la ligne 2.

  • La transaction B demande un verrou exclusif sur la ligne 1, mais elle est bloquée jusqu’à la fin de la transaction A qui libérera le verrou partagé sur la ligne 1.

La transaction A ne peut pas se terminer tant que la transaction B n’est pas terminée, mais la transaction B est bloquée par la transaction A. Il s’agit d’une dépendance cyclique : la transaction A est dépendante de la transaction B, mais celle-ci ne peut pas s’exécuter car elle est dépendante de la transaction A.

Les deux transactions en interblocage attendent indéfiniment que la situation soit débloquée par un processus externe. Le moniteur de détection d'interblocage du moteur de base de données vérifie périodiquement les tâches en situation de deadlock. S'il détecte une situation de dépendance cyclique, il désigne une des tâches comme victime et met fin à sa transaction avec un message d'erreur. Cela permet à l'autre tâche de terminer sa transaction. L’application qui exécutait la transaction abandonnée peut effectuer une nouvelle tentative qui réussit en général une fois que l’autre transaction est terminée.

L'interblocage est souvent confondu avec le blocage ordinaire. Lorsqu'une transaction demande un verrou sur une ressource verrouillée par une autre transaction, elle attend que le verrou soit libéré. Par défaut, les transactions dans le moteur de base de données n’expirent pas, à moins que LOCK_TIMEOUT soit défini. La transaction qui demande le verrou est bloquée, mais pas indéfiniment puisqu’elle n’a rien fait pour bloquer la transaction détenant le verrou. La transaction qui détient le verrou va finir par se terminer et libérer le verrou ; l’autre transaction pourra alors obtenir son verrou et s’exécuter normalement. Les interblocages sont résolus presque immédiatement, tandis que les blocages peuvent, en théorie, persister indéfiniment. Les interblocages sont parfois appelés « étreintes fatales ».

Un interblocage peut se produire sur tout système multithread, pas uniquement sur les systèmes de gestion de bases de données relationnelles. Elle peut également concerner des ressources autres que les verrous sur les objets de base de données. Par exemple, un thread dans un système multithread peut acquérir une ou plusieurs ressources (par exemple, des blocs de mémoire). Si la ressource acquise appartient actuellement à une autre thread, la première thread devra éventuellement attendre que le thread propriétaire libère la ressource cible. le thread en attente a une dépendance sur le thread propriétaire de cette ressource particulière. Dans une instance du moteur de base de données, les sessions peuvent s’interblocer lors de l’acquisition de ressources non de base de données, telles que la mémoire ou les threads.

Diagramme montrant l’interblocage de la transaction.

Dans l’illustration, la transaction T1 dépend de la transaction T2 pour la Part ressource de verrouillage de table. De même, la transaction T2 est dépendante de T1 pour la ressource de Supplier verrouillage de la table. Comme ces dépendances forment un cycle, il y a interblocage entre les transactions T1 et T2.

Voici une illustration plus générale d’un blocage :

Diagramme montrant des tâches dans un état d’interblocage.

  • La tâche T1 a placé un verrou sur la ressource R1 (indiquée par la flèche reliant R1 à T1) et a demandé un verrou sur la ressource R2 (indiquée par la flèche reliant T1 à R2).

  • La tâche T2 a placé un verrou sur la ressource R2 (indiquée par la flèche reliant R2 à T2) et a demandé un verrou sur la ressource R1 (indiquée par la flèche reliant T2 à R1).

  • Dans la mesure où aucune des deux tâches ne peut continuer tant qu'il n'y a pas de ressource disponible et que ni l'une ni l'autre des ressources ne peut être libérée avant la poursuite d'une tâche, un état de blocage se produit.

Note

Le moteur de base de données détecte automatiquement les cycles d’interblocage. Il choisit l’une des transactions en tant que victime d’interblocage et l’arrête avec une erreur pour briser le blocage.

Ressources susceptibles de se bloquer

Chaque session utilisateur peut avoir une ou plusieurs tâches en cours d’exécution, chacune de ces tâches pouvant obtenir ou être en attente d’obtention de ressources. Les types de ressources susceptibles de provoquer un blocage sont les suivants :

  • Locks. L’attente d’obtention de verrous sur des ressources, telles qu’objets, pages, lignes, métadonnées et applications peut provoquer un interblocage. Par exemple, la transaction T1 a un verrou partagé (S) sur la ligne r1 et attend d’obtenir un verrou exclusif (X) sur r2. La transaction T2 a un verrou partagé (S) sur r2 et attend d’obtenir un verrou exclusif (X) sur la ligne r1. Il en résulte un cycle de verrouillage où T1 et T2 attendent l'une de l'autre la libération des ressources que chacune a verrouillées.

  • Threads de travail. Une tâche en attente d’un thread de travail disponible peut provoquer un interblocage. Si la tâche en file d’attente est propriétaire des ressources qui bloquent tous les threads de travail, un interblocage en résulte. Par exemple, la session S1 démarre une transaction et acquiert un verrou partagé (S) sur la ligne r1, puis passe en veille. Les sessions actives s’exécutant sur tous les threads de travail disponibles tentent d’acquérir des verrous exclusifs (X) sur la ligne r1. Étant donné que la session S1 ne peut pas obtenir de thread de travail, elle ne peut pas valider la transaction et libère le verrou au niveau sur la ligne r1. Cela produit un blocage.

  • Memory. Lorsque des demandes concurrentes sont en attente d’allocation de mémoire qui ne peut être satisfaite faute de mémoire suffisante, un interblocage peut se produire. Par exemple, deux demandes concurrentes, Q1 et Q2, qui s'exécutant en tant que fonctions définies par l'utilisateur, obtiennent respectivement 10 Mo et 20 Mo de mémoire. Si chaque requête nécessite 30 Mo et que la quantité de mémoire disponible est de 20 Mo, Q1 et Q2 doivent attendre que chacune libère la mémoire, ce qui entraîne un interblocage.

  • Ressources liées à l’exécution de requêtes parallèles. Les threads de coordination, production ou consommation associées à un port d’échange peuvent se bloquer mutuellement et provoquer un interblocage, qui se produit généralement lors de l’introduction d’au moins un autre processus étranger à la requête parallèle. En outre, lorsqu’une requête parallèle démarre l’exécution, le moteur de base de données détermine le degré de parallélisme et le nombre de threads de travail requis, en fonction de la charge de travail actuelle. Si la charge de travail change de façon inattendue, par exemple si de nouvelles requêtes commencent à s'exécuter sur le serveur ou que le système se trouve à court de threads de travail, il peut s'ensuivre un blocage.

  • Ressources MARS (Multiple Active Result Sets). Ces ressources servent à contrôler l'entrelacement de plusieurs demandes actives sous MARS. Pour en savoir plus, consultez Utilisation de MARS (Multiple Active Result Sets) dans SQL Server Native Client.

    • Ressource utilisateur. Lorsqu'un thread est en attente d'une ressource potentiellement contrôlée par une application d'utilisateur, la ressource est considérée comme étant une ressource externe ou utilisateur et est traitée comme un verrou.

    • Mutex de session. Les tâches exécutées au cours d'une session sont entrelacées, ce qui signifie que seule une tâche peut s'exécuter à un moment donné dans le cadre de la session. Avant de pouvoir s'exécuter, la tâche doit disposer d'un accès exclusif à l'exclusion mutuelle de la session.

    • Mutex de transaction. Toutes les tâches qui s'exécutent lors d'une transaction sont entrelacées, ce qui signifie que seule une tâche peut s'exécuter à un moment donné dans le cadre de la transaction. Avant de pouvoir s'exécuter, la tâche doit disposer d'un accès exclusif à l'exclusion mutuelle de la transaction.

      Pour pouvoir s'exécuter sous MARS, une tâche doit obtenir l'exclusion mutuelle de session. Si la tâche s'exécute dans le cadre d'une transaction, elle doit obtenir l'exclusion mutuelle de transaction. Vous serez ainsi assuré qu'il n'y a qu'une seule tâche active à la fois pour une session et une transaction données. Dès lors que les exclusions mutuelles requises ont été acquises, la tâche peut s'exécuter. Lorsque la tâche se termine ou est interrompue au milieu de la requête, elle libère d’abord le mutex de transaction, puis le mutex de session, dans l’ordre inverse de leur acquisition. Cependant, des blocages peuvent se produire avec ces ressources. Dans le pseudocode suivant, deux tâches, la requête utilisateur U1 et la requête utilisateur U2, s’exécutent dans la même session.

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

      La procédure stockée qui s'exécute à partir de la demande d'utilisateur U1 a obtenu l'exclusion mutuelle de session. Si la procédure stockée prend beaucoup de temps à s’exécuter, elle est supposée par le moteur de base de données que la procédure stockée attend l’entrée de l’utilisateur. La demande d'utilisateur U2 attend l'exclusion mutuelle de session alors que l'utilisateur attend le jeu de résultats d'U2, et U1 attend une ressource utilisateur. Il s'agit d'un état de blocage logiquement illustré ainsi :

      Diagramme du flux logique d’une procédure stockée dans MARS.

Des interblocages peuvent également se produire quand une table est partitionnée et que le paramètre LOCK_ESCALATION de ALTER TABLE est défini sur AUTO. Lorsque LOCK_ESCALATION est défini sur AUTO, cela augmente la concurrence en permettant au moteur de base de données de verrouiller les partitions de table au niveau HoBT plutôt qu'au niveau de la table. Toutefois, lorsque des transactions distinctes maintiennent des verrous de partition dans une table et souhaitent un verrou sur l'autre partition de transactions, cela provoque un interblocage. Ce type d’interblocage peut être évité en définissant LOCK_ESCALATION sur TABLE. Cependant, ce paramètre réduit la concurrence en forçant les mises à jour importantes d’une partition à attendre un verrouillage de table.

Détection d’interblocage

Toutes les ressources répertoriées dans la section Ressources qui peuvent bloquer participent au schéma de détection de blocage du moteur de base de données. La détection de blocage est effectuée par un thread de surveillance de verrou qui lance régulièrement une recherche dans toutes les tâches d’une instance du moteur de base de données. Le processus de recherche présente les caractéristiques suivantes :

  • L'intervalle par défaut est de 5 secondes.

  • Si le thread de contrôle des verrous détecte des interblocages, de 5 secondes, l’intervalle de détection de blocage pourra descendre jusqu’à 100 millisecondes, en fonction de la fréquence des interblocages.

  • Si le thread du moniteur de verrouillage cesse de trouver des interblocages, le moteur de base de données augmente les intervalles entre les recherches à 5 secondes.

  • Si un blocage est détecté, on suppose que les nouveaux threads qui doivent attendre un verrou entrent dans le cycle d’interblocage. Les premières attentes de verrouillage après la détection d’un interblocage déclenchent immédiatement une recherche de blocage, plutôt que d’attendre le prochain intervalle de détection d’interblocage. Par exemple, si l’intervalle courant est de 5 secondes et qu’un interblocage vient d’être détecté, la prochaine attente de verrou lancera immédiatement le détecteur d’interblocage. Si cette attente de verrou fait partie d’un interblocage, elle est détectée immédiatement, plutôt que pendant la recherche de blocage suivante.

Le moteur de base de données effectue généralement une détection périodique des blocages uniquement. Étant donné que le nombre d’interblocages rencontrés dans le système est généralement petit, la détection périodique des interblocages permet de réduire la surcharge de détection des interblocages dans le système.

Lorsque le contrôleur de verrous initialise une recherche de blocage pour une thread particulière, il identifie la ressource sur laquelle le thread est en attente. Il recherche ensuite les propriétaires de la ressource concernée et continue la recherche de façon récursive, jusqu’à ce qu’il trouve un cycle. Un cycle identifié de cette manière forme un blocage.

Une fois qu’un blocage est détecté, le moteur de base de données met fin à un interblocage en choisissant l’un des threads comme victime d’interblocage. Le moteur de base de données met fin à la requête en cours d'exécution pour le thread, annule la transaction de la victime du blocage et retourne l’erreur 1205 à l’application. L'annulation de la transaction de la victime du blocage a pour effet de libérer tous les verrous détenus par la transaction. Cela permet aux transactions des autres threads de se débloquer et de continuer. L’erreur 1205 (victime du blocage) enregistre des informations sur le type de ressources impliquées dans un blocage.

Par défaut, le moteur de base de données choisit la transaction exécutant la transaction qui est la moins coûteuse à restaurer en tant que victime du blocage. Un utilisateur peut également spécifier la priorité des sessions dans une situation de blocage au moyen de l’instruction SET DEADLOCK_PRIORITY. DEADLOCK_PRIORITY peut être défini sur LOW, NORMAL, ou HIGH, ou peut également être défini sur n’importe quelle valeur entière dans l'intervalle de -10 à 10. Dans certains cas, le moteur de base de données peut choisir de modifier la priorité d’interblocage pendant une courte durée pour obtenir une meilleure concurrence.

La priorité d’interblocage est par défaut à NORMAL, ou 0. Si deux sessions ont des priorités de blocage différentes, la transaction sur la session avec la priorité inférieure est choisie comme victime d’interblocage. Si les deux sessions ont la même priorité de blocage, la transaction qui est la moins coûteuse à annuler est choisie. Si les sessions impliquées dans le cycle de blocage présentent une priorité de blocage et un coût identiques, la victime est choisie de façon aléatoire. Une tâche en cours d’annulation ne peut pas être choisie comme victime d’interblocage.

Lors de l’utilisation du CLR (Common Language Runtime), le moniteur d’interblocage détecte automatiquement les interblocages pour les ressources de synchronisation (moniteurs, verrous lecteur/enregistreur et jointure de threads) accessibles dans les procédures managées. Toutefois, le blocage est résolu par la levée d'une exception dans la procédure qui a été sélectionnée comme victime du blocage. Il est important de comprendre que l’exception ne libère pas automatiquement les ressources actuellement détenues par la victime ; les ressources doivent être libérées explicitement. Conformément au comportement des exceptions, l'exception utilisée pour identifier une victime de blocage peut être interceptée et annulée.

Outils d'information sur les blocages

Pour afficher les informations de blocage, le moteur de base de données fournit des outils de surveillance sous la forme de l’événement xml_deadlock_report étendu, de deux indicateurs de trace, et de l’événement de graphe de blocage dans SQL Profiler.

L’événement xml_deadlock_report étendu est la méthode recommandée pour capturer les informations de blocage.

Événement étendu d’interblocage

Dans SQL Server 2012 (11.x) et versions ultérieures, l’événement xml_deadlock_report étendu doit être utilisé au lieu de la classe d’événements de graphe de blocage dans SQL Trace ou SQL Profiler.

La session d’événements system_health capture les xml_deadlock_report événements par défaut. Ces événements contiennent le graphe de blocage. Étant donné que la system_health session est activée par défaut, vous n’avez pas besoin de configurer une session d’événements distincte pour capturer les informations d’interblocage.

L’événement Deadlock Graph capturé a généralement trois nœuds distincts :

  • victim-list. Identificateur du processus victime de l’interblocage.
  • process-list. Informations sur tous les processus impliqués dans l’interblocage.
  • resource-list. Informations sur les ressources impliquées dans l’interblocage.

Vous pouvez afficher les event_file données cibles de la system_health session dans Management Studio. xml_deadlock_report Si des événements se sont produits, Management Studio présente une représentation graphique des tâches et des ressources impliquées dans un interblocage, comme illustré dans l’exemple suivant :

Capture d’écran SSMS d’un diagramme visuel Événement étendu de graphe d’interblocage.

La requête suivante peut afficher tous les événements de blocage capturés par la ring_buffer cible de la system_health session :

SELECT xdr.value('@timestamp', 'datetime') AS deadlock_time,
       xdr.query('.') AS event_data
FROM (SELECT CAST ([target_data] AS XML) AS target_data
      FROM sys.dm_xe_session_targets AS xt
           INNER JOIN sys.dm_xe_sessions AS xs
               ON xs.address = xt.event_session_address
      WHERE xs.name = N'system_health'
            AND xt.target_name = N'ring_buffer') AS XML_Data
CROSS APPLY Target_Data.nodes('RingBufferTarget/event[@name="xml_deadlock_report"]') AS XEventData(xdr)
ORDER BY deadlock_time DESC;

Voici le jeu de résultats.

Capture d’écran SSMS du résultat de la requête system_health XEvent.

L’exemple suivant montre un exemple de sortie de la event_data colonne :

<event name="xml_deadlock_report" package="sqlserver" timestamp="2022-02-18T08:26:24.698Z">
  <data name="xml_report">
    <type name="xml" package="package0" />
    <value>
      <deadlock>
        <victim-list>
          <victimProcess id="process27b9b0b9848" />
        </victim-list>
        <process-list>
          <process id="process27b9b0b9848" taskpriority="0" logused="0" waitresource="KEY: 5:72057594214350848 (1a39e6095155)" waittime="1631" ownerId="11088595" transactionname="SELECT" lasttranstarted="2022-02-18T00:26:23.073" XDES="0x27b9f79fac0" lockMode="S" schedulerid="9" kpid="15336" status="suspended" spid="62" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2022-02-18T00:26:22.893" lastbatchcompleted="2022-02-18T00:26:22.890" lastattention="1900-01-01T00:00:00.890" clientapp="SQLCMD" hostname="ContosoServer" hostpid="7908" loginname="CONTOSO\user" isolationlevel="read committed (2)" xactid="11088595" currentdb="5" lockTimeout="4294967295" clientoption1="538968096" clientoption2="128056">
            <executionStack>
              <frame procname="AdventureWorks2022.dbo.p1" line="3" stmtstart="78" stmtend="180" sqlhandle="0x0300050020766505ca3e07008ba8000001000000000000000000000000000000000000000000000000000000">
SELECT c2, c3 FROM t1 WHERE c2 BETWEEN @p1 AND @p1+    </frame>
              <frame procname="adhoc" line="4" stmtstart="82" stmtend="98" sqlhandle="0x020000006263ec01ebb919c335024a072a2699958d3fcce60000000000000000000000000000000000000000">
unknown    </frame>
            </executionStack>
            <inputbuf>
SET NOCOUNT ON
WHILE (1=1)
BEGIN
    EXEC p1 4
END
   </inputbuf>
          </process>
          <process id="process27b9ee33c28" taskpriority="0" logused="252" waitresource="KEY: 5:72057594214416384 (e5b3d7e750dd)" waittime="1631" ownerId="11088593" transactionname="UPDATE" lasttranstarted="2022-02-18T00:26:23.073" XDES="0x27ba15a4490" lockMode="X" schedulerid="6" kpid="5584" status="suspended" spid="58" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2022-02-18T00:26:22.890" lastbatchcompleted="2022-02-18T00:26:22.890" lastattention="1900-01-01T00:00:00.890" clientapp="SQLCMD" hostname="ContosoServer" hostpid="15316" loginname="CONTOSO\user" isolationlevel="read committed (2)" xactid="11088593" currentdb="5" lockTimeout="4294967295" clientoption1="538968096" clientoption2="128056">
            <executionStack>
              <frame procname="AdventureWorks2022.dbo.p2" line="3" stmtstart="76" stmtend="150" sqlhandle="0x03000500599a5906ce3e07008ba8000001000000000000000000000000000000000000000000000000000000">
UPDATE t1 SET c2 = c2+1 WHERE c1 = @p    </frame>
              <frame procname="adhoc" line="4" stmtstart="82" stmtend="98" sqlhandle="0x02000000008fe521e5fb1099410048c5743ff7da04b2047b0000000000000000000000000000000000000000">
unknown    </frame>
            </executionStack>
            <inputbuf>
SET NOCOUNT ON
WHILE (1=1)
BEGIN
    EXEC p2 4
END
   </inputbuf>
          </process>
        </process-list>
        <resource-list>
          <keylock hobtid="72057594214350848" dbid="5" objectname="AdventureWorks2022.dbo.t1" indexname="cidx" id="lock27b9dd26a00" mode="X" associatedObjectId="72057594214350848">
            <owner-list>
              <owner id="process27b9ee33c28" mode="X" />
            </owner-list>
            <waiter-list>
              <waiter id="process27b9b0b9848" mode="S" requestType="wait" />
            </waiter-list>
          </keylock>
          <keylock hobtid="72057594214416384" dbid="5" objectname="AdventureWorks2022.dbo.t1" indexname="idx1" id="lock27afa392600" mode="S" associatedObjectId="72057594214416384">
            <owner-list>
              <owner id="process27b9b0b9848" mode="S" />
            </owner-list>
            <waiter-list>
              <waiter id="process27b9ee33c28" mode="X" requestType="wait" />
            </waiter-list>
          </keylock>
        </resource-list>
      </deadlock>
    </value>
  </data>
</event>

Indicateur de trace 1204 et indicateur de trace 1222

Lorsque des interblocages se produisent et que l’indicateur de trace 1204 ou l’indicateur de trace 1222 est activé, les détails du blocage sont signalés dans le journal des erreurs SQL Server. L’indicateur de trace 1204 signale les informations de blocage mises en forme par chaque nœud impliqué dans le blocage. L’indicateur de trace 1222 met en forme les informations de blocage, tout d’abord par processus, puis par ressources. Il est possible d’activer les deux indicateurs de trace afin d’obtenir deux représentations du même événement d’interblocage.

Important

Évitez d’utiliser les indicateurs de trace 1204 et 1222 sur les systèmes gourmands en charge de travail qui rencontrent des blocages. L’utilisation de ces indicateurs de trace peut entraîner des problèmes de performances. Utilisez plutôt l’événement étendu Deadlock pour capturer les informations nécessaires.

Outre la définition des propriétés des indicateurs de trace 1204 et 1222, le tableau suivant présente également les similitudes et les différences.

Property Indicateur de trace 1204 et indicateur de trace 1222 Indicateur de trace 1204 uniquement Indicateur de trace 1222 uniquement
Format de sortie La sortie est capturée dans le journal des erreurs de SQL Server. Les nœuds impliqués dans le blocage sont privilégiés. Chaque nœud dispose d'une section dédiée, tandis que la section finale décrit la victime du blocage. Retourne des informations dans un format de type XML, mais non conforme au schéma XSD (XML Schema Definition). Le format possède trois sections principales. La première déclare la victime du blocage. La deuxième décrit chaque processus impliqué dans le blocage. La troisième section décrit les ressources équivalentes à des nœuds dans l'indicateur de trace 1204.
Identification des attributs SPID:<x> ECID:<x>. Identifie le thread d’ID de session dans les cas de processus parallèles. L’entrée SPID:<x> ECID:0, où la valeur SPID remplace <x>, représente le thread principal. L’entrée SPID:<x> ECID:<y>, où <x> est remplacée par la valeur SPID et <y> est supérieure à 0, représente le contexte d’exécution du même SPID.

BatchID (sbid pour l’indicateur de trace 1222). Identifie le traitement à partir duquel l'exécution du code demande ou détient un verrou. Lorsque MARS (Multiple Active Result Sets) est désactivé, la valeur BatchID est 0. Quand MARS est activé, la valeur des lots actifs est 1 pour n. Si la session ne comporte pas de traitements actifs, BatchID a pour valeur 0.

Mode Spécifie, pour une ressource particulière, le type de verrou demandé, accordé ou attendu par un thread. Le mode peut être Intent partagé (IS), Partagé (S), Mise à jour (U), Intention exclusive (IX), Partagé avec intention exclusive (SIX) et Exclusif (X).

Line # (line pour l’indicateur de trace 1222). Indique le numéro de ligne du traitement qui était en cours d'exécution lorsque le blocage s'est produit.

Input Buf (inputbuf pour l’indicateur de trace 1222). Dresse la liste de toutes les instructions du traitement en cours.
Node Il s’agit du numéro d’entrée dans la chaîne d’interblocage.

Lists Le propriétaire du verrou peut faire partie des listes suivantes :

Grant List Énumère les propriétaires actuels de la ressource.

Convert List Énumère les propriétaires en cours qui essaient de convertir leurs verrous vers un niveau supérieur.

Wait List Énumère les nouvelles demandes de verrou en cours pour la ressource.

Statement Type Décrit le type d’instruction (SELECT, INSERT, UPDATEou DELETE) sur lequel les threads ont des autorisations.

Victim Resource Owner Spécifie le thread participant que le moteur de base de données choisit comme victime pour briser le cycle d’interblocage. Le thread choisi et tous ses contextes d’exécution sont arrêtés.

Next Branch Représente les deux contextes d’exécution ou plus du même SPID impliqué dans le cycle d’interblocage.
deadlock victim Représente l’adresse de mémoire physique de la tâche (consultez sys.dm_os_tasks) qui a été sélectionnée comme victime de l’interblocage. La valeur peut être égale à zéro en cas d’interblocage non résolu.

executionstack Représente la pile d’appels Transact-SQL en cours d’exécution au moment de l’interblocage.

priority Représente la priorité de l’interblocage.

logused Espace journal utilisé par la tâche.

owner id ID de la transaction qui contrôle la requête.

status État de la tâche. Pour plus d’informations, consultez sys.dm_os_tasks.

waitresource Ressource convoitée par la tâche.

waittime Délai d’attente de la ressource en millisecondes.

schedulerid Planificateur associé à cette tâche. Voir sys.dm_os_schedulers.

hostname Nom de la station de travail.

isolationlevel Niveau d’isolement des transactions en cours.

Xactid ID de la transaction qui contrôle la requête.

currentdb ID de la base de données.

lastbatchstarted Dernière fois qu’un processus client a démarré une exécution de traitement.

lastbatchcompleted Dernière fois qu’un processus client a terminé une exécution de traitement.

clientoption1 et clientoption2 l'ensemble des options définies pour cette session. Ces valeurs sont des masques de bits représentant les options généralement contrôlées par SET des instructions telles que SET NOCOUNT et SET XACTABORT. Pour plus d’informations, consultez @@OPTIONS.

associatedObjectId Représente l’ID HoBT (Heap ou B-tree, segment de mémoire ou arbre B).
Attributs des ressources RID identifie la ligne d’une table pour laquelle un verrou est détenu ou demandé. RID est représenté en tant que RID : db_id:file_id:page_no:row_no. Par exemple : RID: 6:1:20789:0.

OBJECT identifie la table pour laquelle un verrou est détenu ou demandé. OBJECT est représenté par OBJECT: db_id:object_id. Par exemple : TAB: 6:2009058193.

KEY Identifie la plage de clés d’un index pour laquelle un verrou est détenu ou demandé. KEY est représenté en tant que KEY : db_id:hobt_id (valeur de hachage de la clé d’index). Par exemple : KEY: 6:72057594057457664 (350007a4d329).

PAG Identifie la ressource de page pour laquelle un verrou est détenu ou demandé. PAG est représenté par PAG: db_id:file_id:page_no. Par exemple : PAG: 6:1:20789.

EXT Identifie la structure d’extension. EXT est représenté par EXT: db_id:file_id:extent_no. Par exemple : EXT: 6:1:9.

DB Identifie le verrou de base de données. DB est représenté de l’une des manières suivantes :

DB: db_id

DB: db_id[BULK-OP-DB], qui identifie le verrou de base de données pris par la sauvegarde de base de données.

DB: db_id[BULK-OP-LOG], qui identifie le verrou utilisé par la sauvegarde du journal.

APP Identifie un verrou d’application. APP est représenté par APP: lock_resource. Par exemple : APP: Formf370f478.

METADATA Représente les ressources de métadonnées impliquées dans un interblocage. Comme METADATA possède de nombreuses sous-ressources, la valeur retournée dépend de la sous-ressource bloquée. Par exemple, METADATA.USER_TYPE retourne user_type_id = *integer_value*. Pour en savoir plus sur les METADATA ressources et sous-ressources, consultez sys.dm_tran_locks.

HOBT Représente un segment de mémoire ou d’arbre B (B-Tree) impliqué dans un interblocage.
Non exclusif à cet indicateur de trace. Non exclusif à cet indicateur de trace.

Exemple d’indicateur de trace 1204

L’exemple suivant montre la sortie lorsque l’indicateur de trace 1204 est activé. Dans ce cas, la table du nœud 1 est un segment de mémoire sans index et la table du nœud 2 est un segment de mémoire avec un index non-cluster. La clé d'index du nœud 2 est en cours de mise à jour lorsque le blocage se produit.

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)

Exemple d’indicateur de trace 1222

L’exemple suivant montre la sortie lorsque l’indicateur de trace 1222 est activé. Dans ce cas, une table est un segment de mémoire sans index et l'autre table un segment de mémoire avec un index non-cluster. Dans la seconde table, la clé d'index est en cours de mise à jour lorsque le blocage se produit.

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=2022-02-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=2022-02-05T11:22:42.733
   lastbatchcompleted=2022-02-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=AdventureWorks2022.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=2022-02-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=2022-02-05T11:22:44.077
   lastbatchcompleted=2022-02-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=AdventureWorks2022.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=AdventureWorks2022.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=AdventureWorks2022.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

Evénement Deadlock Graph de SQL Profiler

SQL Profiler a un événement qui présente une représentation graphique des tâches et des ressources impliquées dans un interblocage. L’exemple suivant illustre la sortie obtenue à partir de SQL Profiler quand l’événement Deadlock Graph est activé.

Les fonctionnalités SQL Profiler et Trace SQL sont déconseillées et remplacées par les Événements Étendus. Les événements étendus ont une surcharge de performances plus petite et sont plus configurables que la trace SQL. Envisagez d’utiliser l’événement d’interblocage d’événements étendus au lieu de tracer des interblocages dans SQL Profiler.

Capture d’écran SSMS du graphe d’interblocage visuel à partir d’une trace SQL.

Pour plus d’informations sur l’événement de blocage, consultez Classe d’événements Lock:Deadlock. Pour plus d’informations sur les graphiques d’interblocage SQL Profiler, consultez Enregistrer les graphiques d’interblocage (SQL Server Profiler) .

Les événements étendus fournissent des équivalents de classes d’événements TRACE SQL. Pour plus d’informations, consultez Afficher les événements étendus équivalents aux classes d’événements trace SQL. Les événements étendus sont recommandés plutôt que la trace SQL.

Gérer les interblocages

Lorsqu’une instance du moteur de base de données choisit une transaction comme victime d’interblocage, elle met fin au lot actuel, restaure la transaction et retourne l’erreur 1205 à l’application. Le message retourné est structuré comme suit :

Your transaction (process ID #...) was deadlocked on {lock | communication buffer | thread} resources with another process and has been chosen as the deadlock victim. Rerun your transaction.

Étant donné que toute application qui envoie des requêtes Transact-SQL peut être choisie comme victime du blocage, les applications doivent avoir un gestionnaire d’erreurs qui peut gérer l’erreur 1205. Si une application ne gère pas l’erreur, l’application peut continuer à ignorer que sa transaction a été restaurée.

L’implémentation d’un gestionnaire d’erreurs qui intercepte l’erreur 1205 permet à une application de gérer les interblocages et de prendre des mesures correctives (par exemple, renvoyer automatiquement la requête impliquée dans le blocage).

L'application doit marquer un bref temps d'arrêt avant de soumettre à nouveau la requête. Cela donne à l’autre transaction impliquée dans l’interblocage une chance de terminer et de libérer ses verrous. La randomisation de la durée de la pause réduit la probabilité que l'interblocage se reproduise lorsque la requête resoumise requiert ses verrous. Par exemple, le gestionnaire d’erreurs peut être codé pour suspendre une durée aléatoire comprise entre un et trois secondes.

Gestion de TRY...CATCH

Vous pouvez utiliser un bloc TRY...CATCH afin de gérer les interblocages. L’erreur 1205 peut être interceptée par le CATCH bloc.

Pour plus d’informations, voir Gestion des interblocages.

Réduire les interblocages

Même si les interblocages ne peuvent pas être totalement évités, le respect de certaines conventions de codage peut minimiser le risque d’en générer. La réduction des blocages peut augmenter le débit des transactions et réduire la charge du système, car il y a moins de transactions :

  • restaurées, en annulant ce qui a été accompli par la transaction ;
  • resoumises par les applications, car ces transactions ont été restaurées lors du blocage.

Pour réduire le nombre de blocages :

  • Accédez aux objets dans le même ordre.
  • Évitez les interactions utilisateur dans les transactions.
  • Créez des transactions courtes dans le même traitement.
  • Évitez les niveaux d’isolation plus élevés, tels que REPEATABLE READ et SERIALIZABLE quand cela n’est pas nécessaire.
  • Utilisez un niveau d'isolement basé sur le contrôle de version de ligne.
    • Activez l’option de base de données pour utiliser le READ_COMMITTED_SNAPSHOT contrôle de version de ligne pour les transactions à l’aide du READ COMMITTED niveau d’isolation.
    • Utilisez des transactions d'isolation instantanée.
  • Utilisez des connexions liées.

Accédez aux objets dans le même ordre

Si toutes les transactions concurrentes accèdent aux objets dans le même ordre, le risque de blocage diminue. Par exemple, si deux transactions concurrentes obtiennent un verrou sur la table Supplier, puis sur la table Part, l’une des transactions est bloquée sur la table Supplier jusqu’à ce que l’autre transaction se termine. Après la validation ou la restauration de la première transaction, la seconde continue et aucun interblocage ne se produit. L'utilisation de procédures stockées pour toutes les modifications de données peut standardiser l'ordre d'accès aux objets.

Schéma d’un interblocage.

Évitez les interactions utilisateur dans les transactions

Évitez les transactions qui incluent l’interaction utilisateur, car la vitesse d’exécution des lots sans intervention de l’utilisateur est beaucoup plus rapide que la vitesse à laquelle un utilisateur doit répondre manuellement aux requêtes, telles que la réponse à une invite d’un paramètre demandé par une application. Ceci dégrade les performances du système, car tous les verrous détenus par la transaction ne sont libérés qu'une fois la transaction validée ou restaurée. Même si un blocage ne se produit pas, d’autres transactions accédant aux mêmes ressources sont bloquées en attendant la fin de la transaction.

Transactions courtes dans un seul traitement

Un blocage se produit souvent lorsque plusieurs transactions longues sont exécutées de manière concurrente dans la même base de données. Plus la transaction est longue, plus la durée de détention du verrou exclusif ou de mise à jour est importante, ce qui bloque les autres activités et peut entraîner une situation de blocage.

La conservation des transactions dans un lot vise à minimiser les allers-retours réseau pendant une transaction, réduisant ainsi les éventuels retards lors de l'achèvement de la transaction en raison du traitement par le client.

Éviter des niveaux d’isolation plus élevés

Déterminez si une transaction peut être exécutée à un niveau d'isolement faible. L’utilisation READ COMMITTED permet à une transaction de lire des données précédemment lues (mais non modifiées) par une autre transaction sans attendre la fin de la transaction. READ COMMITTED contient des verrous partagés pendant une durée plus courte qu’un niveau d’isolation supérieur, tel que SERIALIZABLE. Cela réduit la contention de verrou.

Niveau d’isolement basé sur le contrôle de version de ligne

Lorsque l’option READ_COMMITTED_SNAPSHOT de base de données est définie ON, une transaction exécutée sous le niveau d’isolation utilise le READ COMMITTED contrôle de version de ligne plutôt que les verrous partagés pendant les opérations de lecture.

Conseil / Astuce

Microsoft recommande pour toutes les applications le niveau d’isolation basé sur le contrôle de version des lignes READ COMMITTED, sauf si une application s’appuie sur le comportement de blocage du niveau d’isolation basé sur le verrouillage READ COMMITTED.

L’isolement d’instantané utilise également le contrôle de version de ligne, qui n’emploie pas de verrous partagés pendant les opérations de lecture. Pour qu’une transaction puisse s’exécuter sous une isolation d’instantané, l’option de base de données ALLOW_SNAPSHOT_ISOLATION doit avoir la valeur ON.

Utilisez les niveaux d’isolation basés sur le contrôle de version des lignes pour réduire les interblocages pouvant se produire entre les opérations de lecture et d’écriture.

Utiliser des connexions liées

En utilisant des connexions liées, deux connexions ou plus ouvertes par la même application peuvent coopérer entre elles. Tout verrou acquis par la connexion secondaire apparaît comme s'il avait été posé par la connexion primaire et vice-versa. Par conséquent, ils ne se bloquent pas les uns les autres.

Provoquer un interblocage

Vous devrez peut-être provoquer un blocage à des fins d’apprentissage ou de démonstration.

L’exemple suivant fonctionne dans l’exemple AdventureWorksLT2019 de base de données avec le schéma et les données par défaut lorsque READ_COMMITTED_SNAPSHOT a été activé. Pour télécharger cet exemple, consultez Exemples de bases de données AdventureWorks.

Pour obtenir un exemple qui provoque un interblocage lorsque le verrouillage optimisé est activé, consultez Verrouillage optimisé et blocages.

Pour provoquer un interblocage, vous devez connecter deux sessions à la base de données AdventureWorksLT2019. Nous faisons référence à ces sessions en tant que session A et session B. Vous pouvez créer ces deux sessions en créant deux fenêtres de requête dans SQL Server Management Studio (SSMS).

Dans la session A, exécutez le lot suivant. Ce code commence une transaction explicite et exécute une instruction qui met à jour la SalesLT.Product table. Pour ce faire, la transaction acquiert un verrou de mise à jour (U) sur les lignes éligibles de la table SalesLT.Product qui sont ensuite converties en verrous exclusifs (X). Nous laissons la transaction ouverte.

BEGIN TRANSACTION;

UPDATE SalesLT.Product
    SET SellEndDate = SellEndDate + 1
WHERE Color = 'Red';

À présent, dans la session B, exécutez le lot suivant. Ce code ne démarre pas explicitement une transaction. Il travaille plutôt en mode de validation automatique. Cette instruction met à jour la table SalesLT.ProductDescription. La mise à jour prend un verrou de mise à jour (U) sur les lignes éligibles dans la table SalesLT.ProductDescription. La requête se joint à d’autres tables, notamment la table 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';

Pour effectuer cette mise à jour, la session B a besoin de verrous partagés (S) sur les lignes de la table SalesLT.Product, y compris les lignes verrouillées par session A. La session B est bloquée sur SalesLT.Product.

Revenez à la session A. Exécutez l’instruction suivante UPDATE . Cette instruction s’exécute dans le cadre de la transaction ouverte précédemment.

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

La deuxième instruction de mise à jour de la session A est bloquée par la session B sur SalesLT.ProductDescription.

La session A et la session B se bloquent mutuellement. Aucune des transactions ne peut se poursuivre, car chacune a besoin d’une ressource verrouillée par l’autre.

Au bout de quelques secondes, le processus de surveillance des interblocages identifie que les transactions de la session A et de la session B se bloquent mutuellement, et qu’aucune ne peut progresser. Vous voyez qu’un interblocage se produit, avec la session A choisie comme victime de blocage. La session B se finalise avec succès. Un message d’erreur s’affiche dans la fenêtre de requête de session A avec du texte similaire à l’exemple suivant :

Msg 1205, Level 13, State 51, Line 7
Transaction (Process ID 51) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

Si aucun interblocage n’est déclenché, vérifiez que READ_COMMITTED_SNAPSHOT est bien activé dans votre exemple de base de données. Les interblocages peuvent se produire dans n’importe quelle configuration de base de données, mais dans cet exemple, READ_COMMITTED_SNAPSHOT doit être activé.

Vous pouvez afficher les détails du blocage dans la ring_buffer cible de la system_health session d’événements, qui est activée et active par défaut dans SQL Server et Azure SQL Managed Instance. Considérez la requête suivante :

WITH cteDeadLocks ([Deadlock_XML])
AS (SELECT CAST (target_data AS XML) AS [Deadlock_XML]
    FROM sys.dm_xe_sessions AS xs
         INNER JOIN sys.dm_xe_session_targets AS xst
             ON xs.[address] = xst.event_session_address
    WHERE xs.[name] = 'system_health'
          AND xst.target_name = 'ring_buffer')
SELECT x.Graph.query('(event/data/value/deadlock)[1]') AS Deadlock_XML,
       x.Graph.value('(event/data/value/deadlock/process-list/process/@lastbatchstarted)[1]', 'datetime2(3)') AS when_occurred,
       DB_Name(x.Graph.value('(event/data/value/deadlock/process-list/process/@currentdb)[1]', 'int')) AS DB --Current database of the first listed process
FROM (SELECT Graph.query('.') AS Graph
      FROM cteDeadLocks AS c
      CROSS APPLY c.[Deadlock_XML].nodes('RingBufferTarget/event[@name="xml_deadlock_report"]') AS Deadlock_Report(Graph)) AS x
ORDER BY when_occurred DESC;

Vous pouvez afficher le code XML dans la colonne Deadlock_XML au sein de SSMS en sélectionnant la cellule qui apparaît sous forme de lien hypertexte. Enregistrez cette sortie sous la forme d’un fichier .xdl, fermez-le, puis ouvrez de nouveau le fichier .xdl dans SSMS pour le graphe d’interblocage visuel. Le graphique d’interblocage doit ressembler à l’image suivante.

Capture d’écran d’un graphe d’interblocage visuel dans un fichier .xdl dans SSMS.

Verrouillage optimisé et interblocages

Avec le verrouillage optimisé, les verrous de page et de ligne ne sont pas conservés jusqu’à la fin de la transaction. Ils sont libérés dès qu’une ligne est mise à jour. En outre, si READ_COMMITTED_SNAPSHOT est activée, les verrous de mise à jour (U) ne sont pas utilisés. Par conséquent, la probabilité d’interblocages est réduite.

L’exemple précédent ne provoque pas d’interblocage lorsque le verrouillage optimisé est activé, car il s’appuie sur les verrous de mise à jour (U).

L’exemple suivant peut être utilisé pour provoquer un blocage sur une base de données qui a optimisé le verrouillage activé.

Tout d’abord, créez un exemple de table et ajoutez des données.

CREATE TABLE t2
(
    a INT PRIMARY KEY NOT NULL,
    b INT NULL
);

INSERT INTO t2
VALUES (1, 10),
(2, 20),
(3, 30);

Les lots T-SQL suivants, exécutés dans une séquence dans deux sessions distinctes, créent un interblocage.

Dans la session 1 :

BEGIN TRANSACTION xactA;

UPDATE t2
    SET b = b + 10
WHERE a = 1;

Dans la session 2 :

BEGIN TRANSACTION xactB;

UPDATE t2
    SET b = b + 10
WHERE a = 2;

Dans la session 1 :

UPDATE t2
    SET b = b + 100
WHERE a = 2;

Dans la session 2 :

UPDATE t2
    SET b = b + 20
WHERE a = 1;

Dans ce cas, chaque session contient un verrou exclusif (X) sur sa propre ressource d’ID de transaction (TID) et attend le verrou partagé (S) sur l’autre TID, ce qui entraîne un blocage.

Le rapport d’interblocage abrégé suivant contient des éléments et des attributs spécifiques au verrouillage optimisé. Sous chaque ressource du rapport <resource-list>d’interblocage, chaque <xactlock> élément signale les ressources sous-jacentes et les informations de verrouillage TID de chaque membre d’un interblocage.

<deadlock>
 <victim-list>
  <victimProcess id="process12994344c58" />
 </victim-list>
 <process-list>
  <process id="process12994344c58" taskpriority="0" logused="272" waitresource="XACT: 23:2476:0 KEY: 23:72057594049593344 (8194443284a0)" waittime="447" ownerId="3234906" transactionname="xactA" lasttranstarted="2025-10-08T21:36:34.063" XDES="0x12984ba0480" lockMode="S" schedulerid="2" kpid="204928" status="suspended" spid="95" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2025-10-08T21:36:40.857" lastbatchcompleted="2025-10-08T21:36:34.063" lastattention="2025-10-08T21:36:11.340" clientapp="Microsoft SQL Server Management Studio - Query" hostname="WS1" hostpid="23380" loginname="user1" isolationlevel="read committed (2)" xactid="3234906" currentdb="23" currentdbname="AdventureWorksLT" lockTimeout="4294967295" clientoption1="671090784" clientoption2="390200">
   <inputbuf>
UPDATE t2
    SET b = b + 20
WHERE a = 1;
   </inputbuf>
  </process>
  <process id="process1299c969828" taskpriority="0" logused="272" waitresource="XACT: 23:2477:0 KEY: 23:72057594049593344 (61a06abd401c)" waittime="3083" ownerId="3234886" transactionname="xactB" lasttranstarted="2025-10-08T21:36:30.303" XDES="0x12995c84480" lockMode="S" schedulerid="2" kpid="63348" status="suspended" spid="88" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2025-10-08T21:36:38.223" lastbatchcompleted="2025-10-08T21:36:30.303" lastattention="1900-01-01T00:00:00.303" clientapp="Microsoft SQL Server Management Studio - Query" hostname="WS1" hostpid="23380" loginname="user1" isolationlevel="read committed (2)" xactid="3234886" currentdb="23" currentdbname="AdventureWorksLT" lockTimeout="4294967295" clientoption1="671090784" clientoption2="390200">
   <inputbuf>
UPDATE t2
    SET b = b + 100
WHERE a = 2;
   </inputbuf>
  </process>
 </process-list>
 <resource-list>
  <xactlock xdesIdLow="2476" xdesIdHigh="0" dbid="23" id="lock1299fa06c00" mode="X">
   <UnderlyingResource>
    <keylock hobtid="72057594049593344" dbid="23" objectname="e6fc405e-1ee8-49df-a2b3-54ee0151d851.dbo.t2" indexname="PK__t2__3BD0198ED3CBA65E" />
   </UnderlyingResource>
   <owner-list>
    <owner id="process1299c969828" mode="X" />
   </owner-list>
   <waiter-list>
    <waiter id="process12994344c58" mode="S" requestType="wait" />
   </waiter-list>
  </xactlock>
  <xactlock xdesIdLow="2477" xdesIdHigh="0" dbid="23" id="lock129940b2380" mode="X">
   <UnderlyingResource>
    <keylock hobtid="72057594049593344" dbid="23" objectname="e6fc405e-1ee8-49df-a2b3-54ee0151d851.dbo.t2" indexname="PK__t2__3BD0198ED3CBA65E" />
   </UnderlyingResource>
   <owner-list>
    <owner id="process12994344c58" mode="X" />
   </owner-list>
   <waiter-list>
    <waiter id="process1299c969828" mode="S" requestType="wait" />
   </waiter-list>
  </xactlock>
 </resource-list>
</deadlock>