Hvis du bruger blocked process report eller samler deadlock-grafer, der gives af SQL Server, vil du fra tid til anden stÞde pÄ sÄdanne ting:
waitresource=âPAGE: 6:3:70133â
waitresource=âKEY: 6:72057594041991168 (ce52f92a058c)â
Nogle gange vil der vÊre mere information i det enorme XML, som du studerer (deadlock-graferne indeholder en liste over ressourcer, der hjÊlper med at finde navne pÄ objekter og indekser), men ikke altid.
Denne tekst vil hjĂŠlpe dig med at afkode dem.
Alle oplysninger, der her findes, er tilgĂŠngelige online pĂ„ forskellige steder, de er bare meget spredte! Jeg vil samle alt sammen â fra DBCC PAGE til hobt_id samt de u-dokumenterede %%physloc%% og %%lockres%% funktioner.
Lad os fÞrst tale om ventetider pÄ PAGE-lÄse, og derefter gÄr vi videre til KEY-lÄse.
1) waitresource=âPAGE: 6:3:70133â = Database_Id: FileId: PageNumber
Hvis din forespÞrgsel venter pÄ en PAGE-lÄs, vil SQL Server give dig adressen pÄ denne side.
Ved at opdele âPAGE: 6:3:70133â fĂ„r vi:
- database_id = 6
- data_file_id = 3
- page_number = 70133
1.1) AfdĂŠkker database_id
Lad os finde navnet pÄ databasen med forespÞrgslen:
SELECT
name
FROM sys.databases
WHERE database_id=6;
GODette er den offentlige pÄ min SQL Server.
1.2) SĂžg efter navnet pĂ„ datafilen â hvis du er interesseret
Vi vil bruge data_file_id i nÊste trin for at finde navnet pÄ tabellen. Du kan bare gÄ til nÊste trin, men hvis du er nysgerrig efter filnavnet, kan du finde det ved at udfÞre en forespÞrgsel i sammenhÊng med den fundne DB, ved at indsÊtte data_file_id i denne forespÞrgsel:
USE WideWorldImporters;
GO
SELECT
name,
physical_name
FROM sys.database_files
WHERE file_id = 3;
GOI DB WideWorldImporters er dette filen kaldet WWI_UserData, og den er gendannet hos mig i C:MSSQLDATAWideWorldImporters_UserData.ndf. (Ups, du fangede mig med at lÊgge filer pÄ disk med systemet! Nej! Det blev akavet).
1.3) FĂ„ objektets navn fra DBCC PAGE
Nu ved vi, at side #70133 i datafilen 3 tilhĂžrer DB WorldWideImporters. Vi kan se indholdet af denne side ved hjĂŠlp af den u-dokumenterede DBCC PAGE og trace-flaget 3604.
BemĂŠrk: Jeg foretrĂŠkker at bruge DBCC PAGE pĂ„ en gendannet kopi fra en backup et sted pĂ„ en anden server, fordi det er en u-dokumenteret ting. I nogle tilfĂŠlde kan det (note fra oversĂŠtteren â linket fĂžrer desvĂŠrre ingen steder hen, men ifĂžlge URL'en handler det om filtered-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 Scrollende til resultaterne kan man finde object_id og index_id.

NĂŠsten klar! Nu kan du finde tabel- og indeksnavne med forespĂžrgslen:
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;
GOOg her ser vi, at ventetiden pÄ blokeringen var pÄ indekset PK_Sales_OrderLines i tabellen Sales.OrderLines.
BemÊrk: I SQL Server 2014 og nyere kan objektets navn ogsÄ findes ved hjÊlp af den ikke-dokumenterede DMO sys.dm_db_database_page_allocations. Men du skal anmode om hver side i databasen, hvilket ikke ser sÄ smart ud for store databaser, sÄ jeg brugte DBCC PAGE.
1.4) Kan vi se dataene pÄ den side, der var blokeret?
NÄ, ja. Men⊠er du sikker pÄ, at du virkelig har brug for det?
Det er langsomt, selv pÄ smÄ tabeller. Men det er lidt sjovt, sÄ da du er nÄet til dette punkt... lad os tale om %%physloc%%!
%%physloc%% er et ikke-dokumenteret stykke magi, der returnerer den fysiske identifikator for hver post. Du kan bruge .
Nu hvor vi ved, at vi gerne ville lÄse siden i Sales.OrderLines, kan vi se alle data i denne tabel, der er gemt i datafil #3 pÄ side #70133, med denne forespÞrgsel:
Use WideWorldImporters;
GO
SELECT
sys.fn_PhysLocFormatter (%%physloc%%),
*
FROM Sales.OrderLines (NOLOCK)
WHERE sys.fn_PhysLocFormatter (%%physloc%%) like '(3:70133%'
GOSom jeg sagde â det er langsomt, selv pĂ„ bittesmĂ„ tabeller. Jeg tilfĂžjede NOLOCK til forespĂžrgslen, fordi vi alligevel ikke har nogen garantier for, at de data, vi vil se pĂ„, er prĂŠcis de samme, som de var pĂ„ det tidspunkt, da blokeringen blev opdaget â sĂ„ vi kan roligt lave beskidte lĂŠsninger.
Men hurra, forespĂžrgslen returnerer de 25 rĂŠkker, som vores forespĂžrgsel kĂŠmpede for.

Lad os glemme PAGE-blokeringer. Hvad nu hvis vi venter pÄ KEY-blokering?
2) waitresource=âKEY: 6:72057594041991168 (ce52f92a058c)â = Database_Id, HOBT_Id (et magisk hash, der kan afkodes med %%lockres%%, hvis du virkelig vil)
Hvis din forespÞrgsel forsÞger at lÄse en post i indekset og bliver blokeret selv, fÄr du en helt anden type adresse.
Ved at opdele â6:72057594041991168 (ce52f92a058c)â i dele, fĂ„r vi:
- database_id = 6
- hobt_id = 72057594041991168
- magisk hash = (ce52f92a058c)
2.1) Vi afkoder database_id
Det fungerer ligesom det foregÄende eksempel! Vi finder databasens navn med en forespÞrgsel:
SELECT
name
FROM sys.databases
WHERE database_id=6;
GOI mit tilfĂŠlde er det stadig den samme .
2.2) Afkode hobt_id
I konteksten af den fundne database skal der udfĂžres en forespĂžrgsel til sys.partitions med et par join, som hjĂŠlper med at bestemme navne 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;
GODen fortÊller mig, at forespÞrgslen ventede pÄ en lÄs af Application.Countries ved at bruge indekset PK_Application_Countries.
2.3) Nu lidt magi %%lockres%% â hvis du vil finde ud af, hvilken post der var lĂ„st
Hvis jeg virkelig vil finde ud af, hvilken rÊkke lÄsen var nÞdvendig for, kan jeg finde det ved at forespÞrge tabellen selv. Vi kan bruge den uofficielle funktion %%lockres%% til at finde en post, der matcher den magiske hash.
VÊr opmÊrksom pÄ, at denne forespÞrgsel vil scanne hele tabellen, og pÄ store tabeller kan det blive ret besvÊrligt:
SELECT
*
FROM Application.Countries (NOLOCK)
WHERE %%lockres%% = '(ce52f92a058c)';
GO Jeg tilfĂžjede NOLOCK () fordi lĂ„se kan blive et problem. Vi vil blot se, hvad der er der nu, og ikke hvad der var der, da transaktionen begyndte â jeg tror ikke, at dataintegritet er vigtig for os.
VoilĂ , posten vi kĂŠmpede om!

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 mest lidt dokumenterede funktioner, som du mÄske vil finde interessante:
- Paul Randals indlĂŠg om (som vi brugte vores data til i det fĂžrste eksempel)
- SpÞrgsmÄlet pÄ StackOverflow om (som vi fandt data i det andet eksempel). Et af svarene fÞrer til indlÊgget .
Kilde: habr.com
