Dlaczego ta blokada RX-X nie pojawia się w zdarzeniach rozszerzonych?

13

Problem

Mam parę zapytań, które przy szeregowej izolacji powodują blokadę RX-X. Jednak gdy używam zdarzeń rozszerzonych do oglądania akwizycji blokady, akwizycja blokady RX-X nigdy się nie pojawia, jest ona tylko zwolniona. Skąd to pochodzi?

Repro

Oto mój stół:

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

Oto moja partia problemów:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

BEGIN TRAN

INSERT dbo.LockTest
VALUES ('bleh')

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

--ROLLBACK

Sprawdzam blokady utrzymywane przez tę sesję i widzę 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

Ale mam również Wydarzenie rozszerzone na lock_acquiredi lock_released. Filtruję to na odpowiednim ID_obiektu_obowiązanego ... nie ma RX-X.

Rozszerzone wyjście zdarzeń

Po wykonaniu wycofania widzę zwolnienie RX-X (LAST_MODE), mimo że nigdy nie zostało nabyte.

LAST_MODE

Co próbowałem

  • Patrzyłem na wszystkie blokady w rozszerzonych zdarzeniach - bez filtrowania. Brak nabytych zamków RX-X.

  • Próbowałem także Profiler: te same wyniki (z wyjątkiem tego, że ma właściwą nazwę ... nie ma „LAST_MODE”).

  • Uruchomiłem XE dla eskalacji blokady - nie ma jej tam.

  • Nie ma XE specjalnie dla konwersji, ale byłem w stanie potwierdzić, że przechwytywana jest przynajmniej konwersja blokady U na X lock_acquired

Na uwagę zasługuje również RI-N, który zostaje nabyty, ale nigdy nie wydany. Moja obecna hipoteza jest taka, że ​​RX-X jest blokadą konwersji, jak opisano tutaj . W mojej partii występują nakładające się zamki z kluczem, które wyglądają, jakby kwalifikowały się do konwersji, ale zamka RX-X nie ma w tabeli konwersji.

Skąd pochodzi ta blokada i dlaczego nie jest odbierana przez Wydarzenie rozszerzone?

Forrest
źródło

Odpowiedzi:

12

Wkładka jednorzędowa uzyskuje X(wyłączną) blokadę nowego wiersza.

Do SELECTprób mających na celu zdobycie, zakres współdzielony klucz udostępniony ( RangeS-S) zamka.

To żądanie jest zgłaszane przez lock_acquiredWydarzenie rozszerzone jako tryb = RS_S.

Jest zgłaszany przez klasę zdarzeń Profiler Lock:Acquiredjako tryb 13 ( LCK_M_RS_S).

Żądany tryb jest łączony z istniejącym wyłącznym trybem blokady Lock::CalculateGrantModew sqlmin.dll. Nie ma połączonego trybu współdzielonego zakresu, klucza wyłącznego ( RangeS-X), więc wynik obliczeń jest wyłączny dla zakresu, kluczowy wyłączny ( RangeX-X), który przypadkiem jest trybem 15.

Powyższe obliczenia trybu przyznania są wykonywane tuż przed wygenerowaniem zdarzenia rozszerzonego przez lck_ProduceExtendedEvent<XeSqlPkg::lock_acquired>. Niemniej jednak zarówno Profiler, jak i Extended Events rejestrują żądany RangeS-S tryb, a nie wynikowy tryb blokady RangeX-X. Jest to sprzeczne z ograniczoną dokumentacją , która mówi:

Tryb | int | Tryb wynikowy po uzyskaniu blokady.

Tryb kolumna rozszerzonego razie nie ma dokumentacji na wszystkich, a opis w meta danych jest puste. Być może nawet sam Microsoft nie był nawet pewien tego zachowania.

Często myślałem, że byłoby bardziej użyteczne, gdyby zdarzenia blokady zgłaszały zarówno żądany, jak i wynikowy tryb, ale tego nie mamy. Obecny układ praktycznie uniemożliwia śledzenie i dopasowanie pozyskiwania i zwalniania blokady.

Nie może być dobrym powodem do zamków raportowania w ten sposób. Jeśli nie spełnia twoich potrzeb, możesz otworzyć zgłoszenie do pomocy technicznej firmy Microsoft lub utworzyć element Azure Feedback.


LAST_MODE

Tajemnicą LAST_MODEjest coś , o czym wcześniej zauważył Erik Darling . Jest to najwyższa map_keywartość na liście trybów blokady ujawniona przez 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 ║
╚═════════╩═══════════╝

Struktura pamięci dostępna za pośrednictwem DMV (za pomocą sqlmin!CMapValuesTable) jest zapisywana począwszy od adresu sqlmin!XeSqlPkg::g_lock_mode. Każdy 16-bajtowy wpis w strukturze zawiera map_keyi wskaźnik do ciągu zwróconego map_valueprzez TVF przesyłany strumieniowo.

Ciągi są przechowywane dokładnie tak, jak pokazano w powyższej tabeli (choć nie w tej kolejności). Wygląda na błąd, że pozycja 21 ma map_value„LAST_MODE” zamiast oczekiwanego „RX_X”. Erik Darling zgłosił problem w usłudze Azure Feedback .

Paul White 9
źródło