Поиск повреждённого объекта по номеру повреждённой страницы в MS SQL Server 2005

На днях одна из баз данных MS SQL Server перешла в Suspect, в журнале было сообщение об ошибке:

Msg 7105, Level 22, State 9, Line 14
Database ID 6, page (1:386499), slot 0 for LOB data type node does not exist. This is usually caused by transactions that can read uncommitted data on a data page. Run DBCC CHECKTABLE.


Базу перевели в Emergency и попытались выполнить DBCC CHECKDB, но выполнение сразу же прерывалось:

Msg 8921, Level 16, State 1, Line 13
Check terminated. A failure was detected while collecting facts. Possibly tempdb out of space or a system table is inconsistent. Check previous errors.
Msg 7105, Level 22, State 9, Line 13
Database ID 6, page (1:386499), slot 0 for LOB data type node does not exist. This is usually caused by transactions that can read uncommitted data on a data page. Run DBCC CHECKTABLE.


С аналогичной же ошибкой прерывалось выполнение команды DBCC CHECKALLOC. Осложнялось всё тем, что SQL Server был версии 9.0.1399, т.е. RTM, без каких-либо обновлений.

Попытки использовать хинт TABLOCK и явным образом повысить уровень изоляции транзакций, ни к чему не привели (места на дисках с tempdb было достаточно и DBCC CHECKALLOC с WITH ESTIMATEONLY завершался с той же ошибкой). Накатывать SP на сервер с повреждённой БД крайне не хотелось, а с каким конкретно объектом проблема, было абсолютно непонятно. Кроме того, создавалось впечатление, что сообщение DBCC CHECKDB имеет мало общего с реальностью, поскольку в msdb.dbo.suspect_pages была одна запись, но номер страницы отличался от того, который выводил DBCC CHECKDB.

Для того, что последовать инструкциям DBCC CHECKDB и выполнить DBCC CHECKTABLE, нужно было узнать таблицу. И после долгих поисков, одна инструкция нашлась.

Примечание
Я прошу прощения, что номера таблиц в сообщениях об ошибках и в коде не совпадают. Ошибки я взял из журналов, а код уже после выполняю в тестовом окружении на другой, живой базе.

Мы использовали алгоритм ниже для определения object_id обеих страниц — из DBCC CHECKDB и suspect_pages. Проблема оказалась в странице из suspect_pages



Первое, что нужно сделать, это выполнить (в контексте повреждённой базы данных) — это DBCC PAGE (database_id, file_id, page_id, printopt):

DBCC TRACEON (3604);
DBCC PAGE(5, 1, 3242342, 0)
DBCC TRACEOFF (3604);


либо:

DBCC PAGE(5, 1, 3242342, 0) WITH TABLERESULTS.


Если вам повезло (или вы играетесь на живой базе), в результате вы увидите поле Metadata: ObjectId и, собственно нужный object_id:

1piry9lqpblqbq42gv9im7kgdii.png

Однако, если вам, как и нам, не повезло, вы увидите следующее:

Metadata: = Unavailable in offline DB

Если метаданные недоступны, ещё не всё потеряно, в этом случае, нам нужно поле m_objId (AllocUnitId.idObj). Если m_objId = 255, беда закрывайте статью и ищите что-то другое (пытайтесь заскриптовать всё что можно и утащить данные, выполнять DBCC CHECKDB с «восстановительными» параметрами вслепую и т.д.).
На скриншоте видно, что у меня m_objId = 9931, т.е. можно продолжать.

Теперь нужно выполнить небольшие вычисления, чтобы вычислить Allocation Unit ID (подробнее про Allocation Units можно прочитать здесь):

Allocation Unit ID = m_objid * 65536 + (2^56)

В нашем случае:

Allocation Unit ID = 9931×65536 + (2^56) = 72057594688765952


Итак, зная Allocation Unit ID, можно посмотреть что у нас в системном представлении sys.allocation_units:

SELECT * FROM sys.allocation_units 
WHERE allocation_unit_id = 72057594688765952


auu9ltgpmmjqrhjttmrefhezdli.png

И там, в случае, если type = 1 или 3 (IN_ROW_DATA, ROW_OVERFLOW_DATA), столбец container_id = sys.partitions.hobt_id («Heap-Or-B-Tree ID»), т.е. можно выполнить запрос:

SELECT * FROM sys.partitions WHERE hobt_id = 72057594661437440


ply0cyyuympgzig9rdwu5b6axgy.png

И вот тут уже есть корректный object_id и index_id. Теперь можно посмотреть что там у нас в sys.objects и sys.indexes, да и просто выполнить:

SELECT OBJECT_NAME(object_id)


К счастью, и в реальной ситуации, и здесь поверждённым оказался некластерный индекс, после перестройки которого всё пришло в норму (на самом деле нет, но это уже другая история).

Ссылки:
How to use DBCC PAGE
Troubleshooting and Fixing SQL Server Page Level Corruption
What Are Allocation Units?
Finding a table name from a page ID
sys.allocation_units

© Habrahabr.ru