Por que esse bloqueio RX-X não aparece em Eventos estendidos?

13

O problema

Eu tenho um par de consultas que, sob isolamento serializável, causam um bloqueio do RX-X. No entanto, quando eu uso Eventos Estendidos para assistir à aquisição de bloqueios, a aquisição de bloqueios RX-X nunca aparece e é liberada apenas. De onde isso vem?

The Repro

Aqui está a minha 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')

Aqui está o meu lote problemático:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRAN

INSERT dbo.LockTest
VALUES ('bleh')

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

--ROLLBACK

Verifico os bloqueios mantidos por esta sessão e vejo o 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

Mas também tenho um evento estendido em lock_acquirede lock_released. Eu filtro-o no associado_object_id apropriado ... não há RX-X.

Saída de evento estendido

Depois de executar a reversão, vejo o RX-X (LAST_MODE) liberado, mesmo que nunca tenha sido adquirido.

LAST_MODE

O que eu tentei

  • Eu olhei para todos os bloqueios em Eventos Estendidos - sem filtragem. Nenhum bloqueio RX-X adquirido.

  • Eu também tentei o Profiler: mesmos resultados (exceto, é claro, o nome correto ... não "LAST_MODE").

  • Eu executei o XE para escalações de bloqueio - não está lá.

  • Não há XE especificamente para conversões, mas pude confirmar que pelo menos a conversão de bloqueio de U para X é capturada por lock_acquired

Também digno de nota é o RI-N que é adquirido, mas nunca lançado. Minha hipótese atual é que o RX-X é um bloqueio de conversão, conforme descrito aqui . Existem bloqueios de intervalo de chaves sobrepostos no meu lote que parecem se qualificar para conversão, mas o bloqueio RX-X não está na tabela de conversão.

De onde vem esse bloqueio e por que não é captado pelos Eventos Estendidos?

Forrest
fonte

Respostas:

12

A inserção de linha única adquire um Xbloqueio (exclusivo) na nova linha.

As SELECTtentativas de adquirir um RangeS-Sbloqueio de chave compartilhada por intervalo e compartilhado .

Esta solicitação é relatada pelo lock_acquiredEvento Estendido como mode = RS_S.

É relatado pela classe de eventos do Profiler Lock:Acquiredcomo modo 13 ( LCK_M_RS_S).

O modo solicitado é combinado com o modo de bloqueio exclusivo existente em Lock::CalculateGrantModein sqlmin.dll. Não há modo combinado de chave exclusiva ( RangeS-X) compartilhada por intervalo ( ), portanto, o resultado do cálculo é exclusivo de chave exclusiva ( RangeX-X), que é o modo 15.

O cálculo do modo de concessão acima é realizado imediatamente antes da geração do evento estendido lck_ProduceExtendedEvent<XeSqlPkg::lock_acquired>. No entanto, o Profiler e o Extended Events registram o modo solicitado RangeS-S , não o modo de bloqueio resultante RangeX-X. Isso é contrário à documentação limitada , que diz:

Modo | int | Modo resultante após a aquisição do bloqueio.

A coluna de modo do evento estendido não possui nenhuma documentação e a descrição nos metadados está em branco. Talvez a própria Microsoft não tivesse certeza do comportamento.

Muitas vezes pensei que seria mais útil se os eventos de bloqueio reportassem os modos solicitado e resultante , mas não é isso que temos. O arranjo atual torna praticamente impossível rastrear e comparar a aquisição e liberação do bloqueio.

Não pode ser uma boa razão para relatar fechaduras desta forma. Se não atender às suas necessidades, você poderá abrir um caso de suporte com a Microsoft ou criar um item de Feedback do Azure.


LAST_MODE

O misterioso LAST_MODEé algo que Erik Darling já comentou antes . É o map_keyvalor mais alto na lista de modos de bloqueio expostos 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 ║
╚═════════╩═══════════╝

A estrutura da memória acessada via DMV (usando sqlmin!CMapValuesTable) é armazenada começando no endereço sqlmin!XeSqlPkg::g_lock_mode. Cada entrada de 16 bytes na estrutura contém o map_keyponteiro e a seqüência de caracteres retornada map_valuepelo TVF de streaming.

As seqüências de caracteres são armazenadas exatamente como mostrado na tabela acima (embora não nessa ordem). Parece ser um erro que a entrada 21 tenha um map_value"LAST_MODE" em vez do esperado "RX_X". Erik Darling relatou o problema no Feedback do Azure .

Paul White 9
fonte