Поиск повреждённого объекта по номеру повреждённой страницы в 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:



    Однако, если вам, как и нам, не повезло, вы увидите следующее:
    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



    И там, в случае, если 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



    И вот тут уже есть корректный 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
    Поделиться публикацией

    Комментарии 7

      0
      Причину повреждения индекса выяснили?
      Просто так такая фигня у меня за 10 лет ни разу не случалась
        0
        Без меня выясняют, если узнаю — отпишусь.
          0
          Было у меня несколько случаев, за давностью лет не помню подробностей.
          MSSQL+1С на одном сервере, все стандартно. Еще была самописная программа на .net, которая из базы 1с выгружала номенклатуру и отправляла на кассы. Как всегда, внезапно, в пятницу вечером номенклатура перестала отправляться. В логах программы OutOfMemoryException, с чего вдруг непонятно, новых товаров в огромных количествах никто не создавал. В логах сервера SQL также появились непонятные Exception in thread… В конце концов сделали DBCC CHECKDB, были найдены какие-то ошибки, их исправили, и отправка прейскурантов благополучно заработала.
          Через полгода опять возникла такая же ошибка, и все тоже самое. Заподозрили неладное. Нашли время для проверки памяти, memtest где-то ближе к концу в последней плашке на определенном паттерне нашел несколько сбоев. Память была без ECC.
          Другой сервер, бухгалтер при запуске определенного отчета в 1с за определенный период получал ошибку HRESULT 0x-что-тотам. DBCC показал ошибку в файле базе. В конечном счете выяснилось, что на диске был bad block, и попал он именно на файл базы. При этом на сервере был RAID1 на встроенном в материнку чипсете, который fakeraid. Тогда еще возник локальный мем в отделе, как 1с протерла дырку в жестком диске.
            0
            Причина — умирающий диск. Сегодня умер окончательно.
              0
              Ясно, тут уже знания SQL не помогут
            0
            Все таки люблю ваши статьи читать. Спасибо за полезный материал. Чисто из любопытства не подскажите тулы самописные, которые напрямую будут из mdf / ldf данные читать?
              0
              Спасибо! :)
              С такими утилитами сталкиваться пока не приходилось и надеюсь (тьфу-тьфу-тьфу) не придётся, поэтому подсказать не могу.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое