Пошкоджена база даних (help, my database is corrupt
Пошкоджена база даних - це, напевно, один з найгірших нічних кошмарів більшості адміністраторів баз даних. Результатом пошкодження є простої, крики менеджерів і всякі інші неприємні штуки.
У цій статті я поясню що не можна робити з пошкодженої базою даних і опишу дещо з того, що повинно бути зроблено, деякі види пошкоджень і як їх можна виправити.
Як виявити, що база даних пошкоджена
Зазвичай пошкодження чудово виявляються при спробі доступу до пошкодженої сторінці. Запити, бекапи або процедури реіндексація завершуються помилками з високими рівнями серйозності.
Ось пара прикладів системних повідомлень при виявленні пошкодження БД:
SQL Server detected a logical consistency-based I / O error: incorrect checksum (expected: 0xfdff74c9; actual: 0xfdff74cb). It occurred during a read of page (1: 69965) in database ID 13 at offset 0x0000002229a000 in file D: DevelopDatabasesBroken1.mdf.
Attempt to fetch logical page 1: 69965 in database 13 failed. It belongs to allocation unit 72057594049069056 not to 281474980642816.
Що робити якщо база даних все-таки пошкоджена
- Не панікувати
- Чи не від'єднувати (detach) її
- Чи не перезапускати SQL Server
- Чи не починати відновлення відразу
- Запустити перевірку цілісності
- знайти причину
Не панікувати
Найважливіше, при виявленні пошкодження БД - це не панікувати. Будь-які прийняті рішення повинні бути ретельно зважені, до уваги повинні бути прийняті всі можливі чинники. Страшенно просто погіршити ситуацію прийнявши не до кінця обдумане рішення.
Чи не від'єднувати базу даних
У більшості випадків, коли SQL Server обнаржівает пошкодження бази даних, це означає, що в БД насправді є пошкоджені сторінки. Спроба переконати SQL Server що це не так, шляхом від'єднання (detach) і повторного приєднання (attach) БД, бекапа і подальшого відновлення, перезапуску служби SQL Server, або перезавантаження сервера, не призведе до того, що помилка зникне.
Якщо база даних пошкоджена і SQL Server виявить це при приєднанні, він не зможе приєднати її. Є кілька способів змусити його побачити цю БД, але набагато краще просто не від'єднувати її.
Чи не перезапускати SQL Server
Точно так же, як при від'єднанні-приєднання, перезапуск служби SQL Server не зможе виправити виявлені помилки (якщо вони є).
Перезапуск служби може зробити ситуацію гірше. Якщо SQL Server виявить помилки під час виконання фази відновлення (recovery) БД після перезапуску, він позначить її як "suspect", що сильно ускладнить процес відновлення БД.
Чи не починати відновлення відразу
У вас може виникнути спокуса просто запустити DBCC CHECKDB з одним з "відновлювальних" параметрів (зазвичай допускають втрату даних) і сподіватися, що все стане краще (з мого досвіду - перше що рекомендують на "непрофільних" форумах по SQL Server - запустити DBCC CHECKDB REPAIR_ALLOW_DATA_LOSS - прим. перекладача). У багатьох випадках запуск такого відновлення не рекомендується. Він не гарантує виправлення всіх помилок і може привести до неприпустимою втрати даних.
Таке відновлення - це останній крок при виправленні помилок. Воно повинно бути запущено тільки якщо у вас вже немає іншого вибору, але ніяк не в першу чергу.
Запустити перевірку цілісності
знайти причину
Після того як помилки виправлені, роботу не можна вважати закінченою. Якщо причина цих помилок не встановлена, вони можуть виникнути знову. Зазвичай, основною причиною помилок є проблеми з підсистемою введення-виведення, але вони також можуть бути викликані неправильною роботою "низкоуровнего ПО" (на кшталт антивіруса), діями людини, або багами самого SQL Server.
Що далі
Невірна інформація про вільне місце на сторінці
Msg 2508, Level 16, State 3, Line 1
The In-row data RSVD page count for object "Broken1", index ID 0, partition ID 76911687695381, alloc unit ID 76911687695381 (type In-row data) is incorrect. Run DBCC UPDATEUSAGE.
Пошкодження тільки некластерних індексів
В цьому випадку, пошкодження може бути повністю виправлено видаленням пошкоджених некластерних індексів і повторним їх створенням. Перестроювання індексу (ALTER INDEX REBUILD) в режимі on-line (і іноді в off-line) Новомосковскет сторінки старого індексу для створення нового і, отже, завершиться з помилкою. Тому, необхідно видалити старі індекси і створити їх заново.
Саме це зробить DBCC CHECKDB з параметром REPAIR_REBUILD, але база даних при цьому повинна бути в режимі одного. Ось чому зазвичай краще вручну виконати ці операції, щоб з базою даних можна було продовжувати працювати, поки індекси будуть пересоздавал.
Якщо у вас недостатньо часу на те, щоб перебудувати потрібні індекси і в наявності є "чистий" (який не містить в собі помилок) повний бекап і бекапи журналу транзакцій з нерозірваної ланцюжком журналів, ви можете відновити пошкоджені сторінки з них.
Пошкодження LOB-сторінок
Msg 8964, Level 16, State 1, Line 1
Table error: Object ID 181575685, index ID 1, partition ID 72057594145669120, alloc unit ID +72057594087800832 (type LOB data). The off-row data node at page (1: 2444050), slot 0, text ID 901891555328 is not referenced.
Помилка говорить про те, що існують LOB-сторінки (Large OBject), на які не посилається жодна сторінка з даними. Таке може статися, якщо раніше був пошкоджений кластерний індекс (або купа) і його пошкоджені статті було вилучено.
Якщо CheckDB говорить тільки про таких помилках, то можна запускати DBCC CHECKDB з параметром REPAIR_ALLOW_DATA_LOSS - ці сторінки будуть знищені. Оскільки у вас все одно немає сторінок з даними, які посилаються на ці сторінки, більшою втрати даних вже не буде.
Помилки, пов'язані з виходом за межі допустимого діапазону
Msg 2570, Sev 16, State 3, Line 17
Page (1: 1103587), slot 24 in object ID 34, index ID 1, partition ID 281474978938880, alloc unit ID 281474978938880 (type "In-row data"). Column "modified" value is out of range for data type "datetime". Update column to a legal value.
Пошкодження кластерного індексу або купи
Якщо буде виявлено, що пошкоджені сторінки купи або листового рівня (leaf pages) кластерного індексу - це означає, що дані на них втрачені. Сторінки листового рівня кластерного індексу містять безпосередньо сторінки даних і для них надмірність ніяк не забезпечується.
Якщо CheckDB повідомляє про пошкодження сторінок листового рівня кластерного індексу, необхідний "рівень відновлення" для DBCC CHECKDB - REPAIR_ALLOW_DATA_LOSS.
Приклади таких помилок:
Server: Msg 8976, Level 16, State 1, Line 2
Table error: Object ID 181575685, index ID 1, partition ID 76911687695381, alloc unit ID 76911687695381 (type In-row data). Page (1: 22417) was not seen in the scan although its parent (1: 479) and previous (1: 715544) refer to it. Server: Msg 8939, Level 16, State 1, Line 2
Table error: Object ID 181575685, index ID 0, page (1: 168576). Test (m_freeData> = PAGEHEADSIZE m_freeData <= (UINT)PAGESIZE - m_slotCnt * sizeof (Slot)) failed. Values are 44 and 8028.
Слід пам'ятати, що якщо помилки, які повертаються CheckDB, відносяться до index id = 0 або 1, це означає, що пошкоджені безпосередньо дані.
Такий тип помилок виправляється, але виправлення полягає в знищенні рядків або цілих сторінок. Коли CheckDB видаляє дані для виправлення помилки, обмеження, що накладаються зовнішніми ключами, які не перевіряються і ніякі тригери не спрацьовують. Рядки або сторінки просто видаляються. В результаті дані можуть виявитися не узгодженими, або може бути порушена логічна цілісність (на LOB-сторінки може більше не посилатися ні один рядок, або рядки некластерного індексу можуть вказувати "в нікуди"). Через таких наслідків, подібне відновлення, не рекомендується використовувати.
Якщо у вас є "чистий" бекап, відновлення з нього зазвичай є більш предпочительнее, для виправлення таких помилок. Якщо база даних знаходиться в повній моделі відновлення і у вас є бекапи журналу транзакцій з нерозірваної ланцюжком журналів (починаючи з останнього "чистого" повного бекапа), ви можете зробити бекап активної частини балки і відновити базу даних цілком (або тільки пошкоджені сторінки), в внаслідок чого дані взагалі не будуть втрачені.
Якщо бекапа з непошкодженими даними немає, у вас залишається тільки один варіант - запуск DBCC CHECKDB з параметром REPAIR_ALLOW_DATA_LOSS. Це зажадає перекладу бази даних в одного користувача режим на весь час виконання цієї процедури.
І хоча у вас немає можливості уникнути втрати даних, ви можете подивитися які дані будуть видалені з кластерного індексу. Для цього, подивіться цей пост Пола Ренадала.
пошкодження метаданих
Msg 3853, Level 16, State 1, Line 1
Attribute (object_id = 181575685) of row (object_id = 181575685, column_id = 1) in sys.columns does not have a matching row (object_id = 181575685) in sys.objects.
невиправні пошкодження
CheckDB не може виправити все. Будь-які помилки на кшталт наведених нижче непоправні і єдиний варіант - це відновлення бази даних з резервної копії, в якому немає цих ушкоджень. Якщо у вас є повний бекап і ланцюжок журналів не порушена до поточного часу, ви можете забекапіть заключний фрагмент журналу транзакцій і база даних може бути відновлена без втрати будь-яких даних.
Якщо таких резервних копій немає, єдине що ви можете зробити - заскріптованних ті об'єкти і вивантажити ті дані, які ще доступні. Цілком ймовірно, що через пошкодження не всі дані будуть доступні, і, швидше за все, не всі об'єкти зможуть бути заскріптованних без помилок.
Пошкодження системних таблиць
Msg 7985, Level 16, State 2, Line 1
System table pre-checks: Object ID 4. Could not read and latch page (1: 358) with latch type SH.
Check statement terminated due to unrepairable error. Msg 8921, Level 16, State 1, Line 1
Check terminated. A failure was detected while collecting facts. Possibly tempdb out of space or a system table is inconsistent.
CheckDB залежить від декількох критично важливих системних таблиць, для того щоб отримати уявлення про те, що повинно бути в базі даних. Якщо самі ці таблиці пошкоджені, то CheckDB не може навіть припустити що має бути в базі даних і з чим порівняти поточний стан справ, не кажучи вже про те, щоб щось виправити.
Пошкодження "карт розподілу"
Msg 8946, Level 16, State 12, Line 1
Table error: Allocation page (1: 2264640) has invalid PFS_PAGE page header values. Type is 0. Check type, alloc unit ID and page ID on the page. Msg 8998, Level 16, State 2, Line 1
Page errors on the GAM, SGAM, or PFS pages prevent allocation integrity checks in database ID 13 pages from (1: 2264640) to (1: 2272727)
В цьому випадку, одна або кілька сторінок визначають розміщення даних в БД (карти розподілу - прим. Перекладача) пошкоджені. Ці сторінки використовуються для того щоб визначати які сторінки і екстенти в БД використовуються, а які вільні. CheckDB не може виправити такі помилки, оскільки практично неможливо визначити (без цих сторінок) які екстенти використовуються для розміщення даних, а які ні. Просте видалення такої "карти розподілу" неможливо, оскільки видалення будь-який з них спричинить за собою видалення 4 GB даних.
Пошук допомоги
Якщо ви не впевнені в тому що вам потрібно зробити - зверніться за допомогою. Якщо раптом ви отримуєте повідомлення про пошкодження БД, яке вам незрозуміло і яке не описано вище - зверніться за допомогою. Якщо ви не впевнені в тому, що вибрали найкращий метод відновлення - зверніться за допомогою.
Якщо у вас є Senior DBA, зверніться до нього. Якщо у вас є "наставник" - запитаєте у нього. Запитайте поради на форумах, але пам'ятайте, що не всі поради отримані на форумах корисні. Насправді, саме там час від часу публікуються абсолютно неправильні і навіть небезпечні рішення.
Зверніться в службу підтримки Microsoft, нарешті. Це буде не безкоштовно, але вони дійсно знають що можна зробити з пошкодженої базою даних і цілком ймовірно, що якщо ваша база даних критична для підприємства, то вартість простою під час самостійного пошуку рішення буде набагато вище ніж вартість звернення в саппорт.
висновок
У цій статті я дав кілька прикладів того, що можна зробити при виявленні пошкодженої БД і, що навіть важливіше, того, що робити не треба. Сподіваюся, що тепер ви краще розумієте які методи можна застосовувати для вирішення описаних проблем і наскільки важливо мати хороші бекапи (і правильно вибрати модель відновлення - прим. Перекладача).