Дешифрирајте го клучот и ресурсот на чекање на страницата во ќор-сокак и заклучување

Ако го користите извештајот за блокиран процес или периодично собирате графикони за ќор-сокак обезбедени од SQL Server, ќе наидете на вакви работи:

waitresource=„СТРАНИЦА: 6:3:70133“

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

Понекогаш ќе има повеќе информации во џиновскиот XML што го проучувате (графиците за ќор-сокак содржат листа на ресурси што ви помагаат да ги дознаете имињата на објектите и индексите), но не секогаш.

Овој текст ќе ви помогне да ги дешифрирате.

Сите информации што се тука се на Интернет на разни места, само се многу дистрибуирани! Сакам да составам сè заедно - од DBCC PAGE до hobt_id и до недокументираните функции %%physloc%% и %%lockres%%.

Прво, ајде да зборуваме за чекањата на заклучувањата на СТРАНИЦА, а потоа ќе преминеме на заклучувањата KEY.

1) waitresource=„СТРАНИЦА: 6:3:70133“ = Ид_база на податоци: ИД на датотека: Број на страница

Ако вашето барање чека на заклучување на СТРАНИЦА, SQL Server ќе ви ја даде адресата на таа страница.

Разложувајќи ја „СТРАНИЦА: 6:3:70133“ добиваме:

  • ID_база на податоци = 6
  • data_file_id = 3
  • број_страница = 70133

1.1) Декриптирај database_id

Ајде да го најдеме името на базата на податоци користејќи го барањето:

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

Ова е јавно DB WideWorldImporters на мојот SQL Server.

1.2) Барање на името на датотеката со податоци - доколку сте заинтересирани

Ќе користиме data_file_id во следниот чекор за да го најдеме името на табелата. Можете едноставно да прескокнете на следниот чекор, но ако ве интересира името на датотеката, можете да го најдете со извршување на барање во контекст на пронајдената база на податоци, заменувајќи го data_file_id во ова барање:

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

Во базата на податоци WideWorldImporters ова е датотека наречена WWI_UserData и ја вратив на C:MSSQLDATAWideWorldImporters_UserData.ndf. (Упс, ме фати како ставам датотеки на системскиот диск! Не! Тоа беше непријатно).

1.3) Добијте го името на објектот од DBCC PAGE

Сега знаеме дека страницата #70133 во датотеката со податоци 3 припаѓа на базата на податоци WorldWideImporters. Можеме да ја погледнеме содржината на оваа страница користејќи ја недокументираната СТРАНИЦА DBCC и знаменцето за трага 3604.
Забелешка: Претпочитам да користам DBCC PAGE на обновена копија од резервна копија некаде на друг сервер, бидејќи тоа е недокументирана работа. Во некои случаи, таа може да резултира со создавање на депонија (прибл. преведувач - врската, за жал, не води никаде, но судејќи по URL-то, зборуваме за филтрирани индекси).

/* 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

Лизгајќи низ резултатите, можете да најдете object_id и index_id.
Дешифрирајте го клучот и ресурсот на чекање на страницата во ќор-сокак и заклучување
Скоро готово! Сега можете да ги најдете имињата на табелите и индексите користејќи го барањето:

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

И сега гледаме дека чекањето за заклучување беше на индексот PK_Sales_OrderLines на табелата Sales.OrderLines.

Забелешка: Во SQL Server 2014 и подоцна, името на објектот може да се најде и со помош на недокументираните DMO sys.dm_db_database_page_allocations. Но, мора да ја прашате секоја страница во базата на податоци, што не изгледа многу кул за големи бази на податоци, па затоа користев DBCC PAGE.

1.4) Дали е можно да се видат податоците на страницата што е блокирана?

Па, да. Но... дали сте сигурни дека навистина ви треба?
Бавно е дури и на мали маси. Но, некако е кул, затоа што сте прочитале до сега... ајде да зборуваме за %%physloc%%!

%%physloc%% е недокументирана магија што враќа физички идентификатор за секој запис. можеш да користиш %%physloc%% заедно со sys.fn_PhysLocFormatter во SQL Server 2008 и повисоко.

Сега кога знаеме дека сакаме да ја заклучиме страницата во Sales.OrderLines, можеме да ги погледнеме сите податоци во оваа табела, која е зачувана во податочната датотека #3 на страница #70133, користејќи го ова барање:

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

Како што реков, бавно е дури и на мали маси. Го додадов NOLOCK на барањето бидејќи сè уште немаме гаранција дека податоците што сакаме да ги погледнеме се потполно исти како кога беше откриена бравата - за да можеме безбедно да правиме валкани читања.
Но, побргу, барањето ми ги враќа истите 25 редови за кои се бореше нашето барање
Дешифрирајте го клучот и ресурсот на чекање на страницата во ќор-сокак и заклучување
Доста е за заклучувањата на СТРАНИЦА. Што ако чекаме заклучување KEY?

2) waitresource=„КЛУЧ: 6:72057594041991168 (ce52f92a058c)“ = Database_Id, HOBT_Id (магичен хаш што може да се дешифрира со помош на %%lockres%% ако навистина го сакате тоа)

Ако вашето барање се обиде да заклучи запис во индексот и самото се заклучи, ќе завршите со сосема поинаков тип на адреса.
Прекршувајќи го „6:72057594041991168 (ce52f92a058c)“ на делови, добиваме:

  • ID_база на податоци = 6
  • hobt_id = 72057594041991168
  • магичен хаш = (ce52f92a058c)

2.1) Декриптирај database_id

Ова функционира исто како и примерот погоре! Најдете го името на базата на податоци користејќи го барањето:

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

Во мојот случај сè уште е исто DB WideWorldImporters.

2.2) Дешифрирајте hobt_id

Во контекст на пронајдената база на податоци, треба да извршите барање за sys.partitions со пар спојувања кои ќе помогнат да се одредат имињата на табелата и индексот...

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

Ми кажува дека барањето чекало на заклучувањето на Application.Countries користејќи го индексот PK_Application_Countries.

2.3) Сега малку магија %%lockres%% - ако сакате да дознаете кој запис е заклучен

Ако навистина сакам да знам на кој ред беше вклучена бравата, можам да дознаам со пребарување на самата табела. Можеме да ја користиме недокументираната функција %%lockres%% за да најдеме запис што одговара на магичниот хаш.
Ве молиме имајте предвид дека ова барање ќе ја скенира целата табела, а на големите маси ова можеби не е воопшто забавно:

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

Додадов NOLOCK (по совет на Клаус Ашенбренер на Твитер) бидејќи блокадите можат да станат проблем. Ние само сакаме да погледнеме што има сега, а не што беше таму кога започна трансакцијата - мислам дека не ни е важна конзистентноста на податоците.
Voila, рекордот за кој се боревме!
Дешифрирајте го клучот и ресурсот на чекање на страницата во ќор-сокак и заклучување

Признанија и понатамошно читање

Не се сеќавам кој прв опиша многу од овие работи, но еве два поста за најмалку документирани работи што можеби ќе ви се допаднат:

Извор: www.habr.com

Додадете коментар