Compartir a través de


Guía de interbloqueos

Aplica a:SQL ServerAzure SQL DatabaseAzure SQL Managed InstanceAzure Synapse AnalyticsSistema de Plataforma de Analítica (PDW)Base de datos SQL en Microsoft Fabric

En este artículo se describen los interbloqueos en el sistema de gestión de bases de datos de manera exhaustiva. Los interbloqueos se producen debido a bloqueos simultáneos que compiten entre sí en la base de datos, a menudo en transacciones de varios pasos. Para obtener más información sobre las transacciones y los bloqueos, consulte la Guía de bloqueo de transacciones y control de versiones de filas.

Para obtener información más específica sobre la identificación y prevención de interbloqueos en Azure SQL Database y SQL Database en Fabric, consulte Análisis y prevención de interbloqueos en Azure SQL Database y SQL Database en Fabric.

Comprender los interbloqueos

Un interbloqueo se produce cuando dos o más tareas se bloquean entre sí permanentemente teniendo cada tarea un bloqueo en un recurso que las otras tareas intentan bloquear. Por ejemplo:

  • La transacción A adquiere un bloqueo compartido de la fila 1.

  • La transacción B adquiere un bloqueo compartido de la fila 2.

  • Ahora la transacción A solicita un bloqueo exclusivo de la fila 2 y se bloquea hasta que la transacción B finalice y libere el bloqueo compartido que tiene de la fila 2.

  • Ahora la transacción B solicita un bloqueo exclusivo de la fila 1 y se bloquea hasta que la transacción A finalice y libere el bloqueo compartido que tiene de la fila 1.

La transacción A no puede completarse hasta que se complete la transacción B, pero la transacción B está bloqueada por la transacción A. Esta condición también se llama dependencia cíclica: la transacción A tiene una dependencia de la transacción B y la transacción B cierra el círculo teniendo una dependencia de la transacción A.

Ambas transacciones con un interbloqueo esperan para siempre, a no ser que un proceso externo rompa el interbloqueo. El monitor de interbloqueo del motor de base de datos comprueba periódicamente las tareas que están en un interbloqueo. Si el monitor detecta una dependencia cíclica, elige una de las tareas como el sujeto y finaliza su transacción con un error. Esto permite a la otra tarea completar su transacción. La aplicación con la transacción que terminó con un error puede reintentar la transacción, que suele completarse después de que la otra transacción interbloqueada finalice.

A menudo se confunden los interbloqueos con los bloqueos normales. Cuando una transacción solicita un bloqueo en un recurso bloqueado por otra transacción, la transacción solicitante espera hasta que se libere el bloqueo. De forma predeterminada, las transacciones del Motor de Base de Datos no se agotan, a menos que LOCK_TIMEOUT esté configurado. La transacción solicitante está bloqueada, no interbloqueada, porque la transacción solicitante no ha hecho nada para bloquear la transacción a la que pertenece el bloqueo. Finalmente, la transacción a la que pertenece el bloqueo se completa y libera el bloqueo, y a la transacción solicitante se le concede el bloqueo y continúa. Los interbloqueos se resuelven casi inmediatamente, mientras que el bloqueo puede, en teoría, persistir indefinidamente. A veces, los interbloqueos se denominan "abrazo mortal".

Un interbloqueo puede producirse en cualquier sistema con varios subprocesos, no solo en un sistema de administración de bases de datos relacionales, y puede producirse para recursos distintos a los bloqueos en objetos de base de datos. Por ejemplo, un subproceso en un sistema operativo con varios subprocesos puede adquirir uno o más recursos, como bloqueos de memoria. Si el recurso que se va a adquirir pertenece actualmente a otro subproceso, es posible que el primer subproceso deba esperar a que el otro libere el recurso de destino. En consecuencia, se dice que el subproceso que está en espera depende del subproceso que posee ese recurso concreto. En una instancia del motor de base de datos, las sesiones pueden bloquearse al adquirir recursos que no son propios de la base de datos, como la memoria o los subprocesos.

Diagrama que muestra un bloqueo de transacción.

En la ilustración, la transacción T1 tiene una dependencia de la transacción T2 para el recurso de bloqueo de la tabla Part. Del mismo modo, la transacción T2 tiene una dependencia de la transacción T1 para el recurso de bloqueo de la tabla Supplier. Puesto que estas dependencias forman un ciclo, hay un interbloqueo entre las transacciones T1 y T2.

Esta es una ilustración más general de un interbloqueo:

Diagrama en el que se muestran tareas en un estado de bloqueo.

  • La tarea T1 tiene un bloqueo en el recurso R1 (indicado por la flecha de R1 a T1) y ha solicitado un bloqueo en el recurso R2 (indicado por la flecha de T1 a R2).

  • La tarea T2 tiene un bloqueo en el recurso R2 (indicado por la flecha de R2 a T2) y ha solicitado un bloqueo en el recurso R1 (indicado por la flecha de T2 a R1).

  • Dado que ninguna tarea puede continuar hasta que un recurso esté disponible y ningún recurso puede liberarse hasta que continúe una tarea, existe un estado de interbloqueo.

Note

El motor de base de datos detecta automáticamente ciclos de interbloqueo. Elige una de las transacciones como víctima de bloqueo y la finaliza generando un error para interrumpir el bloqueo.

Recursos que pueden causar interbloqueos

Cada sesión de usuario puede tener una o más tareas en ejecución y cada tarea puede adquirir recursos o esperar para adquirirlos. Los siguientes tipos de recursos pueden causar bloqueos que podrían dar como resultado un interbloqueo.

  • Locks. Esperar para adquirir bloqueos en recursos, como objetos, páginas, filas, metadatos y aplicaciones, puede causar un interbloqueo. Por ejemplo, la transacción T1 tiene un bloqueo compartido (S) en la fila r1 y está esperando obtener un bloqueo exclusivo (X) en r2. La transacción T2 tiene un bloqueo compartido (S) en r2 y está esperando obtener un bloqueo exclusivo (X) en la fila r1. Esta situación tiene como resultado un ciclo de bloqueo en el que T1 y T2 esperan que la otra transacción libere los recursos bloqueados.

  • Subprocesos de trabajo. Una tarea en cola que espera un subproceso de trabajo disponible puede causar un interbloqueo. Si la tarea en cola es propietaria de recursos que están bloqueando todos los subprocesos de trabajo, se genera un interbloqueo. Por ejemplo, la sesión S1 inicia una transacción y adquiere un bloqueo compartido (S) en la fila r1 y luego se pone en espera. Las sesiones activas que se ejecutan en todos los hilos de trabajo disponibles intentan adquirir bloqueos exclusivos (X) en la fila r1. Dado que la sesión S1 no puede adquirir un subproceso de trabajo, no puede confirmar la transacción y liberar el bloqueo de la fila r1. Esta situación tiene como resultado un interbloqueo.

  • Memory. Cuando hay solicitudes simultáneas esperando concesiones de memoria que no se pueden satisfacer con la memoria disponible, puede producirse un interbloqueo. Por ejemplo, dos consultas simultáneas, C1 y C2, se ejecutan como funciones definidas por el usuario que adquieren 10 MB y 20 MB de memoria, respectivamente. Si cada consulta necesita 30 MB y el total de memoria disponible es 20 MB, C1 y C2 tienen que esperar a que la otra consulta libere memoria, lo que da lugar a un interbloqueo.

  • Recursos relacionados con la ejecución de consultas en paralelo. Los subprocesos de coordinador, productor o consumidor asociados a un puerto de intercambio se podrían bloquear entre sí y provocar un interbloqueo si incluyen al menos otro proceso que no forme parte de la consulta en paralelo. Además, cuando una consulta paralela inicia la ejecución, el motor de base de datos determina el grado de paralelismo y el número de subprocesos de trabajo necesarios, en función de la carga de trabajo actual. Si la carga de trabajo del sistema cambia de forma inesperada, por ejemplo, si se empiezan a ejecutar nuevas consultas en el servidor o el sistema se queda sin subprocesos de trabajo, se puede producir un interbloqueo.

  • Conjuntos de resultados activos múltiples (MARS). Estos recursos se utilizan para controlar la intercalación de varias solicitudes activas en MARS. Para obtener más información, consulte Uso de conjuntos de resultados activos múltiples (MARS) en SQL Server Native Client.

    • Recurso de usuario. Cuando un subproceso espera un recurso que potencialmente está controlado por una aplicación de usuario, se considera que el recurso es externo o de usuario y se trata como un bloqueo.

    • Mutex de sesión. Las tareas que se ejecutan en una sesión se intercalan, lo que significa que solo puede ejecutarse una tarea en la sesión en un momento dado. Antes de que se pueda ejecutar la tarea, debe tener acceso exclusivo a la exclusión mutua de sesión.

    • Exclusión mutua de transacciones. Todas las tareas que se ejecutan en una transacción se intercalan, lo que significa que solo puede ejecutarse una tarea en la transacción en un momento dado. Antes de que se pueda ejecutar la tarea, debe tener acceso exclusivo a la exclusión mutua de transacción.

      Para que una tarea se ejecute en MARS, debe adquirir la exclusión mutua de sesión. Si la tarea se ejecuta en una transacción, debe adquirir la exclusión mutua de transacción. Esto garantiza que solo una tarea esté activa en un momento dado en una sesión determinada y en una transacción concreta. Una vez adquiridas las exclusiones mutuas necesarias, se puede ejecutar la tarea. Cuando finaliza la tarea, o se produce en medio de la solicitud, primero libera la exclusión mutua de transacción, seguida de la exclusión mutua de sesión en el orden inverso a la adquisición. Sin embargo, pueden producirse interbloqueos con estos recursos. En el pseudocódigo siguiente hay dos tareas, la solicitud de usuario U1 y la solicitud de usuario U2, que se ejecutan en la misma sesión.

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

      El procedimiento almacenado que se ejecuta a partir de la solicitud de usuario U1 ha adquirido la exclusión mutua de sesión. Si el procedimiento almacenado tarda mucho tiempo en ejecutarse, el motor de base de datos asume que el procedimiento almacenado está esperando la entrada del usuario. La solicitud de usuario U2 está esperando la exclusión mutua de sesión mientras que el usuario está esperando el conjunto de resultados de U2, y U1 está esperando un recurso de usuario. Éste es un estado de interbloqueo que se ilustra de forma lógica como:

      Diagrama del flujo lógico de un procedimiento almacenado en MARS.

Los interbloqueos también pueden producirse cuando se crean particiones en una tabla y el valor de LOCK_ESCALATION de ALTER TABLE se ha establecido en AUTO. Cuando LOCK_ESCALATION se establece en AUTO, aumenta la simultaneidad al permitir que el Motor de Base de Datos bloquee las particiones de la tabla a nivel de HoBT en lugar de a nivel de tabla. Sin embargo, cuando transacciones independientes mantienen bloqueos de partición en una tabla y desean un bloqueo en algún punto de la partición de otras transacciones, se produce un interbloqueo. Este tipo de interbloqueo se puede evitar estableciendo LOCK_ESCALATION en TABLE. Sin embargo, esta configuración reduce la simultaneidad, pues fuerza que las actualizaciones grandes de una partición esperen a un bloqueo de tabla.

Detección de interbloqueos

Todos los recursos enumerados en la sección Recursos que pueden interbloquear participan en el esquema de detección de interbloqueo del motor de base de datos. La detección de interbloqueos se realiza mediante un subproceso de supervisión de bloqueo que inicia periódicamente una búsqueda en todas las tareas de una instancia del motor de base de datos. En los siguientes puntos se describe el proceso de búsqueda:

  • El intervalo predeterminado es de 5 segundos.

  • Si el subproceso de supervisión de bloqueos encuentra interbloqueos, el intervalo de detección de interbloqueos cae de 5 segundos a hasta solo 100 milisegundos, en función de la frecuencia de los interbloqueos.

  • Si el subproceso del monitor de bloqueo deja de encontrar interbloqueos, el motor de base de datos aumenta los intervalos entre las búsquedas a 5 segundos.

  • Si se detecta un interbloqueo, se supone que los nuevos subprocesos que deben esperar por un bloqueo están entrando en el ciclo de interbloqueo. Las primeras esperas de bloqueo siguientes a la detección de un interbloqueo desencadenan de inmediato una búsqueda de interbloqueos, en vez de esperar al siguiente intervalo de detección de interbloqueos. Por ejemplo, si el intervalo actual es de 5 segundos y se acaba de detectar un interbloqueo, la siguiente espera de bloqueo activa de inmediato el detector de interbloqueos. Si esta espera de bloqueo forma parte de un interbloqueo, se detecta de inmediato, en lugar de hacerlo durante la siguiente búsqueda de interbloqueo.

Normalmente, el motor de base de datos realiza la detección periódica de interbloqueos. Dado que el número de interbloqueos encontrados en el sistema suele ser pequeño, la detección periódica de interbloqueos ayuda a reducir la sobrecarga de la detección de interbloqueos en el sistema.

Cuando el monitor de bloqueos inicia una búsqueda de interbloqueos para un subproceso determinado, identifica el recurso que está esperando. Después, el monitor de bloqueos encuentra a los propietarios de ese recurso y continúa recursivamente la búsqueda de interbloqueos para esos subprocesos hasta que encuentra un ciclo. Un ciclo que se identifica de esta manera forma un interbloqueo.

Una vez detectado un interbloqueo, el motor de base de datos finaliza un interbloqueo eligiendo uno de los subprocesos como víctima de interbloqueo. El Motor de Base de Datos finaliza el lote actual que se está ejecutando para el hilo, revierte la transacción de la víctima del interbloqueo y devuelve el error 1205 a la aplicación. Revertir la transacción para el sujeto del interbloqueo libera todos los bloqueos que tiene la transacción. Esto permite que las transacciones de otros subprocesos se desbloqueen y continúen. El error 1205 (víctima de interbloqueo) registra información sobre el tipo de recursos implicados en un interbloqueo.

De forma predeterminada, el motor de base de datos elige la transacción menos costosa de revertir como víctima del interbloqueo. Como alternativa, un usuario puede especificar la prioridad de las sesiones en una situación de interbloqueo mediante la instrucción SET DEADLOCK_PRIORITY. DEADLOCK_PRIORITY se puede establecer en LOW, NORMAL, o HIGH, o también se puede establecer en cualquier valor entero en el rango de -10 a 10. En algunos casos, el motor de base de datos podría optar por modificar la prioridad de interbloqueo durante un breve período para lograr una mejor simultaneidad.

La prioridad de interbloqueo tiene NORMALcomo valor predeterminado , o 0. Si dos sesiones tienen prioridades de interbloqueo diferentes, la transacción en la sesión con la prioridad más baja se elige como víctima del interbloqueo. Si ambas sesiones tienen la misma prioridad de interbloqueo, se elige la transacción cuya reversión es menos costosa. Si las sesiones implicadas en el ciclo de interbloqueo tienen la misma prioridad de interbloqueo y el mismo costo, se elige un sujeto de forma aleatoria. Una tarea que se está revirtiendo no se puede seleccionar como sujeto del interbloqueo.

Cuando se trabaja con Common Language Runtime (CLR), el monitor de interbloqueo detecta automáticamente interbloqueos para los recursos de sincronización (monitores, bloqueo de lector/escritor y unión de subprocesos) a los que se accede dentro de los procedimientos administrados. Si embargo, el interbloqueo se resuelve iniciando una excepción en el procedimiento que se seleccionó como sujeto del interbloqueo. Es importante comprender que la excepción no libera automáticamente los recursos que posee actualmente el sujeto; los recursos se tienen que liberar de forma explícita. De forma coherente con el comportamiento de la excepción, la excepción utilizada para identificar un sujeto del interbloqueo se puede interceptar y descartar.

Herramientas de información de interbloqueos

Para ver la información de interbloqueo, el Motor de Base de Datos proporciona herramientas de supervisión en la forma de un evento extendido, dos etiquetas de rastreo y el evento de gráfico de interbloqueo en SQL Profiler.

El xml_deadlock_report evento extendido es el método recomendado para capturar información de interbloqueo.

Evento ampliado de interbloqueo

En SQL Server 2012 (11.x) y versiones posteriores, se debe utilizar el evento extendido xml_deadlock_report en lugar de la clase de evento de grafo de interbloqueo en SQL Trace o SQL Profiler.

La sesión de eventos system_health captura eventos xml_deadlock_report de forma predeterminada. Estos eventos contienen el grafo de interbloqueo. Dado que la system_health sesión está habilitada de forma predeterminada, no es necesario configurar una sesión de eventos independiente para capturar información de interbloqueo.

El grafo de interbloqueo que se capturaba normalmente consta de tres nodos distintos:

  • victim-list. Identificador de proceso del elemento afectado por el interbloqueo.
  • process-list. Información sobre todos los procesos implicados en el interbloqueo.
  • resource-list. Información sobre todos los recursos implicados en el interbloqueo.

Puede ver los event_file datos de destino de la system_health sesión en Management Studio. Si se ha producido algún xml_deadlock_report evento, Management Studio presenta una representación gráfica de las tareas y los recursos implicados en un interbloqueo, como se muestra en el ejemplo siguiente:

Captura de pantalla de SSMS de un diagrama visual del grafo de interbloqueo de XEvent.

La consulta siguiente puede ver todos los eventos de interbloqueo capturados por el ring_buffer destino de la system_health sesión:

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;

Este es el conjunto de resultados.

Captura de pantalla de SSMS del resultado de la consulta system_health de XEvent.

En el ejemplo siguiente se muestra un ejemplo de la salida de la event_data columna:

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

Marca de seguimiento 1204 y marca de seguimiento 1222

Cuando se producen interbloqueos y la marca de seguimiento 1204 o la marca de seguimiento 1222 está habilitada, se notifican los detalles del interbloqueo en el registro de errores de SQL Server. La marca de seguimiento 1204 proporciona información de bloqueo mutuo formateada por cada nodo implicado en el bloqueo mutuo. La marca de seguimiento 1222 da formato a la información de interbloqueo, primero por procesos y luego por recursos. Es posible habilitar ambas marcas de seguimiento para obtener dos representaciones del mismo evento de interbloqueo.

Important

Evite usar marcas de seguimiento 1204 y 1222 en sistemas con cargas de trabajo intensivas que experimentan interbloqueos. Es posible que el uso de estas marcas de seguimiento genere problemas de rendimiento. En su lugar, use el evento extendido Deadlock para capturar la información necesaria.

Además de definir las propiedades de las marcas de seguimiento 1204 y 1222, en la tabla siguiente también se muestran las similitudes y las diferencias.

Property Marca de seguimiento 1204 y marca de seguimiento 1222 Solo la marca de seguimiento 1204 Marca de seguimiento 1222 solo
Formato de salida Los resultados se capturan en el registro de errores de SQL Server. Se centra en los nodos implicados en el interbloqueo. Cada nodo tiene una sección dedicada y la última sección describe al sujeto del interbloqueo. Devuelve información en un formato XML que no se ajusta a un esquema de definición de esquemas XML (XSD). El formato tiene tres secciones principales. La primera sección declara el sujeto del interbloqueo. La segunda sección describe los procesos implicados en el interbloqueo. En la tercera sección se describen los recursos que son sinónimos de nodos en la marca de seguimiento 1204.
Identificación de atributos SPID:<x> ECID:<x>. Identifica el hilo del ID de sesión en casos de procesos paralelos. La entrada SPID:<x> ECID:0, donde <x> se sustituye por el valor del SPID, representa el subproceso principal. La entrada SPID:<x> ECID:<y>, donde <x> se reemplaza por el valor SPID y <y> es mayor que 0, representa el contexto de ejecución para el mismo SPID.

BatchID (sbid para la marca de seguimiento 1222). Identifica el lote desde el que la ejecución del código está solicitando o manteniendo un bloqueo. Cuando se deshabilita Multiple Active Result Sets (MARTE), el valor de BatchID es 0. Cuando se habilita MART, el valor para los lotes activos es 1 para n. Si en la sesión no hay lotes activos, BatchID es 0.

Mode Especifica el tipo de bloqueo de un recurso en concreto que un subproceso solicita, concede o espera. El modo puede ser Compartido de Intención (IS), Compartido (S), Actualización (U), Intención Exclusiva (IX), Compartido con Intención Exclusiva (SIX) y Exclusivo (X).

Line # (line para la marca de seguimiento 1222). Indica el número de línea en el lote actual de instrucciones que se estaba ejecutando cuando se produjo el interbloqueo.

Input Buf (inputbuf para la marca de seguimiento 1222). Indica todas las instrucciones del lote actual.
Node Representa el numero de entrada en la cadena de interbloqueo.

Lists El propietario del bloqueo puede formar parte de estas listas:

Grant List Enumera los propietarios actuales del recurso.

Convert List Enumera los propietarios actuales que están intentando convertir sus bloqueos a un nivel más alto.

Wait List Enumera las solicitudes actuales del nuevo bloqueo para el recurso.

Statement Type Describe el tipo de instrucción (SELECT, INSERT, UPDATEo DELETE) en el que los subprocesos tienen permisos.

Victim Resource Owner Especifica el subproceso participante que el motor de base de datos elige como víctima para interrumpir el ciclo de interbloqueo. El subproceso elegido y todos sus contextos de ejecución finalizan.

Next Branch Representa los dos o más contextos de ejecución del mismo SPID implicados en el ciclo de interbloqueo.
deadlock victim Representa la dirección de la memoria física de la tarea (consulte sys.dm_os_tasks) que se seleccionó como sujeto del interbloqueo. El valor puede ser cero en el caso de un interbloqueo sin resolver.

executionstack Representa la Transact-SQL pila de llamadas que se ejecuta en el momento en que se produce el interbloqueo.

priority Representa la prioridad de interbloqueo.

logused Espacio de registro utilizado por la tarea.

owner id El Id. de la transacción que tiene el control de la solicitud.

status Estado de la tarea. Para obtener más información, consulte sys.dm_os_tasks.

waitresource El recurso que la tarea necesita.

waittime Tiempo en milisegundos de espera del recurso.

schedulerid El planificador asociado a esta tarea. Consulte sys.dm_os_schedulers.

hostname El nombre de la estación de trabajo.

isolationlevel El nivel de aislamiento de transacción actual.

Xactid El Id. de la transacción que tiene el control de la solicitud.

currentdb El Id. de la base de datos.

lastbatchstarted La última vez que un proceso de cliente inició la ejecución de lotes.

lastbatchcompleted La última vez que un proceso de cliente completó la ejecución de lotes.

clientoption1 y clientoption2 Las opciones establecidas en esta sesión. Estos valores son máscaras de bits que representan las opciones controladas normalmente por SET instrucciones como SET NOCOUNT y SET XACTABORT. Para obtener más información, consulte @@OPTIONS.

associatedObjectId Representa el identificador del montón o árbol B (HoBt).
Atributos del recurso RID Identifica la única fila de una tabla en la que se mantiene o se solicita un bloqueo. RID se representa como RID: db_id:file_id:page_no:row_no. Por ejemplo, RID: 6:1:20789:0.

OBJECT Identifica la tabla en la que se mantiene o se solicita un bloqueo. OBJECT se representa como OBJECT: db_id:object_id. Por ejemplo, TAB: 6:2009058193.

KEY Identifica el intervalo de clave de un índice en el que se mantiene o se solicita un bloqueo. KEY se representa como KEY: db_id:hobt_id (valor de hash de clave de índice). Por ejemplo, KEY: 6:72057594057457664 (350007a4d329).

PAG Identifica el recurso de página en el que se mantiene o se solicita un bloqueo. PAG se representa como PAG: db_id:file_id:page_no. Por ejemplo, PAG: 6:1:20789.

EXT Identifica la estructura de extensión. EXT se representa como EXT: db_id:file_id:extent_no. Por ejemplo, EXT: 6:1:9.

DB Identifica el bloqueo de la base de datos. DB se representa de una de las siguientes maneras:

DB: db_id

DB: db_id[BULK-OP-DB], que identifica el bloqueo de base de datos realizado por la copia de seguridad de la base de datos.

DB: db_id[BULK-OP-LOG], que identifica el bloqueo realizado por la copia de seguridad del registro.

APP Identifica un bloqueo de aplicación. APP se representa como APP: lock_resource. Por ejemplo, APP: Formf370f478.

METADATA Representa los recursos de metadatos implicados en un interbloqueo. Debido a que METADATA tiene muchos recursos secundarios, el valor devuelto depende del recurso secundario que se haya interbloqueado. Por ejemplo, METADATA.USER_TYPE devuelve user_type_id = *integer_value*. Para obtener más información acerca de los recursos y recursos secundarios de METADATA, consulte sys.dm_tran_locks.

HOBT Representa el montón o árbol B implicado en un interbloqueo.
Nada exclusivo de esta marca de seguimiento. Nada exclusivo de esta marca de seguimiento.

Ejemplo de marca de seguimiento 1204

En el ejemplo siguiente se muestra la salida cuando está activada la marca de seguimiento 1204. En este caso, la tabla de Node 1 es un montón sin índices, y la tabla de Node 2 es un montón con un índice no clúster. La clave de índice de Node 2 se está actualizando cuando se produce el interbloqueo.

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)

Ejemplo de marca de seguimiento 1222

En el ejemplo siguiente se muestra la salida cuando está activada la marca de seguimiento 1222. En este caso, una tabla es un montón sin índices y la otra tabla es un montón con un índice no clúster. En la segunda tabla, la clave de índice se está actualizando cuando se produce el interbloqueo.

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

Evento Deadlock Graph de Profiler

SQL Profiler tiene un evento que presenta una representación gráfica de las tareas y los recursos implicados en un interbloqueo. En el siguiente ejemplo se muestra el resultado de SQL Profiler cuando se ha activado el evento del grafo de interbloqueo.

Las características SQL Profiler y SQL Trace están en desuso y se reemplazan por Extended Events. Los "Extended Events" tienen una sobrecarga de rendimiento menor y son más configurables que SQL Trace. Considere la posibilidad de usar el evento de interbloqueo de eventos extendidos en lugar de rastrear interbloqueos en SQL Profiler.

Captura de pantalla de SSMS del grafo de interbloqueo visual desde un seguimiento de SQL.

Para obtener más información sobre el evento de interbloqueo, consulte Lock:Deadlock Event Class (Clase de evento Lock:Deadlock). Para obtener más información sobre los gráficos de interbloqueo de SQL Profiler, vea Guardar gráficos de interbloqueo (SQL Server Profiler).

Extended Events proporciona equivalentes de clases de eventos de seguimiento de SQL. Para obtener más información, vea Ver los equivalentes de eventos extendidos a las clases de eventos de seguimiento de SQL. Se recomienda usar Eventos Extendidos en lugar de SQL Trace.

Controlar interbloqueos

Cuando una instancia del motor de base de datos elige una transacción como víctima de interbloqueo, finaliza el lote actual, revierte la transacción y devuelve el error 1205 en la aplicación. El mensaje devuelto se estructura de la siguiente manera:

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.

Dado que cualquier aplicación que envíe Transact-SQL consultas se puede elegir como víctima del interbloqueo, las aplicaciones deben tener un controlador de errores que pueda controlar el error 1205. Si una aplicación no controla el error, la aplicación puede continuar sin saber que su transacción se ha revertido.

La implementación de un controlador de errores que detecta el error 1205 permite a una aplicación controlar interbloqueos y realizar acciones correctivas (por ejemplo, volver a enviar automáticamente la consulta implicada en el interbloqueo).

La aplicación debería realizar una pausa breve antes de volver a enviar su consulta. Esto da a la otra transacción implicada en el impasse una oportunidad para completar, y liberar sus bloqueos. Aleatorizar la duración de la pausa minimiza la probabilidad de que el interbloqueo se repita cuando la consulta reenviada solicite los bloqueos. Por ejemplo, el controlador de errores se puede codificar para pausar durante una duración aleatoria entre uno y tres segundos.

Control con TRY...CATCH

Puede usar TRY...CATCH para controlar interbloqueos. El bloque CATCH puede detectar el error 1205.

Para obtener más información, consulte Control de interbloqueos.

Minimizar interbloqueos

A pesar de que los interbloqueos no se pueden evitar totalmente, si se siguen ciertas convenciones de codificación se puede reducir su número. La minimización de los interbloqueos puede aumentar el rendimiento de las transacciones y reducir la sobrecarga del sistema, debido a que:

  • Se revierten menos transacciones, al deshacer todo el trabajo que realiza la transacción.
  • Las aplicaciones vuelven a enviar menos transacciones debido a que se revirtieron cuando se produjo el interbloqueo.

Para ayudar a reducir los interbloqueos:

  • Obtenga acceso a los objetos en el mismo orden.
  • Evite la interacción con los usuarios en las transacciones.
  • Mantenga transacciones cortas y en un proceso por lotes.
  • Evite niveles de aislamiento más altos, como REPEATABLE READ y SERIALIZABLE cuando no sea necesario.
  • Utilice un nivel de aislamiento basado en versiones de fila.
    • Habilite la opción de base de datos READ_COMMITTED_SNAPSHOT para utilizar el versionamiento de filas en las transacciones con el nivel de aislamiento READ COMMITTED.
    • Use transacciones de aislamiento de instantáneas.
  • Utilice conexiones enlazadas.

Acceso a los objetos en el mismo orden

Si todas las transacciones simultáneas tienen acceso a los objetos en el mismo orden, es menos probable que se produzcan interbloqueos. Por ejemplo, si dos transacciones simultáneas obtienen un bloqueo en la tabla Supplier y después en la tabla Part, una transacción se bloquea en la tabla Supplier hasta que finalice la otra transacción. Una vez confirmada o revertida la primera transacción, continúa la segunda, por lo que no se produce un interbloqueo. La utilización de procedimientos almacenados para todas las modificaciones de datos puede normalizar el orden de acceso a los objetos.

Diagrama de un interbloqueo.

Sin interacción con los usuarios en las transacciones

Evite las transacciones que incluyan la interacción del usuario, ya que la velocidad de los lotes que se ejecutan sin intervención del usuario es mucho más rápida que la velocidad a la que un usuario debe responder manualmente a las consultas, como responder a una solicitud de un parámetro solicitado por una aplicación. De esta forma, se degrada el rendimiento del sistema, ya que los bloqueos que mantiene la transacción solo se liberan cuando se confirma o se revierte la transacción. Incluso si no se produce un interbloqueo, otras transacciones que acceden a los mismos recursos se bloquean mientras esperan a que se complete la transacción.

Conservación de transacciones cortas y en un proceso por lotes

Normalmente, los interbloqueos se producen cuando varias transacciones de larga duración se ejecutan simultáneamente en la misma base de datos. Cuanto más dure la transacción, más tiempo se mantendrán los bloqueos exclusivos o de actualización, con lo cual se bloquean otras actividades y se originan posibles situaciones de interbloqueo.

Mantener las transacciones en un lote minimiza los recorridos de ida y vuelta de red durante una transacción, lo que reduce los posibles retrasos en completar la transacción debido al procesamiento del cliente.

Evitar niveles de aislamiento más altos

Determine si una transacción se puede ejecutar con un nivel de aislamiento inferior. El uso READ COMMITTED permite que una transacción lea datos anteriormente leídos (pero no modificados) por otra transacción sin esperar a que se complete la transacción. READ COMMITTED contiene bloqueos compartidos durante una duración más corta que un nivel de aislamiento superior, como SERIALIZABLE. Esto reduce la contención de bloqueos.

Uso de un nivel de aislamiento basado en versiones de fila

Cuando la opción de base de datos READ_COMMITTED_SNAPSHOT se establece en ON, una transacción que opera bajo el nivel de aislamiento READ COMMITTED utiliza versión de filas en lugar de bloqueos compartidos durante las operaciones de lectura.

Sugerencia

Microsoft recomienda utilizar el nivel de aislamiento basado en versionamiento de filas para todas las aplicaciones, excepto si una aplicación depende del comportamiento de bloqueo del nivel de aislamiento basado en bloqueos.

El aislamiento de instantánea también utiliza las versiones de fila, que no emplean bloqueos compartidos en las operaciones de lectura. Antes de ejecutar una transacción con aislamiento de instantánea, debe establecerse en ALLOW_SNAPSHOT_ISOLATION la opción de base de datos ON.

Utilice los niveles de aislamiento basados en versiones de fila para minimizar los bloqueos que pueden producirse entre las operaciones de lectura y escritura.

Utilizar conexiones enlazadas

Al utilizar conexiones enlazadas, dos o más conexiones abiertas por la misma aplicación pueden cooperar entre sí. Los bloqueos adquiridos por las conexiones secundarias se mantienen como si los adquiriera la conexión principal y viceversa. Por lo tanto, no se bloquean entre sí.

Causa de un interbloqueo

Es posible que tenga que provocar un bloqueo para fines de demostración o aprendizaje.

En el siguiente ejemplo se trabaja en la AdventureWorksLT2019 base de datos de ejemplo con el esquema y los datos predeterminados cuando se ha habilitado READ_COMMITTED_SNAPSHOT. Para descargar este ejemplo, visite Bases de datos de ejemplo AdventureWorks.

Para obtener un ejemplo que provoca un interbloqueo cuando el bloqueo optimizado está habilitado, consulte Bloqueo optimizado y interbloqueos.

Para provocar un interbloqueo, es preciso conectar dos sesiones a la base de datos AdventureWorksLT2019. Nos referimos a estas sesiones como Sesión A y Sesión B. Puede crear estas dos sesiones mediante la creación de dos ventanas de consulta en SQL Server Management Studio (SSMS).

En la sesión A, ejecute el siguiente lote. Este código inicia una transacción explícita y ejecuta una instrucción que actualiza la SalesLT.Product tabla. Para ello, la transacción adquiere un bloqueo de actualización (U) en las filas aptas de la tabla SalesLT.Product que se convierten a bloqueos exclusivos (X). Dejaremos abierta la transacción.

BEGIN TRANSACTION;

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

Ahora, en la sesión B, ejecute el siguiente lote. Este código no inicia explícitamente una transacción. En su lugar, funciona en modo de transacción de confirmación automática. Esta instrucción actualiza la tabla SalesLT.ProductDescription. La actualización toma un bloqueo de actualización (U) en las filas aptas de la SalesLT.ProductDescription tabla. La consulta se combina con otras tablas, incluida la tabla SalesLT.Product.

UPDATE SalesLT.ProductDescription
    SET Description = Description
FROM SalesLT.ProductDescription AS pd
     INNER JOIN SalesLT.ProductModelProductDescription AS pmpd
         ON pd.ProductDescriptionID = pmpd.ProductDescriptionID
     INNER JOIN SalesLT.ProductModel AS pm
         ON pmpd.ProductModelID = pm.ProductModelID
     INNER JOIN SalesLT.Product AS p
         ON pm.ProductModelID = p.ProductModelID
WHERE p.Color = 'Silver';

Para completar esta actualización, la sesión B necesita bloqueos compartidos (S) en las filas de la tabla SalesLT.Product, incluidas las filas bloqueadas por la sesión A. La sesión B está bloqueada en SalesLT.Product.

Regrese a la sesión A. Ejecute la siguiente instrucción UPDATE. Esta instrucción se ejecuta como parte de la transacción previamente abierta.

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 segunda instrucción de actualización de la sesión A se bloquea por la sesión B en la tabla SalesLT.ProductDescription.

La sesión A y la sesión B ahora se bloquean mutuamente entre sí. Ninguna transacción puede continuar, ya que cada una necesita un recurso bloqueado por la otra.

Después de unos segundos, el monitor de interbloqueo identifica que las transacciones de la sesión A y la sesión B se bloquean mutuamente entre sí y que ninguna puede avanzar. Verá que se produce un bloqueo, con Sesión A elegida como víctima de bloqueo. La sesión B finaliza correctamente. Aparece un mensaje de error en la ventana de consulta de la sesión A con texto similar al ejemplo siguiente:

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 no se ha generado un interbloqueo, compruebe que READ_COMMITTED_SNAPSHOT está habilitado en la base de datos de ejemplo. Los interbloqueos se pueden producir en cualquier configuración de base de datos, pero en este ejemplo es preciso que READ_COMMITTED_SNAPSHOT se haya habilitado.

Puede ver los detalles del interbloqueo en el ring_buffer destino de la system_health sesión de eventos, que por defecto está habilitada y activa en SQL Server y Azure SQL Managed Instance. Considere la siguiente consulta:

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;

Puede ver el XML en la columna Deadlock_XML dentro de SSMS seleccionando la celda que aparece como hipervínculo. Guarde esta salida como un archivo .xdl, cierre y vuelva a abrir el archivo .xdl en SSMS para el grafo de interbloqueo visual. El gráfico de interbloqueo debe tener un aspecto similar al de la imagen siguiente.

Captura de pantalla de un grafo de interbloqueo visual en un archivo .xdl en SSMS.

Bloqueos e interbloqueos optimizados

Con el bloqueo optimizado, los bloqueos de página y fila no se mantienen retenidos hasta el final de la transacción. Se liberan en cuanto se actualiza una fila. Además, si READ_COMMITTED_SNAPSHOT está habilitado, no se usan bloqueos de actualización (U). Como resultado, se reduce la probabilidad de interbloqueos.

El ejemplo anterior no provoca un interbloqueo cuando el bloqueo optimizado está habilitado porque se basa en los bloqueos de actualización (U).

El siguiente ejemplo puede ser usado para provocar un interbloqueo en una base de datos que tiene el bloqueo optimizado habilitado.

En primer lugar, cree una tabla de ejemplo y agregue datos.

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

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

Los siguientes lotes de T-SQL, ejecutados uno tras otro en dos sesiones separadas, crean un bloqueo mutuo.

En la sesión 1:

BEGIN TRANSACTION xactA;

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

En la sesión 2:

BEGIN TRANSACTION xactB;

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

En la sesión 1:

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

En la sesión 2:

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

En este caso, cada sesión contiene un bloqueo exclusivo (X) en su propio recurso de identificador de transacción (TID) y está esperando el bloqueo compartido (S) en el otro TID, lo que da lugar a un interbloqueo.

El siguiente informe de interbloqueo abreviado contiene elementos y atributos específicos del bloqueo optimizado. Bajo cada recurso en el informe <resource-list> de interbloqueo, cada <xactlock> elemento informa sobre los recursos subyacentes y la información de bloqueo de TID de cada miembro de un interbloqueo.

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