Dechifrer nøgle og Page WaitResource i dødlåse og låse

Hvis du bruger den blokerede procesrapport eller indsamler deadlock-grafer, der leveres af SQL Server med jævne mellemrum, vil du støde på ting som dette:

waitresource="SIDE: 6:3:70133"

waitresource=“KEY: 6:72057594041991168 (ce52f92a058c)“

Nogle gange vil der være mere information i den gigantiske XML, som du studerer (deadlock-grafer indeholder en liste over ressourcer, der hjælper dig med at finde ud af navnene på objektet og indekset), men ikke altid.

Denne tekst vil hjælpe dig med at tyde dem.

Al den information, der er her, er på internettet forskellige steder, den er simpelthen meget distribueret! Jeg vil sætte det hele sammen, fra DBCC PAGE til hobt_id til de udokumenterede %%physloc%% og %%lockres%% funktioner.

Lad os først tale om ventetider på PAGE-låse, og derefter gå videre til KEY-låse.

1) waitresource="PAGE: 6:3:70133" = Database_Id: FileId: PageNumber

Hvis din anmodning venter på en PAGE-lås, vil SQL Server give dig adressen på den side.

Ved at opdele "PAGE: 6:3:70133" får vi:

  • database_id = 6
  • data_fil_id = 3
  • sidenummer = 70133

1.1) Dekrypter database_id

Find navnet på databasen ved hjælp af forespørgslen:

SELECT 
    name 
FROM sys.databases 
WHERE database_id=6;
GO

Det er offentligt DB WideWorldImporters på min SQL Server.

1.2) Leder efter navnet på datafilen - hvis du er interesseret

Vi skal bruge data_file_id i det næste trin for at finde tabelnavnet. Du kan bare springe til næste trin, men hvis du er interesseret i filnavnet, kan du finde det ved at køre en forespørgsel i konteksten af ​​den fundne database, og erstatte data_file_id i denne forespørgsel:

USE WideWorldImporters;
GO
SELECT 
    name, 
    physical_name
FROM sys.database_files
WHERE file_id = 3;
GO

I WideWorldImporters-databasen er dette en fil kaldet WWI_UserData, og jeg har den gendannet til C:MSSQLDATAWideWorldImporters_UserData.ndf. (Ups, du fangede mig i at lægge filer på systemdrevet! Nej! Det er pinligt).

1.3) Hent objektnavn fra DBCC PAGE

Vi ved nu, at side #70133 i datafil 3 tilhører WorldWideImporters-databasen. Vi kan se på indholdet af denne side ved hjælp af den udokumenterede DBCC PAGE og sporingsflag 3604.
Bemærk: Jeg foretrækker at bruge DBCC PAGE på en gendannet kopi et sted på en anden server, fordi det er udokumenterede ting. I nogle tilfælde har hun kan resultere i et dump (ca. oversætter - linket fører desværre ingen steder hen, men ud fra url'en at dømme taler vi om filtrerede indekser).

/* This trace flag makes DBCC PAGE output go to our Messages tab
instead of the SQL Server Error Log file */
DBCC TRACEON (3604);
GO
/* DBCC PAGE (DatabaseName, FileNumber, PageNumber, DumpStyle)*/
DBCC PAGE ('WideWorldImporters',3,70133,2);
GO

Ved at rulle til resultaterne kan du finde objekt_id og indeks_id.
Dechifrer nøgle og Page WaitResource i dødlåse og låse
Næsten færdig! Nu kan du finde tabel- og indeksnavnene med en forespørgsel:

USE WideWorldImporters;
GO
SELECT 
    sc.name as schema_name, 
    so.name as object_name, 
    si.name as index_name
FROM sys.objects as so 
JOIN sys.indexes as si on 
    so.object_id=si.object_id
JOIN sys.schemas AS sc on 
    so.schema_id=sc.schema_id
WHERE 
    so.object_id = 94623380
    and si.index_id = 1;
GO

Og her ser vi, at ventetiden på låsen var på PK_Sales_OrderLines-indekset i Sales.OrderLines-tabellen.

Bemærk: I SQL Server 2014 og nyere kan objektnavnet også findes ved hjælp af den udokumenterede DMO sys.dm_db_database_page_allocations. Men du skal forespørge på hver side i databasen, hvilket ikke ser særlig fedt ud til store databaser, så jeg brugte DBCC PAGE.

1.4) Er det muligt at se dataene på den side, der blev blokeret?

Nuuu, ja. Men... er du sikker på, at du virkelig har brug for det?
Det er langsomt selv på små borde. Men det er lidt fedt, så siden du har læst så langt... lad os tale om %%physloc%%!

%%physloc%% er et udokumenteret stykke magi, der returnerer et fysisk ID for hver post. du kan bruge %%physloc%% sammen med sys.fn_PhysLocFormatter i SQL Server 2008 og nyere.

Nu hvor vi ved, at vi ønskede at blokere siden i Sales.OrderLines, kan vi se på alle dataene i denne tabel, som er gemt i datafil #3 på side #70133, med følgende forespørgsel:

Use WideWorldImporters;
GO
SELECT 
    sys.fn_PhysLocFormatter (%%physloc%%),
    *
FROM Sales.OrderLines (NOLOCK)
WHERE sys.fn_PhysLocFormatter (%%physloc%%) like '(3:70133%'
GO

Som sagt er det langsomt selv på små borde. Jeg tilføjede NOLOCK til anmodningen, fordi vi stadig ikke har nogen garantier for, at de data, vi ønsker at se på, er nøjagtige de samme, som de var på det tidspunkt, hvor låsen blev opdaget - så vi kan roligt foretage dirty reads.
Men, hurra, forespørgslen returnerer mig netop de 25 rækker, som vores forespørgsel kæmpede for.
Dechifrer nøgle og Page WaitResource i dødlåse og låse
Nok om PAGE-låse. Hvad hvis vi venter på en NØGLElås?

2) waitresource="KEY: 6:72057594041991168 (ce52f92a058c)" = Database_Id, HOBT_Id (magisk hash, der kan dekrypteres med %%lockres%%, hvis du virkelig vil)

Hvis din forespørgsel forsøger at låse en indekspost og bliver låst af sig selv, får du en helt anden type adresse.
Når "6:72057594041991168 (ce52f92a058c)" opdeles i dele, får vi:

  • database_id = 6
  • hobt_id = 72057594041991168
  • magisk hash = (ce52f92a058c)

2.1) Dekrypter database_id

Det fungerer nøjagtigt det samme som med eksemplet ovenfor! Vi finder navnet på databasen ved hjælp af forespørgslen:

SELECT 
    name 
FROM sys.databases 
WHERE database_id=6;
GO

I mit tilfælde er det det samme DB WideWorldImporters.

2.2) Dekrypter hobt_id

I forbindelse med den fundne database skal du forespørge sys.partitions med et par joinforbindelser, der vil hjælpe med at bestemme navnene på tabellen og indekset ...

USE WideWorldImporters;
GO
SELECT 
    sc.name as schema_name, 
    so.name as object_name, 
    si.name as index_name
FROM sys.partitions AS p
JOIN sys.objects as so on 
    p.object_id=so.object_id
JOIN sys.indexes as si on 
    p.index_id=si.index_id and 
    p.object_id=si.object_id
JOIN sys.schemas AS sc on 
    so.schema_id=sc.schema_id
WHERE hobt_id = 72057594041991168;
GO

Det fortæller mig, at anmodningen ventede på Application.Countries-låsen ved hjælp af PK_Application_Countries-indekset.

2.3) Nu til noget %%lockres%% magi - hvis du vil finde ud af, hvilken post der var låst

Hvis jeg virkelig vil vide, på hvilken række låsen var nødvendig, kan jeg finde ud af det ved at forespørge i selve tabellen. Vi kan bruge den udokumenterede %%lockres%%-funktion til at finde en post, der matcher den magiske hash.
Bemærk, at denne forespørgsel vil scanne hele tabellen, og på store borde er det måske slet ikke sjovt:

SELECT
    *
FROM Application.Countries (NOLOCK)
WHERE %%lockres%% = '(ce52f92a058c)';
GO

Jeg tilføjede NOLOCK (efter råd fra Klaus Aschenbrenner på twitter), fordi låse kan være et problem. Vi vil bare se på, hvad der er der nu, og ikke hvad der var der, da transaktionen begyndte - jeg tror ikke, at datakonsistens er vigtig for os.
Voila, rekorden vi kæmpede for!
Dechifrer nøgle og Page WaitResource i dødlåse og låse

Tak og videre læsning

Jeg kan ikke huske, hvem der først beskrev mange af disse ting, men her er to indlæg om de mindst dokumenterede ting, du måske kunne lide:

Kilde: www.habr.com

Tilføj en kommentar