¿Por qué este bloqueo RX-X no aparece en los eventos extendidos?

13

El problema

Tengo un par de consultas que, bajo aislamiento serializable, provocan un bloqueo RX-X. Sin embargo, cuando uso Extended Events para ver la adquisición del bloqueo, la adquisición del bloqueo RX-X nunca aparece, solo se libera. ¿De dónde viene?

El Repro

Aquí está mi mesa:

CREATE TABLE dbo.LockTest (
ID int identity,
Junk char(4)
)

CREATE CLUSTERED INDEX CX_LockTest --not unique!
ON dbo.LockTest(ID)

--preload some rows
INSERT dbo.LockTest
VALUES ('data'),('data'),('data')

Aquí está mi lote de problemas:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRAN

INSERT dbo.LockTest
VALUES ('bleh')

SELECT *
FROM dbo.LockTest
WHERE ID = SCOPE_IDENTITY()

--ROLLBACK

Verifico las cerraduras en esta sesión y veo RX-X:

SELECT resource_type, request_mode, request_status, resource_description
FROM sys.dm_tran_locks
WHERE request_session_id = 72 --change SPID!

dm_tran_locks

Pero también tengo un evento extendido en lock_acquiredy lock_released. Lo filtro en el objeto_id_objetivo asociado apropiado ... no hay RX-X.

Salida de evento extendido

Después de ejecutar la reversión, veo RX-X (LAST_MODE) lanzado, a pesar de que nunca fue adquirido.

ÚLTIMO MODO

Lo que he probado

  • Miré todas las cerraduras en Eventos extendidos, sin filtrado. No se adquirieron bloqueos RX-X.

  • También probé Profiler: los mismos resultados (excepto que, por supuesto, tiene el nombre correcto ... no "LAST_MODE").

  • Ejecuté el XE para escaladas de bloqueo, no está allí.

  • No hay XE específicamente para las conversiones, pero pude confirmar que al menos la conversión de bloqueo U a X es capturada por lock_acquired

También es notable el RI-N que se adquiere pero nunca se lanza. Mi hipótesis actual es que el RX-X es un bloqueo de conversión, como se describe aquí . Hay bloqueos de rango de teclas superpuestos en mi lote que parecen calificar para la conversión, pero el bloqueo RX-X no está en la tabla de conversión.

¿De dónde viene esta cerradura y por qué Extended Events no la activa?

Para descanso
fuente

Respuestas:

12

La inserción de una sola fila adquiere un Xbloqueo (exclusivo) en la nueva fila.

Los SELECTintentos de adquirir un RangeS-Sbloqueo de rango compartido, clave compartida ( ).

El lock_acquiredevento extendido informa de esta solicitud como modo = RS_S.

La clase de evento Profiler lo informa Lock:Acquiredcomo modo 13 ( LCK_M_RS_S).

El modo solicitado se combina con el modo de bloqueo exclusivo existente en Lock::CalculateGrantModein sqlmin.dll. No existe un modo combinado de rango compartido, clave exclusiva ( RangeS-X), por lo que el resultado del cálculo es rango exclusivo, clave exclusiva ( RangeX-X), que resulta ser el modo 15.

El cálculo del modo de concesión anterior se realiza justo antes de que se genere el evento extendido lck_ProduceExtendedEvent<XeSqlPkg::lock_acquired>. Sin embargo, tanto Profiler como Extended Events registran el modo solicitado RangeS-S , no el modo de bloqueo resultante RangeX-X. Esto es contrario a la documentación limitada , que dice:

Modo | int | Modo resultante después de que se adquirió el bloqueo.

La columna de modo del evento extendido no tiene ninguna documentación, y la descripción en los metadatos está en blanco. Quizás los propios Microsoft ni siquiera estaban seguros del comportamiento.

A menudo he pensado que sería más útil si los eventos de bloqueo informaran tanto los modos solicitados como los resultantes , pero eso no es lo que tenemos. La disposición actual hace que sea prácticamente imposible rastrear y combinar la adquisición y liberación de bloqueos.

No podría ser una buena razón para informar cerraduras de esta manera. Si no satisface sus necesidades, puede abrir un caso de soporte con Microsoft o crear un elemento de Comentarios de Azure.


LAST_MODE

Lo misterioso LAST_MODEes algo que Erik Darling ha comentado antes . Es el map_keyvalor más alto en la lista de modos de bloqueo expuestos por sys.dm_xe_map_values:

SELECT
    DXMV.map_key,
    DXMV.map_value
FROM sys.dm_xe_map_values AS DXMV
WHERE 
    DXMV.[name] = N'lock_mode'
ORDER BY
    DXMV.map_key;
╔═════════╦═══════════╗
║ map_key ║ map_value ║
╠═════════╬═══════════╣
║       0 ║ NL        ║
║       1 ║ SCH_S     ║
║       2 ║ SCH_M     ║
║       3 ║ S         ║
║       4 ║ U         ║
║       5 ║ X         ║
║       6 ║ IS        ║
║       7 ║ IU        ║
║       8 ║ IX        ║
║       9 ║ SIU       ║
║      10 ║ SIX       ║
║      11 ║ UIX       ║
║      12 ║ BU        ║
║      13 ║ RS_S      ║
║      14 ║ RS_U      ║
║      15 ║ RI_NL     ║
║      16 ║ RI_S      ║
║      17 ║ RI_U      ║
║      18 ║ RI_X      ║
║      19 ║ RX_S      ║
║      20 ║ RX_U      ║
║      21 ║ LAST_MODE ║
╚═════════╩═══════════╝

La estructura de memoria a la que se accede a través del DMV (usando sqlmin!CMapValuesTable) se almacena a partir de la dirección sqlmin!XeSqlPkg::g_lock_mode. Cada entrada de 16 bytes en la estructura contiene el map_keyy un puntero a la cadena devuelta como map_valuepor el TVF de transmisión.

Las cadenas se almacenan exactamente como se muestra en la tabla anterior (aunque no en ese orden). Parece ser un error que la entrada 21 tenga un map_value"LAST_MODE" en lugar del "RX_X" esperado. Erik Darling ha informado el problema en Azure Feedback .

Paul White 9
fuente