Om du anvÀnder rapporten om blockerade processer eller samlar dödlÀgesgrafer som tillhandahÄlls av SQL Server, kommer du regelbundet att stöta pÄ sÄdana saker:
waitresource="PAGE: 6:3:70133"
waitresource="KEY: 6:72057594041991168 (ce52f92a058c)"
Ibland, i den gigantiska XML som du studerar, kommer det att finnas mer information (dödlÀgesgrafer innehÄller en lista över resurser som hjÀlper till att identifiera objekt- och indexnamn), men det Àr inte alltid.
Denna text hjÀlper dig att avkoda dem.
All information som finns hĂ€r finns pĂ„ internet pĂ„ olika stĂ€llen, den Ă€r bara mycket spridd! Jag vill samla allt pĂ„ ett stĂ€lle â frĂ„n DBCC PAGE till hobt_id och de odokumenterade %%physloc%% och %%lockres%% funktionerna.
Först pratar vi om vÀntan pÄ PAGE-blockeringar, och sedan gÄr vi vidare till KEY-blockeringar.
1) waitresource="PAGE: 6:3:70133" = Databas_Id: FilId: Sidanummer
Om din frÄga vÀntar pÄ en PAGE-blockering, kommer SQL Server att ge dig adressen till den sidan.
Genom att bryta ner "PAGE: 6:3:70133" fÄr vi:
- database_id = 6
- data_file_id = 3
- page_number = 70133
1.1) Avkodar database_id
Vi hittar databasnamnet med hjÀlp av en frÄga:
SELECT
name
FROM sys.databases
WHERE database_id=6;
GODet Àr en offentlig pÄ min SQL Server.
1.2) Letar efter namnet pĂ„ datafilen â om du Ă€r intresserad
Vi kommer att anvÀnda data_file_id i nÀsta steg för att hitta namnet pÄ tabellen. Du kan bara gÄ vidare till nÀsta steg, men om du Àr intresserad av filnamnet kan du hitta det genom att köra en frÄga i kontexten av den hittade databasen, dÀr du stoppar in data_file_id i denna frÄga:
USE WideWorldImporters;
GO
SELECT
name,
physical_name
FROM sys.database_files
WHERE file_id = 3;
GOI databasen WideWorldImporters Àr detta filen som heter WWI_UserData och den finns ÄterstÀlld hos mig i C:MSSQLDATAWideWorldImporters_UserData.ndf. (Oops, du fÄngade mig med hur jag lÀgger filer pÄ disken med systemet! Nej! Det blev lite pinsamt).
1.3) FÄr objektets namn frÄn DBCC PAGE
Nu vet vi att sidan #70133 i datafilen 3 tillhör databasen WorldWideImporters. Vi kan titta pÄ innehÄllet pÄ denna sida med hjÀlp av den odokumenterade DBCC PAGE och trace-flaggan 3604.
Obs: Jag föredrar att anvĂ€nda DBCC PAGE pĂ„ en Ă„terstĂ€lld backup-kopia nĂ„gon annanstans pĂ„ en annan server, eftersom detta Ă€r en odokumenterad funktion. I vissa fall kan den (övers. av översĂ€ttaren â lĂ€nken gĂ„r tyvĂ€rr ingenstans, men det verkar enligt url handla om filtrerade index).
/* 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 Genom att blÀddra till resultaten kan man hitta object_id och index_id.

NÀstan klart! Nu kan vi hitta namn pÄ tabellen och indexet med hjÀlp av en frÄga:
ANVĂND WideWorldImporters;
GO
VĂLJ
sc.name AS schema_name,
so.name AS object_name,
si.name AS index_name
FRĂ
N 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
DĂR
so.object_id = 94623380
OCH si.index_id = 1;
GOOch hÀr ser vi att vÀntan pÄ lÄsningen var pÄ indexet PK_Sales_OrderLines i tabellen Sales.OrderLines.
Observera: i SQL Server 2014 och senare kan du ocksÄ hitta objektnamnet med hjÀlp av den icke dokumenterade DMO sys.dm_db_database_page_allocations. Men du mÄste begÀra varje sida i databasen, vilket inte Àr mycket stiligt för stora databaser, sÄ jag anvÀnde DBCC PAGE.
1.4) Kan vi se data pÄ den sida som var lÄst?
Nja, jo. Men⊠Àr du sÀker pÄ att du verkligen behöver det?
Det Àr lÄngsamt Àven pÄ smÄ tabeller. Men det verkar ÀndÄ coolt, sÄ eftersom du har lÀst till den hÀr punkten⊠lÄt oss prata om %%physloc%%!
%%physloc%% Àr en icke dokumenterad bit magi som returnerar den fysiska identifieraren för varje post. Du kan anvÀnda .
Nu nÀr vi vet att vi ville lÄsa sidan i Sales.OrderLines kan vi se all data i denna tabell som lagras i datakatalog #3 pÄ sida #70133 med hjÀlp av följande frÄga:
ANVĂND WideWorldImporters;
GO
VĂLJ
sys.fn_PhysLocFormatter (%%physloc%%),
*
FRĂ
N Sales.OrderLines (NOLOCK)
DĂR sys.fn_PhysLocFormatter (%%physloc%%) LIKT '(3:70133%'
GOSom jag sa â det Ă€r lĂ„ngsamt Ă€ven pĂ„ pyttesmĂ„ tabeller. Jag lade till NOLOCK till frĂ„gan eftersom vi Ă€ndĂ„ inte har nĂ„gra garantier för att de data vi vill titta pĂ„ verkligen Ă€r de samma som nĂ€r lĂ„sningen upptĂ€cktes â sĂ„ vi kan lugnt göra smutsiga lĂ€sningar.
Men, hurra, frÄgan returnerar de dÀr 25 raderna som vÄr frÄga kÀmpade för

Nog om PAGE-lÄsningar. Vad hÀnder om vi vÀntar pÄ KEY-lÄsning?
2) waitresource="KEY: 6:72057594041991168 (ce52f92a058c)" = Database_Id, HOBT_Id (den magiska hash som kan avkodas med hjÀlp av %%lockres%% om du verkligen vill)
Om din frÄga försöker lÀgga en lÄs pÄ en post i indexet och blir lÄst sjÀlv, fÄr du en helt annan typ av adress.
Genom att dela upp "6:72057594041991168 (ce52f92a058c)" fÄr vi:
- database_id = 6
- hobt_id = 72057594041991168
- den magiska hash = (ce52f92a058c)
2.1) Avkodar databasen_id
Det fungerar pÄ samma sÀtt som i exemplet ovan! Vi hittar databasnamnet med hjÀlp av en frÄga:
SELECT
name
FROM sys.databases
WHERE database_id=6;
GOI mitt fall Àr det fortfarande samma .
2.2) Avkodar hobt_id
I den funna databasen mĂ„ste vi utföra en frĂ„ga till sys.partitions med ett par joins som kommer att hjĂ€lpa oss att bestĂ€mma namnen pĂ„ tabellen och indexetâŠ
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;
GOHan berÀttar för mig att frÄgan vÀntade pÄ lÄset Application.Countries, med hjÀlp av indexet PK_Application_Countries.
2.3) Nu lite magi %%lockres%% â om du vill ta reda pĂ„ vilken post som var lĂ„st
Om jag verkligen vill veta vilken rad som behövde lÄsa kan jag ta reda pÄ det med hjÀlp av en frÄga till sjÀlva tabellen. Vi kan anvÀnda den icke-dokumenterade funktionen %%lockres%% för att hitta posten som matchar den magiska hash.
TÀnk pÄ att denna frÄga kommer att skanna hela tabellen, och pÄ stora tabeller kan det bli ganska besvÀrligt:
SELECT
*
FROM Application.Countries (NOLOCK)
WHERE %%lockres%% = '(ce52f92a058c)';
GO Jag lade till NOLOCK () eftersom lĂ„sningar kan bli ett problem. Vi vill bara snabbt kolla vad som finns dĂ€r just nu, inte vad som fanns dĂ€r nĂ€r transaktionen började â jag tror inte att datakonsistens Ă€r viktigt för oss.
Voila, posten som vi kÀmpade för!

Tack och vidare lÀsning
Jag minns inte vem som först beskrev mÄnga av dessa saker, men hÀr Àr tvÄ inlÀgg om de minst dokumenterade grejerna som kan intressera dig:
- Paul Randals inlÀgg om (som vi hittade vÄra data i det första exemplet)
- En frÄga pÄ StackOverflow om (som vi fann data i det andra exemplet). Ett av svaren leder till ett inlÀgg .
KĂ€lla: habr.com
