Проверка базы firebird на наличие ошибок

В одной из программ (Суперокна), работающих на СУБД FireBird стала возникать ошибка: BLOB NOT FOUND. Тестирование и исправление встроенными средствами программы результата не дало. Пришлось воспользоваться утилитами Firebird gfix.exe (проверка и исправление ошибок) и gbak.exe  (создание и восстановление из резервной копии).

Как восстановить поврежденную БД Firebird

  1. Выключаем сервер Firebird. Для этого останавливаем службу Firebird Server
  2. Копируем нужный файл базы данных. Лучше временно скопировать файл в папку с установленным Firebird. В моем случае это была папка C:Program FilesFirebirdBin
  3. Включаем сервер Firebird
  4. Запускаем командную строку с правами администратора. и переходим в папку с установленным Firebird (при помощи команды cd Имя папки)
  5. Проверяем базу данных на повреждения
    gfix.exe -v -full -user sysdba -password masterkey base.fdb

  6. В параметре -user указываем имя пользователя БД , а в параметре –password пароль пользователя. Эти параметры нужно указывать для всех команд по восстановлению БД

  7. Если в отчете есть ошибки, то исправляем их командой
    gfix.exe –mend -user sysdba -password masterkey base.fdb

  8. Повторно проверяем на повреждения базу данных
    gfix.exe -v -full -user sysdba -password masterkey base.fdb

  9. Если ошибки сохранились то делаем резервную копию БД 
    gbak -b -v -ig -g  -user sysdba -password masterkey base.fdb backup.fbk

    Основные параметры:

    base.fdb – поврежденный файл базы данных
    backup.fbk – файл резервной копии

    -ig – ошибки контрольных сумм будут игнорироваться
    -g – запрет сборки мусора во время резервирования

  10. Восстанавливаем  базу данных из резервной копии
    gbak -c -v -user sysdba -password masterkey backup.fbk newbase.fdb

  11. Проверяем новую базу на ошибки

После проделанных операций удалось восстановить работу базы данных Firebird и избавиться от ошибки BLOB NOT FOUND.

Поделиться ссылкой:

Похожие записи

KDV, 03.09.2002, исправления и дополнения – 25.01.2003, 06.02.2003, 15.04.2003, 16.05.2003, 02.09.2003, 26.11.2003, 03.02.2004, 06.09.2004, 17.02.2005, 05.07.2007 11.10.2007, 2015.10.11.

Если Вам нужно действительно починить БД Firebird

Самый быстрый и надежный способ починить серьёзно поврежденную БД относительно небольшого размера (до 65Гб) – это использовать инструмент IBSurgeon FirstAID, который может починить практически все повреждения БД и спасти оставшиеся данные.

Содержание

  • Введение
  • Повреждения базы данных
    • Отключение питания сервера
    • Дефекты оборудования
      • Память
      • Диски
      • Контроллеры
      • Другие программы
    • Сбои самого сервера
    • Остановка во время сборки мусора
    • Повреждения индексов
    • Повреждения таблиц
  • Ремонт БД
  • Невосстанавливаемый backup
    • Проблемы с индексами
      • Дубликаты в уникальных индексах
      • Отсутствующие записи по Foreign key
    • Добавленный столбец NOT NULL
    • Изменение типа столбца
    • Check constraint, не соответствующий хранимым данным
    • Процедуры и триггеры
    • Повреждения системных таблиц
    • Проблемы оборудования
  • Дополнительные способы ремонта БД
  • Перевод документа Database Validation and Repair
  • Заключение

 

Первый закон Чизхолма: Все, что может испортиться, портится.
Следствие: Все, что не может испортиться, портится тоже.

Введение

На эту тему можно было бы процитировать половину законов Мэрфи. Вместо этого я процитирую начало статьи в журнале Intelligent Enterprise

Установлено, что большинство предприятий, переживших крупную необратимую потерю корпоративных данных, прекращают свое существование в течение трех лет после такого инцидента. Мысль о возможной катастрофе неприятна для ИТ- и бизнес-менеджеров, поэтому они часто не принимают серьезных предупредительных мер.

Когда я читал эту статью, меня больше всего потрясло именно второе предложение в этом абзаце. К сожалению, это грубая правда жизни. И здесь под «предупредительными мерами» ни в коем случае нельзя понимать «покупку техники brand-name». Как показала дискуссия в конференции epsylon.public.interbase, «брэнды» тоже ломаются, иногда даже чаще чем «самосборные машины». Поэтому «предупредительные меры»  это не только качественное «железо», но и планирование резервного копирования данных.

Планирование-планированием, но вот еще одна цитата, уже из письма:

«… днем сдох IBM-ер у моих «ну очень уважаемых клиентов», а вместе с ним и БД акционерок и акционеров, с общим объемом уставных фондов около полуярда гривень (около 100 млн вечно-зеленых гульденов). Резервация ее делалась на RW-диск, отформатированный под UDF. Но кто-то (никто не признался) вставил вместо него неотформатированную RW. И так она пролежала там на протяжении последнего месяца. Как раз в октябре/ноябре у них шел сплошной поток обновлений в БД….»

О технике (железе) здесь будет сказано дальше, но немного. Основной же целью этой статьи является перечисление типов повреждений базы данных, способы ремонта, а также предотвращения порчи баз данных. Обычно при сбое базы данных ее можно починить утилитой gfix. Однако, иногда этот инструмент не помогает. В таких случаях нужно обратиться в конференцию, где попытаются помочь. В случае больших баз данных нужно быть готовым при починке полагаться только на себя, поскольку вряд ли получится отправить ~>1Гб базу (имеется в виду размер архива zip или rar, конечно) данных специалистам по email или на ftp. И нужно знать, что бывают случаи, когда базу данных восстановить не удастся никаким образом. Поэтому никогда не забывайте своевременно делать backup, а также проводить «контрольный» restore! (Разумеется, никогда нельзя делать restore на место оригинальной базы данных, т. е. с ключом -r. Вы рискуете остаться без базы данных).

Для ознакомления – перечень наиболее частых ситуаций, для которых компания iBase осуществляет платный ремонт БД. (Это не означает, что можно починить все – недавно базу со сбоем N2 починить не смогли, т. к. оказались затертыми не только половина данных, но и информация в системных таблицах о структуре записей). Если gfix не чинит базу данных, то в 70% случаев простых повреждений может помочь утилита IBSurgeon FirstAid. Для оценки повреждений БД имеет смысл сначала проверить БД с помощью бесплатного режима (с возможностью предпросмотра данных) FirstAID и прислать лог (в zip) на support@ibase.ru, т. к. может оказаться, что ваш случай попадает в худшие 30%. В любом случае не стоит надеяться на появление инструмента, который из любой самой убитой базы данных сможет вытащить ваши данные.

Если база убита, а есть только backup, и он тоже поврежден, или по иным причинам нужно извлечь из backup только часть данных – к вашим услугам инструмент IBBackupSurgeon. (FirstAid и IBBackupSurgeon – платные. Их можно купить).

Примечание. Чаще всего обращения о ремонте БД содержат не абстрактную просьбу «починить БД», а желание вытащить из поврежденной БД какие-то жизненно важные данные. Как раз этот случай зачастую требует именно ручного ремонта, поскольку автоматизируемый ремонт как gfix так и FirstAID старается отремонтировать БД для 100% возможности ее бэкапа, но не факт, что сделано это будет с максимальной сохранностью данных (см. Ремонт БД, примечание к пункту 5).

Примечание. Исследовать структуру БД самостоятельно можно при помощи утилиты IBSurgeon Viewer.

Повреждения базы данных

Существует несколько причин, при которых база данных может оказаться поврежденной. Здесь перечислены наиболее характерные.

Отключение питания сервера

Самый частый случай повреждения базы данных это отключение питания на сервере. Такие ситуации нужно пытаться предотвращать, используя аппаратные средства (UPS, RAID-контроллеры с батарейками). InterBase имеет два режима записи страниц – синхронный и асинхронный. Для всех версий до 6.0 создаваемые базы данных имели по умолчанию синхронный режим (Firebird v1.0 также имеет этот режим включенным по умолчанию). Для изменения режима можно воспользоваться утилитой gfix.

Включение синхронного режима:

gfix -write sync database.gdb

Включение асинхронного режима:

gfix -write async database.gdb

Синхронная запись означает, что измененные страницы базы данных не будут кэшироваться операционной системой, а будут записываться непосредственно на диск при выталкивании страниц из кэша на запись (на Windows это в буквальном смысле отсутствие флага lazy write при открытии файла БД). Это ухудшает производительность, поэтому большинство людей выключают forced writes. В этом случае измененные страницы находятся в кэше операционной системы до тех пор, пока операционная система не решит записать их на диск. В некоторых случаях при непрерывной работе с БД операционная система не сбрасывала измененные страницы на диск до тех пор, пока все пользователи не отсоединялись от базы данных. Понятно, что при выключении питания в этом случае повреждения базы данных могут быть максимальными. Причем, повреждения в данном случае происходят от «недозаписи» информации. Это куда менее печальный случай, чем «перезапись» информации случайными данными, о чем пойдет речь в следующем разделе. Однако, на Windows было обнаружено, что если у базы данных установлено Forced Write = Off, то при определенных условиях измененные страницы БД могли неделями не попадать в БД, и оставаться в кэше операционной системы. При этом, в случае сбоя сервера (или отключения питания), пропадало огромное количество изменений в БД (а база могла выглядеть вообще неповрежденной). То есть, БД как бы оказывалась «неизменяемой» в течение длительного времени. Для исключения данной проблемы в Firebird 1.5 были введены дополнительные параметры для принудительного сброса кэша операционной системы при ForcedWrite=Off (см. параметры MaxUnflushedWrites и MaxUnflushedWriteTime в firebird.conf).

Примечание. На современных дисковых системах включение Forced Write практически не влияет на производительность. Это заметно разве что на desktop-системах с IDE-дисками (старыми). Хотя вообще кэширование записи операционной системой дает выигрыш в производительности при массовых обновлениях данных.

Дефекты оборудования

Память

Самый частый дефект за последние полтора года – сбои памяти (RAM) (случаи такого рода сильно сократились в 2005 году). Очевидно, при использовании серверов «своей сборки» приобретается память подешевле, что приводит к соответствующим результатам. Желательно для сервера приобретать и материнскую плату и память с поддержкой ECC.Сбои памяти могут привести к достаточно тяжелым последствиям, которые описаны дальше в этом разделе, в главе «невосстанавливаемый backup». Сервер не только «пропускает» страницы базы данных через память, но и кэширует их в памяти. Поскольку контрольные суммы страниц были отключены еще в IB 5.0, повреждения обнаруживаются только когда происходит чтение данных с диска. Собственно, контрольные суммы страниц, даже если бы они и были, не помогут когда сервер будет записывать страницу на диск через сбойный участок памяти. В противном случае данные пришлось бы перечитывать, что весьма серьезно ухудшило бы производительность.Сбои памяти еще плохи тем, что в этом случае поврежденными как правило оказываются и база данных и ее shadow, если shadow используется в качестве «быстрой резервной копии» (т. к. запись на диск идет из одних и тех же участков памяти).

Диски

Упоминавшиеся выше контрольные суммы страниц данных были исключены в сервере IB 5.0 не зря. Дело в том, что эти контрольные суммы фактически дублировали контроль данных, который должен производиться дисковым накопителем. Раньше, лет 10-15 назад, bad-блоки появлялись часто, и существовали специальные утилиты для их исправления. Сейчас контроль ошибок не только может исправить данные на поврежденном блоке самостоятельно, но и прозрачно сохранит блок в рабочем месте диска, а плохой блок пометит к исключению из дальнейшего использования. Грубо говоря, нынешние диски либо работают, либо не работают целиком.

Контроллеры

Здесь можно отметить только то, что при сбое в работе контроллера некорректная информация может быть записана на все носители, которые подключены к этому контроллеру. Именно поэтому при организации shadow рекомендуется располагать ее на винчестере, подключенном к другому контроллеру.

Другие программы

InterBase, Firebird или Yaffil не работают с операционной системой на «внутреннем» уровне. Это обычное приложение, которое никогда не может вызвать сбой типа известного «синего экрана» в Windows NT. Поэтому если подобный сбой ОС произошел, в этом скорее виноваты некорректно работающие драйверы, другие программы или само оборудование (очень часто в «синем экране» виноваты драйвера видео).

Примечание. В W2K по умолчанию при сбое ОС происходит рестарт системы. Если хотите видеть, что система зависла, в My Computer/Properties, Advanced, Startup and Recovery уберите чекбокс Automatically reboot.

В предыдущем разделе был приведен пример повреждения БД, когда не вся новая информация записана в БД. При сбоях ОС может случиться обратная ситуация – данные могут продолжать записываться в файл БД при «зависании» ОС, однако это могут быть уже совсем не те данные, которые собирался записать Interbase в файл базы данных. В этом случае повреждения оказываются наиболее серьезными, которые редко могут быть исправлены при помощи gfix.

Сбои самого сервера

Разумеется, InterBase (Firebird, Yaffil) как и любое другое ПО не является идеальной программой. Идеальной, конечно, в смысле отсутствия ошибок. Если «железо» работает нормально, сервер может сам «сломаться» и либо просто перестать работать, либо испортить базу данных. Конкретный случай последних 1.5-2-х лет – превышение размера в 4 гигабайта файлом базы данных при использовании старых версий InterBase (или ранних версий Firebird на определенных платформах). Раньше, и в том числе в 5.x (1997-2000 годы), код сервера содержал вызов обычной функции позиционирования по файлу БД (seek), которая не могла адресовать более 4-гигабайт (в те далекие времена просто не было файловых систем, которые поддерживали файлы больше 4-х гигабайт). Когда в функцию передавалось такое большое число, оно обрезалось по старшим разрядам. Происходила такая ситуация при операции расширения файла БД, т. е. при записи новых страниц, а следовательно файл БД оказывался «затертым» новой информации с самого начала, т. е. с нулевой страницы (страница заголовка БД). Если новых страниц к записи было много, то уничтожалась начальная часть БД, где как правило содержатся системные таблицы, страницы информации о транзакциях и т. п. Причем борьба с пресловутым размером файла в 4 гигабайта дольше всего велась на Linux, что связано не только с кодом СУБД, но и с поддержкой файлов таких размеров самой операционной системой и ее файловыми системами. Firebird исправил эту проблему окончательно только в версии 1.0.2, причем 1.0.2 выпускался как в старом варианте, так и с 64bit-IO. Borland также не миновала чаша сия, и для IB 7 выпущен патч (7.0.1), который исправлял проблему с размером файла для Linux. Firebird для FreeBSD поддерживает файлы более 4 гигабайт начиная с версии 1.5.

На Windows в IB7, Firebird и Yaffil этой проблемы уже нет, т. е. файл БД может иметь размер и 10, и 20 и больше гигабайт.

В любом случае, при работе на Unix или Windows следует внимательно изучить возможности ядра и конкретной (используемой) файловой системы – способны ли они работать с файлами больше 4-х гигабайт, а также проверить версию IB/FB/YA, чтобы быть уверенным в корректной работе с такими файлами, или наоборот, сразу предусмотреть разбиение БД на многофайловую, например, «кусками» по 2-3 гигабайта.

В отношении файловых систем Windows известна следующая особенность: на FAT32 поддерживаются файлы размером не более 4 гигабайт (чаще всего указанное повреждение БД и происходит при использовании этой, фактически уже устаревшей, файловой системы). Допустим, размер вашей БД достиг 3-х гигабайт, и вы хотите перенести ее на NTFS, чтобы избежать ограничения в 4 Гб. Проблема в том, что с FAT32 на NTFS скопируется только 2 гигабайта из 3-х, из-за особенностей Windows. Это еще раз подчеркивает необходимость знания ограничений используемых файловых систем и их взаимодействия на одном компьютере. На текущий момент все последние версии InterBase, Firebird и Yaffil не имеют ограничений по размеру файла БД, для любых операционных систем. В остальных случаях, одной из характерных ошибок, которую наблюдают разработчики, является «cannot continue after bugcheck(xxx)» с любым номером ошибки. Это означает, что сервер во время работы перешел в такое состояние, что дальнейшая работа с базой данных может ее повредить. При этом рекомендуется рестартовать сервер, после чего желательно проверить базу данных утилитой GFIX.

Замечание. Сообщение bugcheck не имеет ничего общего с ошибками (багами, bugs), обнаруживаемыми и регулярно исправляемыми в коде сервера.

Остановка во время сборки мусора

Когда исходный код Interbase был опубликован, оказалась выявлена еще одна неприятная особенность, которая может привести к серьезным повреждениям базы данных. Если во время принудительного завершения работы сервера (gfix -shut …) были активные подключения и сервер занимался сборкой мусора (работал sweep thread), то база данных может быть повреждена (и чаще всего это так и происходит). Уменьшить вероятность таких повреждений можно только отключив автоматическую сборку мусора (gfix bd.gdb -housekeeping 0), а в случае принудительной сборки мусора (gfix -sweep) предварительно делать «быстрый» backup (gbak с ключом -g, то есть без сборки мусора), чтобы резервная копия базы данных оказалась самой свежей в случае сбоя и повреждения БД. В Yaffil эта проблема исправлена.

Повреждения индексов

Повреждения индексов могут происходить как по всем вышеперечисленным причинам, так и из-за ряда багов сервера при работе с индексами (исправлены в Firebird 1.5, Yaffil). Поскольку индексы не являются 100% необходимым видом объектов для функционирования базы данных, их повреждения обнаруживаются значительно позже (если администратор смотрит в interbase.log), чем повреждения других объектов БД (например, данных таблиц). И клиентские приложения могут продолжать функционировать в такой ситуации как и прежде.

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

SELECT * FROM TABLE

и с перебором по индексу

SELECT * FROM TABLE

WHERE FIELD > 0

где FIELD  столбец, по которому есть возможно поврежденный индекс, а > 0 – условие, которое однозначно будет выбирать все записи.
(разумеется, лучше этого не делать, а при подозрении на «пропадание записей» сразу посмотреть в interbase.log, перестроить те индексы, о повреждениях которых там сообщается.

В interbase.log пишутся только порядковые номера индексов (а не их имена) для конкретных таблиц, как это и указано в rdb$indices). Процесс backup поврежденные или неповрежденные индексы (за исключением повреждений индексов по системным таблицам) не интересуют, т. к. индексы в backup хранятся только в виде описания в системных таблицах (restore создает индексы по этим описаниям в самом конце процесса restore). Backup считывает записи в натуральном порядке и индексы не использует, поэтому все существующие (committed) записи обязательно попадут в backup. Однако, если поврежден уникальный индекс, то в определенных условиях существует вероятность повторной вставки записи в таблицу с уже существующим (уникальным) значением столбца. Эта ситуация может привести к невосстановимому backup, т. е. процесс restore остановится в момент создания уникального индекса, обнаружив дубликат уникального значения в восстановленных записях. Но такая проблема также не является катастрофической  процесс создания индексов выполняется самым последним, т. е. после того как абсолютно все объекты БД уже восстановлены в базе данных процессом restore. Если вдруг обнаружена проблема неуникальных данных при создании индекса, можно попробовать найти такую запись (и затем удалить лишнюю) запросом

SELECT ID, COUNT(*) FROM TABLE
GROUP BY ID
HAVING (COUNT(*)) > 1

где id столбец, по которому есть несоздаваемый уникальный индекс. После этого можно активировать индексы, которые не были восстановлены (установив RDB$INDICES.RDB$INACTIVE в 0, там, где этот столбец не 0).

Повреждения таблиц

Нормальная база данных  это не набор отдельных таблиц. Таблицы между собой могут быть достаточно сильно взаимосвязаны, вплоть до циклических ссылок. Поэтому даже один и тот же тип и объем повреждения может иметь разные последствия, в зависимости от того, с какой таблицей это произошло. Типичный пример: таблица CLIENTS  справочная, а ORDERS  оперативная. Если пропадет часть записей из ORDERS, то после починки БД будет нормально функционировать. Если же будет повреждена CLIENTS, то после починки в ORDERS будут записи, ссылающиеся на несуществующих клиентов. Таким образом БД вроде бы будет отремонтирована, но логическая целостность данных будет нарушена. Бороться с этой ситуацией никак невозможно, разве что чаще делая backup (поскольку справочники меняются реже, чем оперативные данные). Подобная ситуация (с повреждением master-таблицы) может возникнуть даже в том случае, если все записи пакета master-detail вставляются в одной транзакции, а Forced Write выключен (off)  страницы с записями detail могут быть записаны на диск операционной системой из кэша раньше, чем соответствующие им записи таблицы master. Это не приводит к «невосстановимому backup», но после restore придется или добавлять недостающие master-записи, или удалять «осиротевшие» detail-записи (в зависимости от сложности триггеров, обрабатывающих вставку в master или удаление в detail. Возможно, такие триггеры на время ремонта данных нужно будет отключить).

Также, в подобной ситуации, при restore отремонтированной базы данных могут оказаться неактивными (rdb$indices.rdb$index_inactive = 1) индексы по Foreign Key соответствующих связей master-detail. Активировать их можно после упомянутых вставок или удалений master/detail, путем установки rdb$indices.rdb$index_inactive в 0. О повреждениях системных таблиц см. дальше.

Ремонт БД

Для ремонта баз данных рекомендуется использовать именно утилиту командной строки gfix, несмотря на то, что раньше такие операции можно было делать из Server Manager, а сейчас из IBConsole или через Services API.

Внимание! Даже если у вас только что рухнула база, и вы хотите починить ее максимально быстро – все равно ВНИМАТЕЛЬНО прочитайте указанные ниже пункты от 1 до 8.

Повреждения баз данных могут быть исправлены как при помощи только gfix, так и одновременно gfix и gbak. Открываем консольное окно (cmd на W2K, или command на Win9x, или терминальное на Linux)

Лично я предпочитаю при операциях backup/restore всегда сохранять вывод в файл ключами -v -y outfile.txt. При обычном выводе на консоль «вывод» может потеряться, и тогда придется процедуру backup/restore повторять. Кроме того, в файле лога можно легко найти список объектов, которые успешно восстановлены.

1. Установите системные переменные, чтобы облегчить себе жизнь и не вводить постоянно для gfix/gbak параметры -user SYSDBA -pass masterkey

SET ISC_USER=SYSDBA
SET ISC_PASSWORD=masterkey

 

Внимание! Не оставляйте эти переменные после починки БД. В этом случае клиент с любой машины может подсоединиться к БД без указания имени пользователя и пароля (например, isql). Если не хотите устанавливать эти переменные, то по тексту дальше дописывайте
gbak …. -user SYSDBA -pass masterke
gfix …. -user SYSDBA -pass masterke

2. При починке работайте с копией базы данных, а не с оригиналом. У вас должен быть эксклюзивный доступ к БД

copy employee.gdb database.gdb
 

Внимание! Копировать БД таким способом можно только если вы абсолютно уверены, что к БД никто не подсоединен. База данных – файл произвольного доступа, а файловое копирование производится поблочно-последовательно. Поэтому если с БД кто-то работает, вы 100% получите «на выходе» испорченный файл БД вместо копии. Существуют редкие сообщения и о том, что при копировании с подключениями может испортиться и оригинальный файл БД, правда природа этого случая неясна.

3. Иногда помогает перед началом починки перевести базу данных в режим shutdown

gfix database.gdb -shut -force 0
 

Внимание! Не забудьте потом вернуть ее в online командой gfix database.gdb -online

4. Проверьте базу данных на повреждения

gfix при проверке БД на наличие повреждений выводит информацию о повреждениях в interbase.log или firebird.log (подробно) и на экран (суммарно). Поэтому чтобы посмотреть на подробное описание повреждений, перед запуском команды ремонта БД следует переименовать interbase/firebird.log (например, firebird1.log, firebird20071010.log и т. д.), чтобы уже хранимая там информация не мешала дополнительному выводу gfix. Когда сервер не обнаружит лог-файл, он его создаст заново.

gfix -v -full database.gdb

Если выдаются ошибки checksum error, то нужно выполнить следующую команду:

gfix -v -ignore database.gdb

5. Если предыдущая команда обнаружила ошибки, то нужно их исправить командой

gfix -mend database.gdb
если выдаются ошибки checksum error, то нужно добавить опцию -ig
gfix -mend -ig database.gdb
 

Внимание! Ключ -mend помечает поврежденные структуры как исключаемые при backup. В результате целостность между таблицами может быть нарушена, и даже если после этого backup пройдет, не факт что базу данных удастся восстановить из backup. В этом случае придется вручную копировать данные при помощи утилит вроде IBPump.

6. Проверим, все ли починилось

gfix -v -full database.gdb

7. Если на этот момент вы все еще видите ошибки, то надо попытаться сделать backup, при этом обязательно нужно отключать сборку мусора (ключ -g)

gbak -b -v -ig -g database.gdb database.gbk

ключ -ig игнорирует ошибки при чтении структур данных, и пытается сохранить в backup все неповрежденные структуры и данные.

Внимание! Никогда не указывайте ключ -ig при обычном бэкапе – если в базе есть ошибки, gbak их проигнорирует и вы не узнаете, что база была повреждена. В результате такой бэкап может быть невосстановимым.
Если ваши приложения работают с двухфазным коммитом, то возможно потребуется ключ -limbo для игнорирования зависших 2pc транзакций (limbo).

8. Теперь надо попытаться восстановить базу данных из backup

gbak -c -v database.gbk new.gdb
 

Внимание! Никогда не делайте restore поверх существующей базы данных. В случае ошибки при restore вы лишитесь оригинальной базы данных.

9. Если при restore есть ошибки, то нужно попробовать следующие ключи:
-inactive, если есть проблема с индексами, то с этим ключом база будет восстановлена с деактивированными индексами. Их можно потом активировать (altex index X active) поодиночке.
-one_at_a_time, этот ключ приводит к commit после восстановления каждой таблицы. По крайней мере будет восстановлена хотя бы часть таблиц.

Внимание! gbak в InterBase 7.x и выше по умолчанию не проверяет при restore следующие ограничения – not null, check, primary, unique, foregin key. Для восстановления оригинального функционирования gbak процесс restore должен проводиться с ключом -validate. У Firebird наоборот, проверка ограничений включена, поэтому для ее отключения нужно указывать ключ -no_validity. Если до сих пор так и не получилось сделать нормально backup и restore, но доступ к поврежденной базе все-таки есть, можно попробовать утилитой IBPump (или другими) скопировать хотя бы часть (неповрежденную) данных.

Невосстанавливаемый backup

Получить backup, который не пройдет restore («невосстанавливаемый» или «невосстановимый») вполне возможно по ряду причин и без сбоев сервера или оборудования. Для минимизации потраченного времени на поиск мест, где эти ошибки происходят, всегда нужно делать restore с ключом -v, и еще лучше – с выводом в файл (gbak …. -v -y rest_log.txt). Если с починкой не сможете справиться сами, то тогда как минимум сможете предъявить в news-конференции или службе технического сопровождения точное сообщение об ошибке.

Существует утилита IBBackupSurgeon, которая позволяет вытащить из бэкапа БД необходимую информацию, даже в случае повреждения файла бэкапа. Также его можно использовать для нормальных или поврежденных бэкапов с целью извлечения из них только части хранимой информации (данных, структур и т. п.).

Проблемы с индексами

Как известно, восстановление базы данных из бэкапа происходит следующим образом:

  1. gbak дает команду серверу создать новую БД с параметрами из бэкапа,
  2. gbak копирует в новую базу пользовательские метаданные из бэкапа,
  3. gbak копирует в новую базу пользовательские данные из бэкапа,
  4. gbak активирует (создает) все индексы.

Пункт 4, то есть создание индексов, не случайно выполняется после восстановления всех данных. Если бы индексы были активны сразу после пункта 2, то заливка данных в БД была бы очень медленной.Соответственно, раз индексы создаются в самом конце процесса restore, ошибки при их создании не являются фатальными для функционирования БД, разве что без индексов запросы будут работать очень медленно. Во всех версиях InterBase и Firebird (кроме Firebird 2.x) при первой же ошибке создания индекса дальнейшее создание индексов прекращается. В Firebird 2.x ошибка создания индекса сообщается, но создание других индексов продолжается, что позволяет легко идентифицировать неактивные индексы по окончании restore.

В данном случае считать базу «невосстановленной» нельзя. Но понятно, что из-за неактивности некоторых индексов определенные запросы будут работать медленнее, и ссылочная целостность по этим FK проверяться не будет.

Дубликаты в уникальных индексах

Как уже было упомянуто в начале статьи, повреждения индексов обычно незаметны. Более того, повреждения уникальных индексов (ограничения Primary Key, Unique или просто уникальный индекс) могут привести к появлению неуникальных дубликатов записей в таблице. Во время работы это заметно не будет, однако при попытке restore базы данных может быть выдано сообщение о невозможности создать конкретный индекс.Интересно, что пользователи InterBase 7.x/2007, 2009, XE могут вообще не обнаруживать эту проблему, т. к. gbak в InterBase 7.x и выше по умолчанию не проверяет при restore следующие ограничения – not null, check, primary, unique, foregin key. То есть база может пережить много циклов сохранения-восстановления без обнаружения данной проблемы. Данное поведение полезно только в том случае, когда вам нужно восстановить именно невосстанавливаемый с данными проверками бэкап. Для принудительного контроля логической целостности при restore настоятельно рекомендуется добавлять ключ -validate.

У Firebird наоборот, по умолчанию ограничения проверяются, а выключить их проверку можно указав ключ -no_validity. Соответственно, при наличии дубликатов в уникальном индексе нужно эти дубликаты найти, а затем удалить. После чего проверить логическую целостность БД через бэкап-рестор.

Отсутствующие записи по Foreign key

Этот тип логической нецелостности данных также был описан в первой части статьи. Бороться с этой проблемой можно как на оригинальной базе данных, так и на восстановленной из бэкапа. Нужно выполнить запрос, который покажет имеющиеся в детали и отсутствующие в мастере идентификаторы.

Затем можно или записи с этими идентификаторами удалить из таблицы деталей, либо добавить соответствующие записи в таблицу-мастер (первое быстро, но с потерей данных; второе – долго, зато данные остаются). После исправления несоответствий в мастер-детали можно пересоздать (или активировать индекс) Foreign key.

Добавленный столбец NOT NULL

Это самая частая проблема, с которой сталкиваются разработчики. Известно, что IB позволяет добавлять новые столбцы к таблице даже если есть любое количество данных в этой таблице. При этом меняется формат таблицы (запись в системных таблицах), однако существующие записи никаким образом не обновляются. Процесс backup запишет данные как есть, и не обнаружив значения столбца установит флаг соответствующего столбца у записи в NULL. При restore контроль not null не даст записать такие данные в таблицу.

Внимание! gbak в InterBase 7.x не проверяет при restore следующие ограничения – not null, check, primary, unique, foregin key. Для восстановления оригинального функционирования gbak процесс restore должен проводиться с ключом -validate.

В Firebird для gbak при restore нужно указывать дополнительный ключ -no_validity. Если такая ошибка при restore является единственной, то можно попытаться сделать следующее:

  1. В базе данных у столбца убрать контроль not null. Сделать backup только метаданных (ключ -m). Если оригинальной базы уже нет, т.е. есть только бэкап, то сделать restore только метаданных, убрать в базе у столбца контроль not null… сравнить побайтово бэкап метаданных, полученный пунктом 1, и бэкап с данными. Сравнивать, естественно, по размеру бэкапа метаданных. Просмотреть обнаруженные отличия hex-редактором, чтобы убедиться, что отличия находятся примерно в месте расположения объявления домена или столбца. Исправить отличия hex-редактором в backup.
  2. Сделать restore только метаданных. Если флаг not null у столбца или домена пропал, значит все хорошо, и можно делать restore всего backup.

Если приведенная процедура кажется вам сложной, или вы не хотите заниматься этим в будущем – перед бэкапом используйте утилиту CheckNull.

Изменение типа столбца

То же самое, что и предыдущий случай. Допустим, у таблицы есть строковый столбец. Его потребовалось заменить на столбец типа integer. IB допускает такую операцию (alter table alter column в IB6 и выше), но не пытается в момент изменения типа столбца преобразовать данные. Если хоть в одной записи в таком столбце случайно оказался символ, который невозможно преобразовать в число, restore не пройдет.

Помните, что исправить ситуацию повторным изменением типа столбца с integer на строку не удастся! В таких случаях обязательно надо перед изменением типа столбца проверять его на последующую читаемость операцией

SELECT CAST(FIELD as INTEGER) FROM TABLE

и просмотром всех записей возвращаемых этим запросом в любом инструменте. Если при просмотре всей таблицы не произошло ошибок вроде «transliteration … overflow…», то тип столбца можно смело менять. Если же ошибка была, нужно искать «проблемную» запись, и менять ее значение на допустимое для преобразования.То же самое относится и к случаю уменьшения длины строк. Операция alter, допустимая в IB 6 и выше, не даст уменьшить длину строкового столбца, т. к. данные могут оказаться длиннее нового размера столбца. Поэтому более безопасным способом (и для изменения типа столбца) является добавление нового столбца с нужным типом, копирование данных в него (update table set oldfield=newfield), удаление старого столбца и переименование нового в старое имя.

Однако такой способ часто не подходит, если на столбец есть масса ссылок из других объектов БД (например, таблиц), или объем данных очень велик. Изменить длину столбца или его тип в этом случае остается возможным только через системные таблицы. Но опять же, лучше заранее проверить максимальную длину данных (используя udf strlen в любой библиотеке udf, не из ib_udf) – select max(strlen(field)) from table.

Check constraint, не соответствующий хранимым данным

Cмысл проблемы состоит в том, что при restore сначала восстанавливаются метаданные, а потом уже сами данные. Исправить ситуацию можно используя при restore gbak с ключом -n, однако при этом не будут восстановлены ВСЕ check constraints у всех таблиц.

Данная ситуация также может возникнуть, например, если в для проверки столбца используется select. Если база данных была повреждена, и вы сделали gfix -mend, часть данных может пропасть (оказаться поврежденными и не попасть в backup). А раз часть данных пропала, ею могут оказаться как раз те данные, которые проверяли столбец по select. Также, в самых первых версиях InterBase 6.0 была ошибка, при которой check constraints проверяли данные до срабатывания триггеров. Таким образом, забыв о контроле check constraint можно было написать такой триггер, который изменял бы данные в противоречивое с check constraint состояние. Например, check constraint проверяет значение столбца > 0 и < 10, а триггер мог спокойно содержать текст вроде new.field + 100. В результате тоже можно было получить невосстановимый backup. В InterBase 6.5, последних версиях Firebird и Yaffil, эта ошибка исправлена.

Кроме перечисленных случаев для check constraints и computed by хочется отметить еще один. Синтаксис create/alter table допускает создание контроля (или вычисляемого) поля как SELECT из другой таблицы. Т. е. это разрешено в соответствии со стандартом, но может оказаться) крайне неэффективным.

Мне приходилось видеть запрос с sum и группировкой в check constraint. Это означает что при выборке такого столбца указанный запрос будет выполняться каждый раз. В отношении check это означает, что заданный запрос а) будет выполняться при каждом insert или update, б) может привести к невосстановимому backup.

Обычно люди не задумываются, каким образом серверу удается восстановить данные из бэкапа для взаимосвязанных таблиц. Например, если оперативная таблица (накладные) восстанавливается раньше чем справочная таблица (товары). Фокус в том, что FK, который отслеживает целостность между этими двумя таблицами, активируется после восстановления всех данных. А в случае check constraints или computed by речь идет о структурах таблиц, которые сначала создаются, а потом заполняются.

Пример: у таблицы X есть столбец, для которого определен контроль

CHECK(STUFF IN (SELECT STUFF FROM STUFFHOLDER WHERE STUFF_ID=100))

такая конструкция может сделать невосстановимым бэкап дважды:

  1. если структура таблицы X будет восстанавливаться раньше таблицы STUFFHOLDER
  2. если данные таблицы X будут восстанавливаться раньше данных таблицы STUFFHOLDER

Кроме этого, весьма странно устанавливать зависимость метаданных таблицы от конкретных значений данных совсем другой таблицы. В такой ситуации лучше и намного безопаснее создать триггер с указанной проверкой (а кроме того заменить IN на EXISTS. Еще правильнее вообще так не делать, а сделать нормальный Foreing Key на STUFFHOLDER).

Еще одна ошибка, которая связана с check constraint, но не с данными, уже давно исправлена в Firebird и Yaffil. Это использование русских букв в default для строковых столбцов. При restore такая БД выдаст сообщение «arithmetic exception, numeric overflow, or string truncation» еще на этапе восстановления метаданных. При наличии оригинальной базы данных необходимо удалить такой default, и сделать backup повторно. При отсутствии БД следует попытаться использовать ключ gbak -n.

Процедуры и триггеры

Здесь ситуация с невосстановимым backup может произойти при модификации (alter) процедур, которые вызываются другими триггерами или процедурами. Если меняется только тело процедуры, то проблем не будет. А если меняется количество или типы параметров процедуры, то в этом случае при restore (да и при alter вызывающих процедур) будет выдаваться сообщение о том, что список параметров процедуры не соответствует используемому.

Проблема там же, где и всегда – при реконструировании метаданных объекты строятся в памяти один за другим, образуя целые «гирлянды» из взаимосвязанных объектов. При этом код этих объектов (текст процедур или триггеров) не перекомпилируется. Но список параметров процедур хранится отдельно, в rdb$procedure_parameters, поэтому несоответствие используемомого и хранимого наборов становится камнем преткновения для restore. Борьба с этой ситуацией возможна фактически только при наличии оригинальной БД. Сейчас можно удалить или изменить процедуру, которая вызывает другую с измененным количеством параметров, однако в предыдущих версиях InterBase и Firebird это не было возможным. Единственный способ исправить проблему приведен в документе. Однако, с триггерами, вызывающими такие процедуры, ситуация похуже, потому что сервер не даст возможности обновить blr триггера. Поэтому остаются только «хакерские» приемы, вроде создания «пустой» процедуры с нужным количеством и типами параметров, и изменению blr триггера при помощи hex-editor.

В общем, к подобным изменениям нужно относиться более тщательно, используя инструменты, которые показывают зависимости между объектами (IBExpert), и комментируя вызовы процедур перед изменением количества или типов их параметров.

Повреждения системных таблиц

Разумеется, при повреждении системных таблиц информация о некоторых объектах будет потеряна, и backup не сможет сохранить данные этих объектов. Однако, если сами данные в системных таблицах целы, а индексы системных таблиц повреждены, backup может неверно сохранить информацию об объектах.

Происходит это потому, что backup выполняет запросы к системным таблицам, а эти запросы могут использовать индексы. Если индекс поврежден, то запрос может вернуть неверную информацию (меньше записей, чем их есть на самом деле). Таким образом или часть объектов просто не попадет в backup, или при restore будут серьезные проблемы из-за несоответствия информации в связанных между собой системных таблицах.

Поэтому, если вы в interbase.log наблюдаете информацию о поврежденных индексах на системных таблицах, то делать backup надо только после удаления таких индексов. Поврежденные индексы можно особо не выискивать, а удалить все целиком в rdb$indices, у которых rdb$relation_name начинается с префикса RDB$. Все равно индексы по системным таблицам относятся к системным таблицам, и создаются gbak при restore автоматически, независимо от содержимого backup.

Есть еще один случай повреждения базы данных, который относится к правам на объекты (таблицы, процедуры и т. п.). Проявляется это повреждение как ошибка в процессе restore с сообщением об отсутствии объекта, на который существуют назначенные права (в самом конце процесса restore). Как следует из кода restore.e, данная проверка является избыточной, т. к. если объекта нет, то и права на него восстанавливать не надо. Однако, код restore.e в таком случае выдает не предупреждение, а сообщение об ошибке, в результате чего процесс restore не доходит до завершения.

В конкретном случае были попытки определить несоответствие в rdb$user_privileges и остальных таблицах (rdb$relations и т. п.), однако таковых найдено не было. Ситуацию удалось решить компиляцией специального варианта restore.e (gbak.exe), в котором при отсутствии объекта, на который выдаются права, просто ничего не делается (не выдается никаких сообщений). Остается надеяться, что в новых версиях IB/FB/YA эта ситуация будет исправлена.

Пока что для обнаружения подобных проблем можно воспользоваться следующим способом – при правильном backup/restore всегда оставляют оригинальную БД (см. невосстанавливаемый backup). Для проверки существования в восстановленной базе данных всех объектов можно извлечь скрипты из обоих БД (например isql-ом), и сравнить их утилитой Database Comparer (info, download). Если отличий найдено не будет, значит структуры обоих БД идентичны. Если нет – найти поврежденный индекс и саму системную таблицу очень легко, зная в каких системных таблицах какие объекты хранятся (Language Reference.pdf).

Проблемы оборудования

Проблемы «железа» тоже могут сделать бэкап невосстанавливаемым. Я сам столкнулся со случаем, когда пользователь сообщил о том, что при попытке restore ошибки выдаются в разных местах. Было сделано предположение о дефективном HDD или RAM. Последнее предположение подтвердилось.В таких случаях хорошо, если проблемы оборудования проявили себя на этапе restore, а не backup. Если бы память (RAM) начала сбоить в момент backup, никто бы не узнал о проблемах вплоть до момента restore, и наверняка файл backup был бы поврежден так, что его нельзя было бы восстановить никаким образом. Причем никакими средствами вроде «контрольных сумм backup» предотвратить эту ситуацию невозможно, т. к. в файл данные пишутся именно операционной системой из памяти.

Поэтому позволю себе дать несколько банальных советов: не используйте модули RAM разных производителей в одном компьютере
старайтесь для сервера выбирать motherboard и память, поддерживающие ECC.

Дополнительные способы ремонта БД

Иногда возникают ситуации, которые не позволяют сделать backup даже после всех усилий применения gfix. Например, при бэкапе одной БД gbak выдал сообщение «Blob xxx corrupted». Если вы прочитаете статью по процессу gfix в конце этой статьи, то там написано, что такие ошибки чинятся gfix с ключом -mend. Однако это все равно не помогло, сообщение об ошибке не пропадало, и сделать backup было невозможно. Поэтому были проделаны следующие шаги:

  1. В инструменте, который не помещает записи в буфер (не кэширует. Можно написать простую программу с использованием компонента IBSQL из IBX. Главное, чтобы при больших объемах считываемые данные не попадали в кэш программы, иначе к концу считывания пары миллионов записей программа будет работать ужасно медленно) были просмотрены все записи через PgDn. При этом записи считываются с диска, но блобы не выбираются (можно написать select field1, field2, field… from table, исключая столбцы blob, если просмотр данных все-таки идет в гриде). Обнаружено, что таблица читается целиком. Значит, действительно поврежден блоб.
  2. Для нахождения поврежденного блоба сканирующая программка была дополнена операцией чтения блоба, простым присвоением переменной string (asString). Перед чтением блоба ключевое поле записи выдавалось на экран.
  3. В момент ошибки ключевое поле записи запомнили, после чего сделали

UPDATE TABLE
SET BLB=NULL
WHERE PK_FIELD = x

где вместо x подставили значение ключевого поля. Поскольку значение blob было испорчено, главным было обеспечить backup остальных записей и их blob.

Backup после этой операции прошел успешно.

То же самое можно проделывать в отношении «дефектных» записей (или их версий), при чтении которых выдается сообщение об ошибке. Основная трудность состоит в том, что физически на диске записи неотсортированы, и придется несколько раз выполнять запросы, выбирающие записи с разных «сторон» таблицы, чтобы найти примерно запись с какими данными (первичным ключом) не может быть прочитана. Пример кода, который производит копирование записей из «дефектной» таблицы в другую БД.

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

Внутри val.c есть кусочек документации, описывающий что и как делает gfix. Вот перевод этого куска. (Ни один другой файл исходного текста не содержит подобной «документации»).

Database Validation and Repair

Deej Bredenberg
March 16, 1994
Updated: 1996-Dec-11 David Schnepper

I. Терминология

Приводимые термины помогут понять этот документ:

  • record fragment (фрагмент записи): наименьший распознаваемый кусочек записи. Несколько фрагментов могут быть связаны и формировать отдельную версию записи.
  • record version (версия записи): Версия записи, созданная в результате выполнения оператора INSERT, UPDATE или DELETE в какой либо транзакции (обратите внимание, что при удалении создается новая версия записи, которая представляет собой так называемый «deleted stub»).
  • record chain (цепочка записей): Связанный список версий записи, который представляет собой целостную «запись».
  • slot (слот): Номер строки, где находится запись на странице. Массив переменной длины на каждой странице данных хранит смещения на записи, находящиеся на этой странице. Слот является индексом этого массива. За более подробной информацией по структуре страницы данных обращайтесь к моему документу по ядру Interbase (документ отсутствует).

II. Опции командной строки

Вот все ключи командной строки, которые используются при починке базы gfix-ом:

ключ константа dbp  
-validate isc_dpb_verify Выполняет проверку и починку БД. Все другие ключи модифицируют действие этого ключа.
-full isc_dpb_records Проверяет все записи. Без этого ключа проверяются только структуры страниц.
-mend isc_dpb_repair Пытается починить базу данных, чтобы она хотя бы могла быть читаема. Не гарантирует восстановления испорченных данных.
-no_update isc_dpb_no_update Указывает, чтобы осиротевшие страницы не освобождались, а занятые не помечались как используемые, если обнаружится что они на самом деле свободны. Не совсем корректное название ключа, потому что при его употреблении с -mend база будет чиниться, а если -mend не указан и указано -no_update, никаких изменений в БД сделано не будет.
-ignore isc_dpb_ignore Выключает подсчет контрольных сумм при выборке страниц. Проверка все равно будет сообщать контрольные суммы. Рекомендуется к использованию.
Замечание: ODS 8.0 не имеет контрольных сумм на страницах. ODS 9.0 вообще не имеет контрольных сумм.

III. Как это работает

Проверка БД работает только при эксклюзивном доступе к базе данных, чтобы в момент починки не было никаких посторонних изменений. При подключении к БД происходит попытка поставить эксклюзивную блокировку на файл базы данных. Если обнаружены другие коннекты к БД, выдается сообщение: «Lock timeout during wait transaction — Object «database_filename.gdb» is in use»

ЗАМЕЧАНИЕ: Обычно, когда процесс получает эксклюзивный доступ к БД, все активные транзакции помечаются как dead в Transaction Inventory Pages. Это поведение выключено при проверке БД.

IV. Фазы проверки

Существует две фазы проверки. В первой сканируется вся база данных. В этот момент номера всех страниц помещаются в битовую маску, для последующего использования в фазе сборки мусора

A. Просмотр страниц

В первой фазе любая выбираемая страница проходит проверки:
1. Контроль типа страницы
Каждая страница проверяется на ожидаемый тип страницы. Если в заголовке страницы указан неверный тип страницы (с точки зрения распределения страниц), то выдается сообщение: «Page xxx wrong type (expected xxx encountered xxx)» Это может сигнализировать о

  1. проблеме в базе данных
  2. ошибке в механизме распределения страниц IB, что привело к переписыванию одной страницы другой
  3. том, что страница, которая была распределена, не была записана на диск (чаще всего в этом случае тип страницы = 0). Это сообщение не говорит о том, как называется соответствующих тип страницы, поэтому здесь приведена выдержка из ods.h:

#define pag_undefined 0 /* не определено */
#define pag_header 1 /* страница заголовка базы данных */
#define pag_pages 2 /* Page Inventory Page — страница списка страниц */
#define pag_transactions 3 /* Transaction Iinventory Page — страница списка состояния транзакций*/
#define pag_pointer 4 /* страница указателей */
#define pag_data 5 /* страница данных */
#define pag_root 6 /* корневая страница индекса */
#define pag_index 7 /* страница индекса (B-tree) */
#define pag_blob 8 /* страница данных Blob */
#define pag_ids 9 /* страница значений генераторов */
#define pag_log 10 /* Write ahead log: только для 4.0 */

2. Контрольные суммы
Если указан ключ -ignore, то контрольная сумма считается при проверке, но не ядром. Если контрольная сумма не совпадает, то будет выдана ошибка:»Checksum error on page xxx» Это не влияет на проверку, и страница будет продолжать проверяться. Если указан ключ -mend, то страница в конце проверки будет записана на диск, и ее контрольная сумма будет автоматически пересчитана.
ЗАМЕЧАНИЕ: Только 4.0 для Windows и Netware обрабатывает контрольные суммы.
3. Повторный просмотр
Мы проверяем каждую выбранную страницу в битовой карте выбранных страниц, чтобы просматривать только новые страницы. Если номер повторяется, то будет выдано сообщение: «Page xxx doubly allocated» Это означает что на страницу одного и того же типа дважды ссылаются из разных мест. Страницы данных не проверяются этим механизмом, поскольку они проверяются намного чаще при просмотре фрагментов и цепочек записей.

B. Сборка мусора

В этой фазе страницы Page Inventory (PIP) проверяются на упоминание в битовой карте просмотренных страниц. Этим детектируются два типа ошибок:
1. Осиротевшие (Orphan) страницы
Если в PIP упомянута страница, которая еще не прошла проверку, то будет выдано сообщение «Page xxx is an orphan». Если не был указан ключ -no_update, то страница будет помечена в PIP как свободная.
2. Некорректно освобожденные страницы
Если страница помечена как свободная в PIP, но проверялась при первой фазе проверки, будет выдано сообщение:»Page xxx is use but marked free» (sic) (KDV: ни разу не видел такого сообщения. А вот page xxx is orphan – хоть отбавляй, особенно при крахе ОС в тот момент, когда производится заливка данных в БД). Если не был указан ключ -no_update, то такая страница будет помечена в PIP как используемая.
ЗАМЕЧАНИЕ: Если в фазе проверки были обнаружены ошибки, то PIP изменяться не будут. Потому что нет шансов проверить все страницы, если обнаружены поврежденные структуры.

V. Фаза просмотра

A. Выбор страниц

Для проверки того, что все страницы были считаны, следующие страницы проходят следующие проверки:
1. Заголовок базы данных (нулевая страница).
2. Page Inventory pages.
3. Transaction Inventory pages. Если невозможно просмотреть системную таблицу RDB$PAGES, или эта таблица не содержит ни одного номера страницы TIP, выдается сообщение: «Transaction inventory pages lost». Если какая то страница пропала из списка, указанного в RDB$PAGE_SEQUENCE, будет выдано следующее сообщение: «Transaction inventory page lost, sequence xxx». Если указан ключ -mend, то распределяется новая страница TIP, и корректно записывается в RDB$PAGES. Все транзакции, которые находились на потерянной таким образом странице, считаются завершенными по committ. Если страница TIP не указывает на следующую в последовательности страницу, то выдается сообщение: «Transaction inventory pages confused, sequence xxx» 5. Страницы генераторов, как указано в RDB$PAGES.

B. Проверка таблиц

Проверяются все таблицы в базе данных. Для каждой таблицы выбираются все индексы, и все страницы указателей и данных (см. дальше). Но вначале, сканируются метаданные в RDB$RELATIONS для определения формата таблицы. Если эта информация утеряна или повреждена, таблица не может быть поверена. Если любые ошибки бнаружены в момент проверки, выдается сообщение: «bugcheck during scan of table xxx ()» После этого любая дальнейшая проверка таблицы прекращается.
ЗАМЕЧАНИЕ: Для views производится только проверка метаданных.

C. Проверка индексов

До версии 4.5 (NevaStone) индексы проверялись перед страницами данных. В 4.5 проверка индексовы была перенесена после проверки страниц данных. См. дальше раздел «Проверка индексов». (KDV: версию 4.5 практически никто не видел. Выпущена она была для какой то одной операционной системы, и поставлялась только клиентам, использующим 4.1. В продажу не поступала)

D. Страницы указателей

Проверяются все страницы указателей для таблицы. При их проходе проверяются все дочерние страницы (см. дальше). Если страница указателей не может быть найдена, выдается сообщение: «Pointer page (sequence xxx) lost». Если страница указателей оказалась не той таблицы, которую мы проверяем, или она не помечена в правильной последовательности, выдается сообщение: «Pointer page xxx is inconsistent». Для каждой страницы указателей, которая не указывает на следующую страницу указателей в соответствии с столбцом RDB$PAGE_SEQUENCE в RDB$PAGES, выдается ошибка: «Pointer page (sequence xxx) inconsistent».

E. Страницы данных

Проверяется каждая страница данных, на которую указывает страница указателей. Если найдены повреждения на уровне страницы, и указан ключ -mend, то страница удаляется со страницы указателей. Это приводит к потере данных, которые располагались на этой странице данных. Если страница данных повреждена на уровне страницы, и не помечена как часть проверяемой таблицы, или не помечена в правильной последовательности, выдается сообщение: «Data page xxx (sequence xxx) is confused».

F. Проверка слотов

Проверяется каждый слот на странице, и на ней обновляется количество хранимых записей. Если слот не нулевой, то выбирается фрагмент записи по указанному смещению. Если запись начинается перед концом массива слотов, или продолжается за пределами страницы, выдается сообщение: «Data page xxx (sequence xxx), line xxx is bad», где «line» означает номер слота.
ЗАМЕЧАНИЕ: Если обнаруживается такое условие, страница данных считается поврежденной на уровне страницы, и в результате будет удалена со страницы указателей, если указан ключ -mend.

G. Проверка записей

Проверяется запись в каждом слоте, независимо от указания ключа -full. Фрагмент записи может быть опознан как
1. Обратная версия
Если фрагмент помечен как «обратная» версия, то он пропускается. Он будет выбран как часть записи.
2. Поврежденный
Если обнаруживается, что фрагмент поврежден любым образом, и указан ключ -mend, то в заголовке запись помечается как «поврежденная».
3. Помеченный как поврежденный
Если фрагмент уже помечен как поврежденный в результате предыдущей проверки, то выдается сообщение: «Record xxx is marked as damaged», где xxx является номером записи.
4. От неверной транзакции
Если запись имеет номер транзакции больше, чем номер самой последней транзакции в базе данных, выдается сообщение: «Record xxx has bad transaction xxx».

H. Проверка записей

Если указан ключ -full, и фрагмент является первым фрагментом логической записи, то запись в соответствующем номере слота выбирается целиком. Это приводит к выборке всех версий, и всех фрагментов для каждой версии. Другими словами, запись извлекается целиком.
1. Обратные версии
Если есть обратные версии, то они в этот момент проверяются. Если обратная версия находится на другой странице, то страница выбирается, но не проверяется пока она не будет проверена отдельно (проверкой страниц). Если номер слота обратной версии больше количества записей на странице, или нет записи в таком номере слота, или это blob, или это фрагмент записи, или фрагмент поврежден, выдается сообщение: «Chain for record xxx is broken».
2. Неполные
Если заголовок записи имеет пометку «неполный», это означает что для выборки записи нужно обратиться к другим фрагментам – запись оказалась слишком большой, чтобы поместиться в один слот. Для фрагментированных записей все фрагменты выбираются для всех версий записей. Если любой из фрагментов не обнаруживается в ожидаемом месте, или имеет неверную длину, выдается сообщение: «Fragmented record xxx is corrupt». Как только запись выбрана целиком, проверяется длина формата, в соответствии с ожидаемым в RDB$FORMATS (номер формата хранится в заголовке записи, представляя точную структуру таблицы на тот момент, когда запись была сохранена на диск). Если длина реконструированной записи не соответствует длине, получаемой из формата, выдается сообщение: «Record xxx is wrong length». Для версий записей, которые представляют собой результат действия оператора update, данная проверка не производится.

I. Проверка блобов

Если слот на странице данных указывает на запись blob, то blob выбирается (даже без -full). При этом возникает ряд вариантов, в соответствии с различными уровнями blob (см. документацию «Особенности ядра»). (KDV: документация с названием Engine Internals (Особенности ядра) не опубликована до сих пор). Действия в зависимости от уровня
—— ——————————————————————
0 Данные blob находятся тут же, на странице данных. Дополнительные проверки не производятся.
1 Все страницы, на которые указывает запись blob, выбираются и проверяются.
2 Все страницы, на которые указывают страницы указателей blob, выбираются и проверяются..
3 Страница blob является указателем на страницы blob. Все дочерние страницы выбираются и проверяются. Для каждой найденной страницы blob производятся проверки. Если страница не указывает обратно на страницу-источник, то выдается сообщение: «Warning: blob xxx appears inconsistent» где xxx это номер записи blob. Если любая из страниц blob не отмечена в последовательности, как это ожидается, выдается сообщение: «Blob xxx is corrupt» Tip: сообщение для такой же ошибки, но на уровнях 2 и 3, немного отличается: «Blob xxx corrupt». Если потеряна любая из страниц blob в последовательности, выдается сообщение: «Blob xxx is truncated». Если обнаруживается, что выбираемый blob поврежден одной из вышеуказанных причин, и указан ключ -mend, то запись blob помечается как поврежденная. (KDV: иногда маркировка как «damaged» не помогает. Т. е. сообщение blob xxx corrupt выдается при backup независимо от того, как и с какими ключами производилась починка БД gfix. См. пример ручного исправления ситуации в начале статьи).

J. Проверка индексов

В версии 4.5 (NevaStone) проверка индексов выполняется после завершения проверки страниц данных. Проверяются индексы для конкретной таблицы. Если корневая страница индексов не обнаружена, выдается сообщение: «Missing index root page» и индексы не проверяется. Иначе считывается корневая страница все индексы, упомянутые на этой странице, проверяются. Для каждого индекса, страницы btree выбираются сверху вниз, слева направо. Базовая проверка нелистовых страниц проверяет, указывает ли каждый узел на другую страницу индекса. Если указано -full, то проверяется целостность страниц нижнего уровня в том, что они указывают на родительские страницы. На листовых страницах указатели на страницы индекс не проверяются. Ключи проверяются на правильный порядок следования (по возрастанию или убыванию). Если проверяемая страница не относится к проверяемой таблице или индексу, выдается сообщение: «Index xxx is corrupt at page xxx». Если есть осиротевшие дочерние страницы, т. е. дочерние страницы, на которые не указывает родительская страница, то обновляется btr_sibling дочерней страницы и выводится сообщение об ошибке: «Index xxx has orphan child page at page xxx». Если страница не содержит число ключей, которые мы ожидаем по подсчитанной длине, то выдаем сообщение: «Index xxx is corrupt on page xxx». При проходе листовых страниц, мы сохраняем битмап всех номеров записей, видимых в индексе. По окончании прохода по индексу мы сравниваем полученный битмап с битмапом записей в таблице (полученном при проходе страниц данных и проверке записей). Если эти битовые маски не эквивалентны, значит мы имеем дело с поврежденным индексом, и выдаем следующее сообщение: «Index %d is corrupt (missing entries)». Мы НЕ проверяем каждую версию каждой записи на существование соответствующего ключа в индексе. Также мы не проверяем что конкретный ключ индекса соответствует определенной записи или версии.

K. Проверка таблиц

Мы считаем количество обратных версий, видимых при просмотре pointer pages, и отдельно считаем количество обратных версий при просмотре цепочек записей. Если эти числа не совпадают, значит или появились «осиротевшие» обратные версии, или двусвязанные списки. В этом случае выводится сообщение: «Relation has %ld orphan backversions (%ld in use)». В настоящее время мы не исправляем такую ситуацию, а только сообщаем о ней. Занятое «осиротевшими» обратными версиями пространство может быть возвращено путем backup/restore. Для двусторонних цепочек SWEEP должен удалять все обратные версии.

VI. Дополнительные замечания

A. Поврежденные записи

Если при проверке обнаруживается повреждение любого фрагмента записи, то заголовок такой записи помечается как «damaged» (поврежденная). Насколько я могу видеть, это не имеет влияния на ядро. Записи помеченные таким образом будут и дальше выбираться ядром. А вот будет ли такая запись выбираться при backup – это вопрос. Если идет обращение к поврежденной записи, то выводится следующее сообщение: «Record xxx is marked as damaged» Обратите внимание, что когда обнаруживается поврежденная запись, это сообщение не выводится. Запись просто помечается как поврежденная. И только после того, как будет повторное обращение к такой записи, будет выведено это сообщение. Поэтому я утверждаю, что пока не будет сделана полная проверка БД, вы можете не увидеть такого сообщения. А вот после полной проверки БД такое сообщение будет появляться даже тогда, когда gfix запускается без ключа -full.

B. Поврежденные Blob

Записи Blob, помеченные как поврежденные, не могут быть открыты и будут удалены с диска. Это означает, что даже при backup помеченные как поврежденные структуры Blob не будут выбраны и сохранены в backup (почему сделано для blob и записей по разному, я не знаю. Может быть это показалось слишком сложным – извлекать поврежденный blob).

Заключение

Третий закон Чизхолма: Любые предложения люди понимают иначе, чем тот кто их вносит.
Следствие 1: Даже если ваше объяснение настолько ясно, что исключает всякое ложное толкование, все равно найдется человек, который поймет вас неправильно.

Спасибо за комментарии и поправки к статье:

Олегу Иванову, Альберту Губайдуллину, Алексею Емельянову, Игорю Захребеткову, Александру Невскому, Дмитрию Еманову

Литература

  1. Interbase Operations Guide Diagnosing and Repairing InterBase Database Corruption, Paul Beach
  2. Невосстановимый Backup или GBAK и уверенность в завтрашнем дне, А. Невский.
  3. CVS/interbase/jrd/val.c

Дополнительно читать

  1. А. Н. Ковязин, С. М. Востриков, «Мир InterBase», изд. Кудиц-Образ, 2002 г. 

Ссылки

  • Платный ремонт баз данных
  • Утилита (платная) для ремонта определенных повреждений БД

Проверка и ремонт

Firebird предоставляет утилиты для проверки логических структур в базе данных и идентификации незначительных проблем, а также некоторый их ремонт. Множество таких ошибок может появляться время от времени, особенно в средах, где сети нестабильные или шумные или электроснабжение является нестабильным. Поведение пользователя, а также дефекты в проектировании приложения или базы данных также часто приводят к логическим разрушениям.

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

Инструменты проверки достоверности способны определить и устранить незначительные аномалии, явившиеся следствием ошибок операционной системы или оборудования. Такие ошибки обычно приводят к появлению проблем целостности базы данных по причине ошибок записи данных в страницы или потери страниц данных или индексов.

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

Когда проверять достоверность и зачем

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

* ошибки «corrupt database» или «consistency check»;

* резервное копирование, которое закончилось ненормально;

* отказ или изменение напряжения электропитания при отсутствии источника бесперебойного питания (UPS) или при предположении об отказе UPS;

* предполагаемые или сообщенные системой ошибки жесткого диска, сети или памяти;

* теневая копия заменяет собой базу данных после разрушения диска;

* база данных была перенесена с другой платформы или системы хранения;

* ожидаемое несанкционированное обращение к сети или базе данных со стороны внешних атак.

Подробности использования gfix для выполнения проверки базы данных см. в главе 39.

Что делать с разрушенной базой данных

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

В приложении 4 содержится подробное описание процедур починки разрушенной базы данных.

Как разрушить базу данных Firebird

В отличие от других СУБД, Firebird прекрасно справляется с травмирующими воздействиями на базу данных. Тем не менее практика показывает несколько проверенных техник, полезных в случае, если разрушение вашей базы данных является одной из ваших целей. Автор желает поделиться с читателем этими средствами искажения базы данных.

Модификация системных таблиц

Firebird хранит и ведет все свои метаданные и ваши определенные пользователем объекты в… базе данных Firebird! Более точно он хранит их в отношениях (таблицах) прямо в самой базе данных. Идентификаторы системных таблиц, их столбцов и некоторых других типов системных объектов начинаются с символов «RDB$».

Поскольку это обычные объекты базы данных, их можно запрашивать и манипулировать ими как определенными пользователем объектами. Однако то, что вы можете., не означает, что вы должны.

Нельзя настоятельно рекомендовать, чтобы вы использовали только операторы DDL — непрямые операции SQL над системными таблицами — всякий раз, когда вам нужно изменять или удалять метаданные. Отложите всякие «прямые изменения», пока ваши умения в SQL и ваши знания сервера Firebird не станут более полными. Потерпевшая аварию база данных не является ни предметом приятного созерцания, ни легкой в починке.

Отмена принудительной записи для Firebird 1.0.x в Windows

Firebird по умолчанию устанавливается с возможностью принудительной записи (forced writes, синхронной записью). Измененные и новые данные записываются на диск немедленно после завершения операции (post).

Возможно конфигурирование базы данных для использования асинхронной записи данных, когда измененные или новые данные сохраняются в памяти кэша и периодически сбрасываются на диск подсистемой ввода/вывода операционной системы. Общий термин для такой конфигурации — отмена принудительной записи. Иногда это значение восстанавливается для улучшения производительности больших пакетных операций.

Сервер платформ Win32 не сохраняет на диск кэш сервера Firebird 1.0.x, пока не будет закрыт сервис Firebird. Не говоря уже о сбое в питании, может много чего плохо- го произойти с сервером Windows. Если он зависнет, система ввода/вывода прекратит работу, и работа вашего пользователя будет потеряна в процессе перезагрузки.

Серьезное предупреждение: не отключайте принудительную запись на сервере Windows, если вы не используете Firebird 1.5 и выше.

Firebird 1.5 по умолчанию сбрасывает кэш на диск каждые 5 секунд или каждые 100 операций записи- что произойдет быстрее. Эта частота может быть изменена В firebird.conf корректировкой одного или двух параметров MaxUnflushedWrites и MaxUnflushedWriteTime.

Windows 95 не поддерживает асинхронную запись на диск.

Серверы Linux более надежны при выполнении операций в случае временного отключения принудительной записи. Не оставляйте этот режим отключенным после завершения задачи, выполнявшей пакетные обновления, если у вас нет очень надежной системы питания.

Восстановление резервной копии в работающую базу данных

Один из режимов восстановления в утилите gbak (gbak -r[estore]) позволяет восстанавливать файл резервной копии поверх существующей базы данных — база данных перезаписывается. В этом режиме возможно восстановление без предупреждения о том, что пользователи соединены с базой данных. Разрушение базы данных является практически гарантированным результатом.

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

Чтобы сделать это практически, рекомендуется восстанавливать базу на резервное дисковое пространство, используя gbak -с[reate]. Прежде чем сделать восстановленную базу данных активной, проверьте ее в резервной области, используя isql или ваш предпочитаемый инструмент администратора.

Разрешение пользователям соединяться с базой данных в процессе ее восстановления

Если вашей организации нравится жить на острие ножа, используйте переключатель -restore и позвольте пользователям соединяться с базой данных и выполнять изменения. Процесс восстановления создает базу данных с нуля, и как только будут созданы таблицы, ваши пользователи смогут (по крайней мере, потенциально, или если они все SYSDBA) обращаться к ним с операциями DML, в то время как ссылочная целостность и другие ограничения находятся еще только на подходе. В лучшем случае они получат исключения и кучу неподтвержденных транзакций в частично сконструированной базе данных. В худшем, они полностью уничтожат целостность данных.

Копирование файлов базы данных, в то время как с ней соединены пользователи

Используйте любую утилиту копирования или архивирования файловой системы (DOS copy, xcopy, tar, gzip, WinZip, WinRAR и т.д.) для копирования файлов базы данных, в то время как с ней соединены любые пользователи (включая SYSDBA). Копия будет поврежденной, но еще хуже, система блокировки и/или кэширования этих программ может привести к потере данных и, возможно, к разрушению исходного файла.

Проверка и ремонт

Firebird предоставляет утилиты для проверки логических структур в базе данных и идентификации незначительных проблем, а также некоторый их ремонт. Множество таких ошибок может появляться время от времени, особенно в средах, где сети нестабильные или шумные или электроснабжение является нестабильным. Поведение пользователя, а также дефекты в проектировании приложения или базы данных также часто приводят к логическим разрушениям.

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

Инструменты проверки достоверности способны определить и устранить незначительные аномалии, явившиеся следствием ошибок операционной системы или оборудования. Такие ошибки обычно приводят к появлению проблем целостности базы данных по причине ошибок записи данных в страницы или потери страниц данных или индексов.

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

Когда проверять достоверность и зачем

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

* ошибки «corrupt database» или «consistency check»;

* резервное копирование, которое закончилось ненормально;

* отказ или изменение напряжения электропитания при отсутствии источника бесперебойного питания (UPS) или при предположении об отказе UPS;

* предполагаемые или сообщенные системой ошибки жесткого диска, сети или памяти;

* теневая копия заменяет собой базу данных после разрушения диска;

* база данных была перенесена с другой платформы или системы хранения;

* ожидаемое несанкционированное обращение к сети или базе данных со стороны внешних атак.

Подробности использования gfix для выполнения проверки базы данных см. в главе 39.

Что делать с разрушенной базой данных

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

В приложении 4 содержится подробное описание процедур починки разрушенной базы данных.

Как разрушить базу данных Firebird

В отличие от других СУБД, Firebird прекрасно справляется с травмирующими воздействиями на базу данных. Тем не менее практика показывает несколько проверенных техник, полезных в случае, если разрушение вашей базы данных является одной из ваших целей. Автор желает поделиться с читателем этими средствами искажения базы данных.

Модификация системных таблиц

Firebird хранит и ведет все свои метаданные и ваши определенные пользователем объекты в… базе данных Firebird! Более точно он хранит их в отношениях (таблицах) прямо в самой базе данных. Идентификаторы системных таблиц, их столбцов и некоторых других типов системных объектов начинаются с символов «RDB$».

Поскольку это обычные объекты базы данных, их можно запрашивать и манипулировать ими как определенными пользователем объектами. Однако то, что вы можете., не означает, что вы должны.

Нельзя настоятельно рекомендовать, чтобы вы использовали только операторы DDL — непрямые операции SQL над системными таблицами — всякий раз, когда вам нужно изменять или удалять метаданные. Отложите всякие «прямые изменения», пока ваши умения в SQL и ваши знания сервера Firebird не станут более полными. Потерпевшая аварию база данных не является ни предметом приятного созерцания, ни легкой в починке.

Отмена принудительной записи для Firebird 1.0.x в Windows

Firebird по умолчанию устанавливается с возможностью принудительной записи (forced writes, синхронной записью). Измененные и новые данные записываются на диск немедленно после завершения операции (post).

Возможно конфигурирование базы данных для использования асинхронной записи данных, когда измененные или новые данные сохраняются в памяти кэша и периодически сбрасываются на диск подсистемой ввода/вывода операционной системы. Общий термин для такой конфигурации — отмена принудительной записи. Иногда это значение восстанавливается для улучшения производительности больших пакетных операций.

Сервер платформ Win32 не сохраняет на диск кэш сервера Firebird 1.0.x, пока не будет закрыт сервис Firebird. Не говоря уже о сбое в питании, может много чего плохо- го произойти с сервером Windows. Если он зависнет, система ввода/вывода прекратит работу, и работа вашего пользователя будет потеряна в процессе перезагрузки.

Серьезное предупреждение: не отключайте принудительную запись на сервере Windows, если вы не используете Firebird 1.5 и выше.

Firebird 1.5 по умолчанию сбрасывает кэш на диск каждые 5 секунд или каждые 100 операций записи- что произойдет быстрее. Эта частота может быть изменена В firebird.conf корректировкой одного или двух параметров MaxUnflushedWrites и MaxUnflushedWriteTime.

Windows 95 не поддерживает асинхронную запись на диск.

Серверы Linux более надежны при выполнении операций в случае временного отключения принудительной записи. Не оставляйте этот режим отключенным после завершения задачи, выполнявшей пакетные обновления, если у вас нет очень надежной системы питания.

Восстановление резервной копии в работающую базу данных

Один из режимов восстановления в утилите gbak (gbak -r[estore]) позволяет восстанавливать файл резервной копии поверх существующей базы данных — база данных перезаписывается. В этом режиме возможно восстановление без предупреждения о том, что пользователи соединены с базой данных. Разрушение базы данных является практически гарантированным результатом.

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

Чтобы сделать это практически, рекомендуется восстанавливать базу на резервное дисковое пространство, используя gbak -с[reate]. Прежде чем сделать восстановленную базу данных активной, проверьте ее в резервной области, используя isql или ваш предпочитаемый инструмент администратора.

Разрешение пользователям соединяться с базой данных в процессе ее восстановления

Если вашей организации нравится жить на острие ножа, используйте переключатель -restore и позвольте пользователям соединяться с базой данных и выполнять изменения. Процесс восстановления создает базу данных с нуля, и как только будут созданы таблицы, ваши пользователи смогут (по крайней мере, потенциально, или если они все SYSDBA) обращаться к ним с операциями DML, в то время как ссылочная целостность и другие ограничения находятся еще только на подходе. В лучшем случае они получат исключения и кучу неподтвержденных транзакций в частично сконструированной базе данных. В худшем, они полностью уничтожат целостность данных.

Копирование файлов базы данных, в то время как с ней соединены пользователи

Используйте любую утилиту копирования или архивирования файловой системы (DOS copy, xcopy, tar, gzip, WinZip, WinRAR и т.д.) для копирования файлов базы данных, в то время как с ней соединены любые пользователи (включая SYSDBA). Копия будет поврежденной, но еще хуже, система блокировки и/или кэширования этих программ может привести к потере данных и, возможно, к разрушению исходного файла.

KDV, 03.09.2002, исправления и дополнения – 25.01.2003, 06.02.2003, 15.04.2003, 16.05.2003, 02.09.2003, 26.11.2003, 03.02.2004, 06.09.2004, 17.02.2005, 05.07.2007 11.10.2007, 2015.10.11.

Если Вам нужно действительно починить БД Firebird

Самый быстрый и надежный способ починить серьёзно поврежденную БД относительно небольшого размера (до 65Гб) – это использовать инструмент IBSurgeon FirstAID, который может починить практически все повреждения БД и спасти оставшиеся данные.

Содержание

  • Введение
  • Повреждения базы данных
    • Отключение питания сервера
    • Дефекты оборудования
      • Память
      • Диски
      • Контроллеры
      • Другие программы
    • Сбои самого сервера
    • Остановка во время сборки мусора
    • Повреждения индексов
    • Повреждения таблиц
  • Ремонт БД
  • Невосстанавливаемый backup
    • Проблемы с индексами
      • Дубликаты в уникальных индексах
      • Отсутствующие записи по Foreign key
    • Добавленный столбец NOT NULL
    • Изменение типа столбца
    • Check constraint, не соответствующий хранимым данным
    • Процедуры и триггеры
    • Повреждения системных таблиц
    • Проблемы оборудования
  • Дополнительные способы ремонта БД
  • Перевод документа Database Validation and Repair
  • Заключение

Первый закон Чизхолма: Все, что может испортиться, портится.
Следствие: Все, что не может испортиться, портится тоже.

Введение

На эту тему можно было бы процитировать половину законов Мэрфи. Вместо этого я процитирую начало статьи в журнале Intelligent Enterprise

Установлено, что большинство предприятий, переживших крупную необратимую потерю корпоративных данных, прекращают свое существование в течение трех лет после такого инцидента. Мысль о возможной катастрофе неприятна для ИТ- и бизнес-менеджеров, поэтому они часто не принимают серьезных предупредительных мер.

Когда я читал эту статью, меня больше всего потрясло именно второе предложение в этом абзаце. К сожалению, это грубая правда жизни. И здесь под «предупредительными мерами» ни в коем случае нельзя понимать «покупку техники brand-name». Как показала дискуссия в конференции epsylon.public.interbase, «брэнды» тоже ломаются, иногда даже чаще чем «самосборные машины». Поэтому «предупредительные меры»  это не только качественное «железо», но и планирование резервного копирования данных.

Планирование-планированием, но вот еще одна цитата, уже из письма:

«… днем сдох IBM-ер у моих «ну очень уважаемых клиентов», а вместе с ним и БД акционерок и акционеров, с общим объемом уставных фондов около полуярда гривень (около 100 млн вечно-зеленых гульденов). Резервация ее делалась на RW-диск, отформатированный под UDF. Но кто-то (никто не признался) вставил вместо него неотформатированную RW. И так она пролежала там на протяжении последнего месяца. Как раз в октябре/ноябре у них шел сплошной поток обновлений в БД….»

О технике (железе) здесь будет сказано дальше, но немного. Основной же целью этой статьи является перечисление типов повреждений базы данных, способы ремонта, а также предотвращения порчи баз данных. Обычно при сбое базы данных ее можно починить утилитой gfix. Однако, иногда этот инструмент не помогает. В таких случаях нужно обратиться в конференцию, где попытаются помочь. В случае больших баз данных нужно быть готовым при починке полагаться только на себя, поскольку вряд ли получится отправить ~>1Гб базу (имеется в виду размер архива zip или rar, конечно) данных специалистам по email или на ftp. И нужно знать, что бывают случаи, когда базу данных восстановить не удастся никаким образом. Поэтому никогда не забывайте своевременно делать backup, а также проводить «контрольный» restore! (Разумеется, никогда нельзя делать restore на место оригинальной базы данных, т. е. с ключом -r. Вы рискуете остаться без базы данных).

Для ознакомления – перечень наиболее частых ситуаций, для которых компания iBase осуществляет платный ремонт БД. (Это не означает, что можно починить все – недавно базу со сбоем N2 починить не смогли, т. к. оказались затертыми не только половина данных, но и информация в системных таблицах о структуре записей). Если gfix не чинит базу данных, то в 70% случаев простых повреждений может помочь утилита IBSurgeon FirstAid. Для оценки повреждений БД имеет смысл сначала проверить БД с помощью бесплатного режима (с возможностью предпросмотра данных) FirstAID и прислать лог (в zip) на support@ibase.ru, т. к. может оказаться, что ваш случай попадает в худшие 30%. В любом случае не стоит надеяться на появление инструмента, который из любой самой убитой базы данных сможет вытащить ваши данные.

Если база убита, а есть только backup, и он тоже поврежден, или по иным причинам нужно извлечь из backup только часть данных – к вашим услугам инструмент IBBackupSurgeon. (FirstAid и IBBackupSurgeon – платные. Их можно купить).

Примечание. Чаще всего обращения о ремонте БД содержат не абстрактную просьбу «починить БД», а желание вытащить из поврежденной БД какие-то жизненно важные данные. Как раз этот случай зачастую требует именно ручного ремонта, поскольку автоматизируемый ремонт как gfix так и FirstAID старается отремонтировать БД для 100% возможности ее бэкапа, но не факт, что сделано это будет с максимальной сохранностью данных (см. Ремонт БД, примечание к пункту 5).

Примечание. Исследовать структуру БД самостоятельно можно при помощи утилиты IBSurgeon Viewer.

Повреждения базы данных

Существует несколько причин, при которых база данных может оказаться поврежденной. Здесь перечислены наиболее характерные.

Отключение питания сервера

Самый частый случай повреждения базы данных это отключение питания на сервере. Такие ситуации нужно пытаться предотвращать, используя аппаратные средства (UPS, RAID-контроллеры с батарейками). InterBase имеет два режима записи страниц – синхронный и асинхронный. Для всех версий до 6.0 создаваемые базы данных имели по умолчанию синхронный режим (Firebird v1.0 также имеет этот режим включенным по умолчанию). Для изменения режима можно воспользоваться утилитой gfix.

Включение синхронного режима:

gfix -write sync database.gdb

Включение асинхронного режима:

gfix -write async database.gdb

Синхронная запись означает, что измененные страницы базы данных не будут кэшироваться операционной системой, а будут записываться непосредственно на диск при выталкивании страниц из кэша на запись (на Windows это в буквальном смысле отсутствие флага lazy write при открытии файла БД). Это ухудшает производительность, поэтому большинство людей выключают forced writes. В этом случае измененные страницы находятся в кэше операционной системы до тех пор, пока операционная система не решит записать их на диск. В некоторых случаях при непрерывной работе с БД операционная система не сбрасывала измененные страницы на диск до тех пор, пока все пользователи не отсоединялись от базы данных. Понятно, что при выключении питания в этом случае повреждения базы данных могут быть максимальными. Причем, повреждения в данном случае происходят от «недозаписи» информации. Это куда менее печальный случай, чем «перезапись» информации случайными данными, о чем пойдет речь в следующем разделе. Однако, на Windows было обнаружено, что если у базы данных установлено Forced Write = Off, то при определенных условиях измененные страницы БД могли неделями не попадать в БД, и оставаться в кэше операционной системы. При этом, в случае сбоя сервера (или отключения питания), пропадало огромное количество изменений в БД (а база могла выглядеть вообще неповрежденной). То есть, БД как бы оказывалась «неизменяемой» в течение длительного времени. Для исключения данной проблемы в Firebird 1.5 были введены дополнительные параметры для принудительного сброса кэша операционной системы при ForcedWrite=Off (см. параметры MaxUnflushedWrites и MaxUnflushedWriteTime в firebird.conf).

Примечание. На современных дисковых системах включение Forced Write практически не влияет на производительность. Это заметно разве что на desktop-системах с IDE-дисками (старыми). Хотя вообще кэширование записи операционной системой дает выигрыш в производительности при массовых обновлениях данных.

Дефекты оборудования

Память

Самый частый дефект за последние полтора года – сбои памяти (RAM) (случаи такого рода сильно сократились в 2005 году). Очевидно, при использовании серверов «своей сборки» приобретается память подешевле, что приводит к соответствующим результатам. Желательно для сервера приобретать и материнскую плату и память с поддержкой ECC.Сбои памяти могут привести к достаточно тяжелым последствиям, которые описаны дальше в этом разделе, в главе «невосстанавливаемый backup». Сервер не только «пропускает» страницы базы данных через память, но и кэширует их в памяти. Поскольку контрольные суммы страниц были отключены еще в IB 5.0, повреждения обнаруживаются только когда происходит чтение данных с диска. Собственно, контрольные суммы страниц, даже если бы они и были, не помогут когда сервер будет записывать страницу на диск через сбойный участок памяти. В противном случае данные пришлось бы перечитывать, что весьма серьезно ухудшило бы производительность.Сбои памяти еще плохи тем, что в этом случае поврежденными как правило оказываются и база данных и ее shadow, если shadow используется в качестве «быстрой резервной копии» (т. к. запись на диск идет из одних и тех же участков памяти).

Диски

Упоминавшиеся выше контрольные суммы страниц данных были исключены в сервере IB 5.0 не зря. Дело в том, что эти контрольные суммы фактически дублировали контроль данных, который должен производиться дисковым накопителем. Раньше, лет 10-15 назад, bad-блоки появлялись часто, и существовали специальные утилиты для их исправления. Сейчас контроль ошибок не только может исправить данные на поврежденном блоке самостоятельно, но и прозрачно сохранит блок в рабочем месте диска, а плохой блок пометит к исключению из дальнейшего использования. Грубо говоря, нынешние диски либо работают, либо не работают целиком.

Контроллеры

Здесь можно отметить только то, что при сбое в работе контроллера некорректная информация может быть записана на все носители, которые подключены к этому контроллеру. Именно поэтому при организации shadow рекомендуется располагать ее на винчестере, подключенном к другому контроллеру.

Другие программы

InterBase, Firebird или Yaffil не работают с операционной системой на «внутреннем» уровне. Это обычное приложение, которое никогда не может вызвать сбой типа известного «синего экрана» в Windows NT. Поэтому если подобный сбой ОС произошел, в этом скорее виноваты некорректно работающие драйверы, другие программы или само оборудование (очень часто в «синем экране» виноваты драйвера видео).

Примечание. В W2K по умолчанию при сбое ОС происходит рестарт системы. Если хотите видеть, что система зависла, в My Computer/Properties, Advanced, Startup and Recovery уберите чекбокс Automatically reboot.

В предыдущем разделе был приведен пример повреждения БД, когда не вся новая информация записана в БД. При сбоях ОС может случиться обратная ситуация – данные могут продолжать записываться в файл БД при «зависании» ОС, однако это могут быть уже совсем не те данные, которые собирался записать Interbase в файл базы данных. В этом случае повреждения оказываются наиболее серьезными, которые редко могут быть исправлены при помощи gfix.

Сбои самого сервера

Разумеется, InterBase (Firebird, Yaffil) как и любое другое ПО не является идеальной программой. Идеальной, конечно, в смысле отсутствия ошибок. Если «железо» работает нормально, сервер может сам «сломаться» и либо просто перестать работать, либо испортить базу данных. Конкретный случай последних 1.5-2-х лет – превышение размера в 4 гигабайта файлом базы данных при использовании старых версий InterBase (или ранних версий Firebird на определенных платформах). Раньше, и в том числе в 5.x (1997-2000 годы), код сервера содержал вызов обычной функции позиционирования по файлу БД (seek), которая не могла адресовать более 4-гигабайт (в те далекие времена просто не было файловых систем, которые поддерживали файлы больше 4-х гигабайт). Когда в функцию передавалось такое большое число, оно обрезалось по старшим разрядам. Происходила такая ситуация при операции расширения файла БД, т. е. при записи новых страниц, а следовательно файл БД оказывался «затертым» новой информации с самого начала, т. е. с нулевой страницы (страница заголовка БД). Если новых страниц к записи было много, то уничтожалась начальная часть БД, где как правило содержатся системные таблицы, страницы информации о транзакциях и т. п. Причем борьба с пресловутым размером файла в 4 гигабайта дольше всего велась на Linux, что связано не только с кодом СУБД, но и с поддержкой файлов таких размеров самой операционной системой и ее файловыми системами. Firebird исправил эту проблему окончательно только в версии 1.0.2, причем 1.0.2 выпускался как в старом варианте, так и с 64bit-IO. Borland также не миновала чаша сия, и для IB 7 выпущен патч (7.0.1), который исправлял проблему с размером файла для Linux. Firebird для FreeBSD поддерживает файлы более 4 гигабайт начиная с версии 1.5.

На Windows в IB7, Firebird и Yaffil этой проблемы уже нет, т. е. файл БД может иметь размер и 10, и 20 и больше гигабайт.

В любом случае, при работе на Unix или Windows следует внимательно изучить возможности ядра и конкретной (используемой) файловой системы – способны ли они работать с файлами больше 4-х гигабайт, а также проверить версию IB/FB/YA, чтобы быть уверенным в корректной работе с такими файлами, или наоборот, сразу предусмотреть разбиение БД на многофайловую, например, «кусками» по 2-3 гигабайта.

В отношении файловых систем Windows известна следующая особенность: на FAT32 поддерживаются файлы размером не более 4 гигабайт (чаще всего указанное повреждение БД и происходит при использовании этой, фактически уже устаревшей, файловой системы). Допустим, размер вашей БД достиг 3-х гигабайт, и вы хотите перенести ее на NTFS, чтобы избежать ограничения в 4 Гб. Проблема в том, что с FAT32 на NTFS скопируется только 2 гигабайта из 3-х, из-за особенностей Windows. Это еще раз подчеркивает необходимость знания ограничений используемых файловых систем и их взаимодействия на одном компьютере. На текущий момент все последние версии InterBase, Firebird и Yaffil не имеют ограничений по размеру файла БД, для любых операционных систем. В остальных случаях, одной из характерных ошибок, которую наблюдают разработчики, является «cannot continue after bugcheck(xxx)» с любым номером ошибки. Это означает, что сервер во время работы перешел в такое состояние, что дальнейшая работа с базой данных может ее повредить. При этом рекомендуется рестартовать сервер, после чего желательно проверить базу данных утилитой GFIX.

Замечание. Сообщение bugcheck не имеет ничего общего с ошибками (багами, bugs), обнаруживаемыми и регулярно исправляемыми в коде сервера.

Остановка во время сборки мусора

Когда исходный код Interbase был опубликован, оказалась выявлена еще одна неприятная особенность, которая может привести к серьезным повреждениям базы данных. Если во время принудительного завершения работы сервера (gfix -shut …) были активные подключения и сервер занимался сборкой мусора (работал sweep thread), то база данных может быть повреждена (и чаще всего это так и происходит). Уменьшить вероятность таких повреждений можно только отключив автоматическую сборку мусора (gfix bd.gdb -housekeeping 0), а в случае принудительной сборки мусора (gfix -sweep) предварительно делать «быстрый» backup (gbak с ключом -g, то есть без сборки мусора), чтобы резервная копия базы данных оказалась самой свежей в случае сбоя и повреждения БД. В Yaffil эта проблема исправлена.

Повреждения индексов

Повреждения индексов могут происходить как по всем вышеперечисленным причинам, так и из-за ряда багов сервера при работе с индексами (исправлены в Firebird 1.5, Yaffil). Поскольку индексы не являются 100% необходимым видом объектов для функционирования базы данных, их повреждения обнаруживаются значительно позже (если администратор смотрит в interbase.log), чем повреждения других объектов БД (например, данных таблиц). И клиентские приложения могут продолжать функционировать в такой ситуации как и прежде.

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

SELECT * FROM TABLE

и с перебором по индексу

SELECT * FROM TABLE

WHERE FIELD > 0

где FIELD  столбец, по которому есть возможно поврежденный индекс, а > 0 – условие, которое однозначно будет выбирать все записи.
(разумеется, лучше этого не делать, а при подозрении на «пропадание записей» сразу посмотреть в interbase.log, перестроить те индексы, о повреждениях которых там сообщается.

В interbase.log пишутся только порядковые номера индексов (а не их имена) для конкретных таблиц, как это и указано в rdb$indices). Процесс backup поврежденные или неповрежденные индексы (за исключением повреждений индексов по системным таблицам) не интересуют, т. к. индексы в backup хранятся только в виде описания в системных таблицах (restore создает индексы по этим описаниям в самом конце процесса restore). Backup считывает записи в натуральном порядке и индексы не использует, поэтому все существующие (committed) записи обязательно попадут в backup. Однако, если поврежден уникальный индекс, то в определенных условиях существует вероятность повторной вставки записи в таблицу с уже существующим (уникальным) значением столбца. Эта ситуация может привести к невосстановимому backup, т. е. процесс restore остановится в момент создания уникального индекса, обнаружив дубликат уникального значения в восстановленных записях. Но такая проблема также не является катастрофической  процесс создания индексов выполняется самым последним, т. е. после того как абсолютно все объекты БД уже восстановлены в базе данных процессом restore. Если вдруг обнаружена проблема неуникальных данных при создании индекса, можно попробовать найти такую запись (и затем удалить лишнюю) запросом

SELECT ID, COUNT(*) FROM TABLE
GROUP BY ID
HAVING (COUNT(*)) > 1

где id столбец, по которому есть несоздаваемый уникальный индекс. После этого можно активировать индексы, которые не были восстановлены (установив RDB$INDICES.RDB$INACTIVE в 0, там, где этот столбец не 0).

Повреждения таблиц

Нормальная база данных  это не набор отдельных таблиц. Таблицы между собой могут быть достаточно сильно взаимосвязаны, вплоть до циклических ссылок. Поэтому даже один и тот же тип и объем повреждения может иметь разные последствия, в зависимости от того, с какой таблицей это произошло. Типичный пример: таблица CLIENTS  справочная, а ORDERS  оперативная. Если пропадет часть записей из ORDERS, то после починки БД будет нормально функционировать. Если же будет повреждена CLIENTS, то после починки в ORDERS будут записи, ссылающиеся на несуществующих клиентов. Таким образом БД вроде бы будет отремонтирована, но логическая целостность данных будет нарушена. Бороться с этой ситуацией никак невозможно, разве что чаще делая backup (поскольку справочники меняются реже, чем оперативные данные). Подобная ситуация (с повреждением master-таблицы) может возникнуть даже в том случае, если все записи пакета master-detail вставляются в одной транзакции, а Forced Write выключен (off)  страницы с записями detail могут быть записаны на диск операционной системой из кэша раньше, чем соответствующие им записи таблицы master. Это не приводит к «невосстановимому backup», но после restore придется или добавлять недостающие master-записи, или удалять «осиротевшие» detail-записи (в зависимости от сложности триггеров, обрабатывающих вставку в master или удаление в detail. Возможно, такие триггеры на время ремонта данных нужно будет отключить).

Также, в подобной ситуации, при restore отремонтированной базы данных могут оказаться неактивными (rdb$indices.rdb$index_inactive = 1) индексы по Foreign Key соответствующих связей master-detail. Активировать их можно после упомянутых вставок или удалений master/detail, путем установки rdb$indices.rdb$index_inactive в 0. О повреждениях системных таблиц см. дальше.

Ремонт БД

Для ремонта баз данных рекомендуется использовать именно утилиту командной строки gfix, несмотря на то, что раньше такие операции можно было делать из Server Manager, а сейчас из IBConsole или через Services API.

Внимание! Даже если у вас только что рухнула база, и вы хотите починить ее максимально быстро – все равно ВНИМАТЕЛЬНО прочитайте указанные ниже пункты от 1 до 8.

Повреждения баз данных могут быть исправлены как при помощи только gfix, так и одновременно gfix и gbak. Открываем консольное окно (cmd на W2K, или command на Win9x, или терминальное на Linux)

Лично я предпочитаю при операциях backup/restore всегда сохранять вывод в файл ключами -v -y outfile.txt. При обычном выводе на консоль «вывод» может потеряться, и тогда придется процедуру backup/restore повторять. Кроме того, в файле лога можно легко найти список объектов, которые успешно восстановлены.

1. Установите системные переменные, чтобы облегчить себе жизнь и не вводить постоянно для gfix/gbak параметры -user SYSDBA -pass masterkey

SET ISC_USER=SYSDBA
SET ISC_PASSWORD=masterkey

 

Внимание! Не оставляйте эти переменные после починки БД. В этом случае клиент с любой машины может подсоединиться к БД без указания имени пользователя и пароля (например, isql). Если не хотите устанавливать эти переменные, то по тексту дальше дописывайте
gbak …. -user SYSDBA -pass masterke
gfix …. -user SYSDBA -pass masterke

2. При починке работайте с копией базы данных, а не с оригиналом. У вас должен быть эксклюзивный доступ к БД

copy employee.gdb database.gdb
 

Внимание! Копировать БД таким способом можно только если вы абсолютно уверены, что к БД никто не подсоединен. База данных – файл произвольного доступа, а файловое копирование производится поблочно-последовательно. Поэтому если с БД кто-то работает, вы 100% получите «на выходе» испорченный файл БД вместо копии. Существуют редкие сообщения и о том, что при копировании с подключениями может испортиться и оригинальный файл БД, правда природа этого случая неясна.

3. Иногда помогает перед началом починки перевести базу данных в режим shutdown

gfix database.gdb -shut -force 0
 

Внимание! Не забудьте потом вернуть ее в online командой gfix database.gdb -online

4. Проверьте базу данных на повреждения

gfix при проверке БД на наличие повреждений выводит информацию о повреждениях в interbase.log или firebird.log (подробно) и на экран (суммарно). Поэтому чтобы посмотреть на подробное описание повреждений, перед запуском команды ремонта БД следует переименовать interbase/firebird.log (например, firebird1.log, firebird20071010.log и т. д.), чтобы уже хранимая там информация не мешала дополнительному выводу gfix. Когда сервер не обнаружит лог-файл, он его создаст заново.

gfix -v -full database.gdb

Если выдаются ошибки checksum error, то нужно выполнить следующую команду:

gfix -v -ignore database.gdb

5. Если предыдущая команда обнаружила ошибки, то нужно их исправить командой

gfix -mend database.gdb
если выдаются ошибки checksum error, то нужно добавить опцию -ig
gfix -mend -ig database.gdb
 

Внимание! Ключ -mend помечает поврежденные структуры как исключаемые при backup. В результате целостность между таблицами может быть нарушена, и даже если после этого backup пройдет, не факт что базу данных удастся восстановить из backup. В этом случае придется вручную копировать данные при помощи утилит вроде IBPump.

6. Проверим, все ли починилось

gfix -v -full database.gdb

7. Если на этот момент вы все еще видите ошибки, то надо попытаться сделать backup, при этом обязательно нужно отключать сборку мусора (ключ -g)

gbak -b -v -ig -g database.gdb database.gbk

ключ -ig игнорирует ошибки при чтении структур данных, и пытается сохранить в backup все неповрежденные структуры и данные.

Внимание! Никогда не указывайте ключ -ig при обычном бэкапе – если в базе есть ошибки, gbak их проигнорирует и вы не узнаете, что база была повреждена. В результате такой бэкап может быть невосстановимым.
Если ваши приложения работают с двухфазным коммитом, то возможно потребуется ключ -limbo для игнорирования зависших 2pc транзакций (limbo).

8. Теперь надо попытаться восстановить базу данных из backup

gbak -c -v database.gbk new.gdb
 

Внимание! Никогда не делайте restore поверх существующей базы данных. В случае ошибки при restore вы лишитесь оригинальной базы данных.

9. Если при restore есть ошибки, то нужно попробовать следующие ключи:
-inactive, если есть проблема с индексами, то с этим ключом база будет восстановлена с деактивированными индексами. Их можно потом активировать (altex index X active) поодиночке.
-one_at_a_time, этот ключ приводит к commit после восстановления каждой таблицы. По крайней мере будет восстановлена хотя бы часть таблиц.

Внимание! gbak в InterBase 7.x и выше по умолчанию не проверяет при restore следующие ограничения – not null, check, primary, unique, foregin key. Для восстановления оригинального функционирования gbak процесс restore должен проводиться с ключом -validate. У Firebird наоборот, проверка ограничений включена, поэтому для ее отключения нужно указывать ключ -no_validity. Если до сих пор так и не получилось сделать нормально backup и restore, но доступ к поврежденной базе все-таки есть, можно попробовать утилитой IBPump (или другими) скопировать хотя бы часть (неповрежденную) данных.

Невосстанавливаемый backup

Получить backup, который не пройдет restore («невосстанавливаемый» или «невосстановимый») вполне возможно по ряду причин и без сбоев сервера или оборудования. Для минимизации потраченного времени на поиск мест, где эти ошибки происходят, всегда нужно делать restore с ключом -v, и еще лучше – с выводом в файл (gbak …. -v -y rest_log.txt). Если с починкой не сможете справиться сами, то тогда как минимум сможете предъявить в news-конференции или службе технического сопровождения точное сообщение об ошибке.

Существует утилита IBBackupSurgeon, которая позволяет вытащить из бэкапа БД необходимую информацию, даже в случае повреждения файла бэкапа. Также его можно использовать для нормальных или поврежденных бэкапов с целью извлечения из них только части хранимой информации (данных, структур и т. п.).

Проблемы с индексами

Как известно, восстановление базы данных из бэкапа происходит следующим образом:

  1. gbak дает команду серверу создать новую БД с параметрами из бэкапа,
  2. gbak копирует в новую базу пользовательские метаданные из бэкапа,
  3. gbak копирует в новую базу пользовательские данные из бэкапа,
  4. gbak активирует (создает) все индексы.

Пункт 4, то есть создание индексов, не случайно выполняется после восстановления всех данных. Если бы индексы были активны сразу после пункта 2, то заливка данных в БД была бы очень медленной.Соответственно, раз индексы создаются в самом конце процесса restore, ошибки при их создании не являются фатальными для функционирования БД, разве что без индексов запросы будут работать очень медленно. Во всех версиях InterBase и Firebird (кроме Firebird 2.x) при первой же ошибке создания индекса дальнейшее создание индексов прекращается. В Firebird 2.x ошибка создания индекса сообщается, но создание других индексов продолжается, что позволяет легко идентифицировать неактивные индексы по окончании restore.

В данном случае считать базу «невосстановленной» нельзя. Но понятно, что из-за неактивности некоторых индексов определенные запросы будут работать медленнее, и ссылочная целостность по этим FK проверяться не будет.

Дубликаты в уникальных индексах

Как уже было упомянуто в начале статьи, повреждения индексов обычно незаметны. Более того, повреждения уникальных индексов (ограничения Primary Key, Unique или просто уникальный индекс) могут привести к появлению неуникальных дубликатов записей в таблице. Во время работы это заметно не будет, однако при попытке restore базы данных может быть выдано сообщение о невозможности создать конкретный индекс.Интересно, что пользователи InterBase 7.x/2007, 2009, XE могут вообще не обнаруживать эту проблему, т. к. gbak в InterBase 7.x и выше по умолчанию не проверяет при restore следующие ограничения – not null, check, primary, unique, foregin key. То есть база может пережить много циклов сохранения-восстановления без обнаружения данной проблемы. Данное поведение полезно только в том случае, когда вам нужно восстановить именно невосстанавливаемый с данными проверками бэкап. Для принудительного контроля логической целостности при restore настоятельно рекомендуется добавлять ключ -validate.

У Firebird наоборот, по умолчанию ограничения проверяются, а выключить их проверку можно указав ключ -no_validity. Соответственно, при наличии дубликатов в уникальном индексе нужно эти дубликаты найти, а затем удалить. После чего проверить логическую целостность БД через бэкап-рестор.

Отсутствующие записи по Foreign key

Этот тип логической нецелостности данных также был описан в первой части статьи. Бороться с этой проблемой можно как на оригинальной базе данных, так и на восстановленной из бэкапа. Нужно выполнить запрос, который покажет имеющиеся в детали и отсутствующие в мастере идентификаторы.

Затем можно или записи с этими идентификаторами удалить из таблицы деталей, либо добавить соответствующие записи в таблицу-мастер (первое быстро, но с потерей данных; второе – долго, зато данные остаются). После исправления несоответствий в мастер-детали можно пересоздать (или активировать индекс) Foreign key.

Добавленный столбец NOT NULL

Это самая частая проблема, с которой сталкиваются разработчики. Известно, что IB позволяет добавлять новые столбцы к таблице даже если есть любое количество данных в этой таблице. При этом меняется формат таблицы (запись в системных таблицах), однако существующие записи никаким образом не обновляются. Процесс backup запишет данные как есть, и не обнаружив значения столбца установит флаг соответствующего столбца у записи в NULL. При restore контроль not null не даст записать такие данные в таблицу.

Внимание! gbak в InterBase 7.x не проверяет при restore следующие ограничения – not null, check, primary, unique, foregin key. Для восстановления оригинального функционирования gbak процесс restore должен проводиться с ключом -validate.

В Firebird для gbak при restore нужно указывать дополнительный ключ -no_validity. Если такая ошибка при restore является единственной, то можно попытаться сделать следующее:

  1. В базе данных у столбца убрать контроль not null. Сделать backup только метаданных (ключ -m). Если оригинальной базы уже нет, т.е. есть только бэкап, то сделать restore только метаданных, убрать в базе у столбца контроль not null… сравнить побайтово бэкап метаданных, полученный пунктом 1, и бэкап с данными. Сравнивать, естественно, по размеру бэкапа метаданных. Просмотреть обнаруженные отличия hex-редактором, чтобы убедиться, что отличия находятся примерно в месте расположения объявления домена или столбца. Исправить отличия hex-редактором в backup.
  2. Сделать restore только метаданных. Если флаг not null у столбца или домена пропал, значит все хорошо, и можно делать restore всего backup.

Если приведенная процедура кажется вам сложной, или вы не хотите заниматься этим в будущем – перед бэкапом используйте утилиту CheckNull.

Изменение типа столбца

То же самое, что и предыдущий случай. Допустим, у таблицы есть строковый столбец. Его потребовалось заменить на столбец типа integer. IB допускает такую операцию (alter table alter column в IB6 и выше), но не пытается в момент изменения типа столбца преобразовать данные. Если хоть в одной записи в таком столбце случайно оказался символ, который невозможно преобразовать в число, restore не пройдет.

Помните, что исправить ситуацию повторным изменением типа столбца с integer на строку не удастся! В таких случаях обязательно надо перед изменением типа столбца проверять его на последующую читаемость операцией

SELECT CAST(FIELD as INTEGER) FROM TABLE

и просмотром всех записей возвращаемых этим запросом в любом инструменте. Если при просмотре всей таблицы не произошло ошибок вроде «transliteration … overflow…», то тип столбца можно смело менять. Если же ошибка была, нужно искать «проблемную» запись, и менять ее значение на допустимое для преобразования.То же самое относится и к случаю уменьшения длины строк. Операция alter, допустимая в IB 6 и выше, не даст уменьшить длину строкового столбца, т. к. данные могут оказаться длиннее нового размера столбца. Поэтому более безопасным способом (и для изменения типа столбца) является добавление нового столбца с нужным типом, копирование данных в него (update table set oldfield=newfield), удаление старого столбца и переименование нового в старое имя.

Однако такой способ часто не подходит, если на столбец есть масса ссылок из других объектов БД (например, таблиц), или объем данных очень велик. Изменить длину столбца или его тип в этом случае остается возможным только через системные таблицы. Но опять же, лучше заранее проверить максимальную длину данных (используя udf strlen в любой библиотеке udf, не из ib_udf) – select max(strlen(field)) from table.

Check constraint, не соответствующий хранимым данным

Cмысл проблемы состоит в том, что при restore сначала восстанавливаются метаданные, а потом уже сами данные. Исправить ситуацию можно используя при restore gbak с ключом -n, однако при этом не будут восстановлены ВСЕ check constraints у всех таблиц.

Данная ситуация также может возникнуть, например, если в для проверки столбца используется select. Если база данных была повреждена, и вы сделали gfix -mend, часть данных может пропасть (оказаться поврежденными и не попасть в backup). А раз часть данных пропала, ею могут оказаться как раз те данные, которые проверяли столбец по select. Также, в самых первых версиях InterBase 6.0 была ошибка, при которой check constraints проверяли данные до срабатывания триггеров. Таким образом, забыв о контроле check constraint можно было написать такой триггер, который изменял бы данные в противоречивое с check constraint состояние. Например, check constraint проверяет значение столбца > 0 и < 10, а триггер мог спокойно содержать текст вроде new.field + 100. В результате тоже можно было получить невосстановимый backup. В InterBase 6.5, последних версиях Firebird и Yaffil, эта ошибка исправлена.

Кроме перечисленных случаев для check constraints и computed by хочется отметить еще один. Синтаксис create/alter table допускает создание контроля (или вычисляемого) поля как SELECT из другой таблицы. Т. е. это разрешено в соответствии со стандартом, но может оказаться) крайне неэффективным.

Мне приходилось видеть запрос с sum и группировкой в check constraint. Это означает что при выборке такого столбца указанный запрос будет выполняться каждый раз. В отношении check это означает, что заданный запрос а) будет выполняться при каждом insert или update, б) может привести к невосстановимому backup.

Обычно люди не задумываются, каким образом серверу удается восстановить данные из бэкапа для взаимосвязанных таблиц. Например, если оперативная таблица (накладные) восстанавливается раньше чем справочная таблица (товары). Фокус в том, что FK, который отслеживает целостность между этими двумя таблицами, активируется после восстановления всех данных. А в случае check constraints или computed by речь идет о структурах таблиц, которые сначала создаются, а потом заполняются.

Пример: у таблицы X есть столбец, для которого определен контроль

CHECK(STUFF IN (SELECT STUFF FROM STUFFHOLDER WHERE STUFF_ID=100))

такая конструкция может сделать невосстановимым бэкап дважды:

  1. если структура таблицы X будет восстанавливаться раньше таблицы STUFFHOLDER
  2. если данные таблицы X будут восстанавливаться раньше данных таблицы STUFFHOLDER

Кроме этого, весьма странно устанавливать зависимость метаданных таблицы от конкретных значений данных совсем другой таблицы. В такой ситуации лучше и намного безопаснее создать триггер с указанной проверкой (а кроме того заменить IN на EXISTS. Еще правильнее вообще так не делать, а сделать нормальный Foreing Key на STUFFHOLDER).

Еще одна ошибка, которая связана с check constraint, но не с данными, уже давно исправлена в Firebird и Yaffil. Это использование русских букв в default для строковых столбцов. При restore такая БД выдаст сообщение «arithmetic exception, numeric overflow, or string truncation» еще на этапе восстановления метаданных. При наличии оригинальной базы данных необходимо удалить такой default, и сделать backup повторно. При отсутствии БД следует попытаться использовать ключ gbak -n.

Процедуры и триггеры

Здесь ситуация с невосстановимым backup может произойти при модификации (alter) процедур, которые вызываются другими триггерами или процедурами. Если меняется только тело процедуры, то проблем не будет. А если меняется количество или типы параметров процедуры, то в этом случае при restore (да и при alter вызывающих процедур) будет выдаваться сообщение о том, что список параметров процедуры не соответствует используемому.

Проблема там же, где и всегда – при реконструировании метаданных объекты строятся в памяти один за другим, образуя целые «гирлянды» из взаимосвязанных объектов. При этом код этих объектов (текст процедур или триггеров) не перекомпилируется. Но список параметров процедур хранится отдельно, в rdb$procedure_parameters, поэтому несоответствие используемомого и хранимого наборов становится камнем преткновения для restore. Борьба с этой ситуацией возможна фактически только при наличии оригинальной БД. Сейчас можно удалить или изменить процедуру, которая вызывает другую с измененным количеством параметров, однако в предыдущих версиях InterBase и Firebird это не было возможным. Единственный способ исправить проблему приведен в документе. Однако, с триггерами, вызывающими такие процедуры, ситуация похуже, потому что сервер не даст возможности обновить blr триггера. Поэтому остаются только «хакерские» приемы, вроде создания «пустой» процедуры с нужным количеством и типами параметров, и изменению blr триггера при помощи hex-editor.

В общем, к подобным изменениям нужно относиться более тщательно, используя инструменты, которые показывают зависимости между объектами (IBExpert), и комментируя вызовы процедур перед изменением количества или типов их параметров.

Повреждения системных таблиц

Разумеется, при повреждении системных таблиц информация о некоторых объектах будет потеряна, и backup не сможет сохранить данные этих объектов. Однако, если сами данные в системных таблицах целы, а индексы системных таблиц повреждены, backup может неверно сохранить информацию об объектах.

Происходит это потому, что backup выполняет запросы к системным таблицам, а эти запросы могут использовать индексы. Если индекс поврежден, то запрос может вернуть неверную информацию (меньше записей, чем их есть на самом деле). Таким образом или часть объектов просто не попадет в backup, или при restore будут серьезные проблемы из-за несоответствия информации в связанных между собой системных таблицах.

Поэтому, если вы в interbase.log наблюдаете информацию о поврежденных индексах на системных таблицах, то делать backup надо только после удаления таких индексов. Поврежденные индексы можно особо не выискивать, а удалить все целиком в rdb$indices, у которых rdb$relation_name начинается с префикса RDB$. Все равно индексы по системным таблицам относятся к системным таблицам, и создаются gbak при restore автоматически, независимо от содержимого backup.

Есть еще один случай повреждения базы данных, который относится к правам на объекты (таблицы, процедуры и т. п.). Проявляется это повреждение как ошибка в процессе restore с сообщением об отсутствии объекта, на который существуют назначенные права (в самом конце процесса restore). Как следует из кода restore.e, данная проверка является избыточной, т. к. если объекта нет, то и права на него восстанавливать не надо. Однако, код restore.e в таком случае выдает не предупреждение, а сообщение об ошибке, в результате чего процесс restore не доходит до завершения.

В конкретном случае были попытки определить несоответствие в rdb$user_privileges и остальных таблицах (rdb$relations и т. п.), однако таковых найдено не было. Ситуацию удалось решить компиляцией специального варианта restore.e (gbak.exe), в котором при отсутствии объекта, на который выдаются права, просто ничего не делается (не выдается никаких сообщений). Остается надеяться, что в новых версиях IB/FB/YA эта ситуация будет исправлена.

Пока что для обнаружения подобных проблем можно воспользоваться следующим способом – при правильном backup/restore всегда оставляют оригинальную БД (см. невосстанавливаемый backup). Для проверки существования в восстановленной базе данных всех объектов можно извлечь скрипты из обоих БД (например isql-ом), и сравнить их утилитой Database Comparer (info, download). Если отличий найдено не будет, значит структуры обоих БД идентичны. Если нет – найти поврежденный индекс и саму системную таблицу очень легко, зная в каких системных таблицах какие объекты хранятся (Language Reference.pdf).

Проблемы оборудования

Проблемы «железа» тоже могут сделать бэкап невосстанавливаемым. Я сам столкнулся со случаем, когда пользователь сообщил о том, что при попытке restore ошибки выдаются в разных местах. Было сделано предположение о дефективном HDD или RAM. Последнее предположение подтвердилось.В таких случаях хорошо, если проблемы оборудования проявили себя на этапе restore, а не backup. Если бы память (RAM) начала сбоить в момент backup, никто бы не узнал о проблемах вплоть до момента restore, и наверняка файл backup был бы поврежден так, что его нельзя было бы восстановить никаким образом. Причем никакими средствами вроде «контрольных сумм backup» предотвратить эту ситуацию невозможно, т. к. в файл данные пишутся именно операционной системой из памяти.

Поэтому позволю себе дать несколько банальных советов: не используйте модули RAM разных производителей в одном компьютере
старайтесь для сервера выбирать motherboard и память, поддерживающие ECC.

Дополнительные способы ремонта БД

Иногда возникают ситуации, которые не позволяют сделать backup даже после всех усилий применения gfix. Например, при бэкапе одной БД gbak выдал сообщение «Blob xxx corrupted». Если вы прочитаете статью по процессу gfix в конце этой статьи, то там написано, что такие ошибки чинятся gfix с ключом -mend. Однако это все равно не помогло, сообщение об ошибке не пропадало, и сделать backup было невозможно. Поэтому были проделаны следующие шаги:

  1. В инструменте, который не помещает записи в буфер (не кэширует. Можно написать простую программу с использованием компонента IBSQL из IBX. Главное, чтобы при больших объемах считываемые данные не попадали в кэш программы, иначе к концу считывания пары миллионов записей программа будет работать ужасно медленно) были просмотрены все записи через PgDn. При этом записи считываются с диска, но блобы не выбираются (можно написать select field1, field2, field… from table, исключая столбцы blob, если просмотр данных все-таки идет в гриде). Обнаружено, что таблица читается целиком. Значит, действительно поврежден блоб.
  2. Для нахождения поврежденного блоба сканирующая программка была дополнена операцией чтения блоба, простым присвоением переменной string (asString). Перед чтением блоба ключевое поле записи выдавалось на экран.
  3. В момент ошибки ключевое поле записи запомнили, после чего сделали

UPDATE TABLE
SET BLB=NULL
WHERE PK_FIELD = x

где вместо x подставили значение ключевого поля. Поскольку значение blob было испорчено, главным было обеспечить backup остальных записей и их blob.

Backup после этой операции прошел успешно.

То же самое можно проделывать в отношении «дефектных» записей (или их версий), при чтении которых выдается сообщение об ошибке. Основная трудность состоит в том, что физически на диске записи неотсортированы, и придется несколько раз выполнять запросы, выбирающие записи с разных «сторон» таблицы, чтобы найти примерно запись с какими данными (первичным ключом) не может быть прочитана. Пример кода, который производит копирование записей из «дефектной» таблицы в другую БД.

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

Внутри val.c есть кусочек документации, описывающий что и как делает gfix. Вот перевод этого куска. (Ни один другой файл исходного текста не содержит подобной «документации»).

Database Validation and Repair

Deej Bredenberg
March 16, 1994
Updated: 1996-Dec-11 David Schnepper

I. Терминология

Приводимые термины помогут понять этот документ:

  • record fragment (фрагмент записи): наименьший распознаваемый кусочек записи. Несколько фрагментов могут быть связаны и формировать отдельную версию записи.
  • record version (версия записи): Версия записи, созданная в результате выполнения оператора INSERT, UPDATE или DELETE в какой либо транзакции (обратите внимание, что при удалении создается новая версия записи, которая представляет собой так называемый «deleted stub»).
  • record chain (цепочка записей): Связанный список версий записи, который представляет собой целостную «запись».
  • slot (слот): Номер строки, где находится запись на странице. Массив переменной длины на каждой странице данных хранит смещения на записи, находящиеся на этой странице. Слот является индексом этого массива. За более подробной информацией по структуре страницы данных обращайтесь к моему документу по ядру Interbase (документ отсутствует).

II. Опции командной строки

Вот все ключи командной строки, которые используются при починке базы gfix-ом:

ключ константа dbp  
-validate isc_dpb_verify Выполняет проверку и починку БД. Все другие ключи модифицируют действие этого ключа.
-full isc_dpb_records Проверяет все записи. Без этого ключа проверяются только структуры страниц.
-mend isc_dpb_repair Пытается починить базу данных, чтобы она хотя бы могла быть читаема. Не гарантирует восстановления испорченных данных.
-no_update isc_dpb_no_update Указывает, чтобы осиротевшие страницы не освобождались, а занятые не помечались как используемые, если обнаружится что они на самом деле свободны. Не совсем корректное название ключа, потому что при его употреблении с -mend база будет чиниться, а если -mend не указан и указано -no_update, никаких изменений в БД сделано не будет.
-ignore isc_dpb_ignore Выключает подсчет контрольных сумм при выборке страниц. Проверка все равно будет сообщать контрольные суммы. Рекомендуется к использованию.
Замечание: ODS 8.0 не имеет контрольных сумм на страницах. ODS 9.0 вообще не имеет контрольных сумм.

III. Как это работает

Проверка БД работает только при эксклюзивном доступе к базе данных, чтобы в момент починки не было никаких посторонних изменений. При подключении к БД происходит попытка поставить эксклюзивную блокировку на файл базы данных. Если обнаружены другие коннекты к БД, выдается сообщение: «Lock timeout during wait transaction — Object «database_filename.gdb» is in use»

ЗАМЕЧАНИЕ: Обычно, когда процесс получает эксклюзивный доступ к БД, все активные транзакции помечаются как dead в Transaction Inventory Pages. Это поведение выключено при проверке БД.

IV. Фазы проверки

Существует две фазы проверки. В первой сканируется вся база данных. В этот момент номера всех страниц помещаются в битовую маску, для последующего использования в фазе сборки мусора

A. Просмотр страниц

В первой фазе любая выбираемая страница проходит проверки:
1. Контроль типа страницы
Каждая страница проверяется на ожидаемый тип страницы. Если в заголовке страницы указан неверный тип страницы (с точки зрения распределения страниц), то выдается сообщение: «Page xxx wrong type (expected xxx encountered xxx)» Это может сигнализировать о

  1. проблеме в базе данных
  2. ошибке в механизме распределения страниц IB, что привело к переписыванию одной страницы другой
  3. том, что страница, которая была распределена, не была записана на диск (чаще всего в этом случае тип страницы = 0). Это сообщение не говорит о том, как называется соответствующих тип страницы, поэтому здесь приведена выдержка из ods.h:

#define pag_undefined 0 /* не определено */
#define pag_header 1 /* страница заголовка базы данных */
#define pag_pages 2 /* Page Inventory Page — страница списка страниц */
#define pag_transactions 3 /* Transaction Iinventory Page — страница списка состояния транзакций*/
#define pag_pointer 4 /* страница указателей */
#define pag_data 5 /* страница данных */
#define pag_root 6 /* корневая страница индекса */
#define pag_index 7 /* страница индекса (B-tree) */
#define pag_blob 8 /* страница данных Blob */
#define pag_ids 9 /* страница значений генераторов */
#define pag_log 10 /* Write ahead log: только для 4.0 */

2. Контрольные суммы
Если указан ключ -ignore, то контрольная сумма считается при проверке, но не ядром. Если контрольная сумма не совпадает, то будет выдана ошибка:»Checksum error on page xxx» Это не влияет на проверку, и страница будет продолжать проверяться. Если указан ключ -mend, то страница в конце проверки будет записана на диск, и ее контрольная сумма будет автоматически пересчитана.
ЗАМЕЧАНИЕ: Только 4.0 для Windows и Netware обрабатывает контрольные суммы.
3. Повторный просмотр
Мы проверяем каждую выбранную страницу в битовой карте выбранных страниц, чтобы просматривать только новые страницы. Если номер повторяется, то будет выдано сообщение: «Page xxx doubly allocated» Это означает что на страницу одного и того же типа дважды ссылаются из разных мест. Страницы данных не проверяются этим механизмом, поскольку они проверяются намного чаще при просмотре фрагментов и цепочек записей.

B. Сборка мусора

В этой фазе страницы Page Inventory (PIP) проверяются на упоминание в битовой карте просмотренных страниц. Этим детектируются два типа ошибок:
1. Осиротевшие (Orphan) страницы
Если в PIP упомянута страница, которая еще не прошла проверку, то будет выдано сообщение «Page xxx is an orphan». Если не был указан ключ -no_update, то страница будет помечена в PIP как свободная.
2. Некорректно освобожденные страницы
Если страница помечена как свободная в PIP, но проверялась при первой фазе проверки, будет выдано сообщение:»Page xxx is use but marked free» (sic) (KDV: ни разу не видел такого сообщения. А вот page xxx is orphan – хоть отбавляй, особенно при крахе ОС в тот момент, когда производится заливка данных в БД). Если не был указан ключ -no_update, то такая страница будет помечена в PIP как используемая.
ЗАМЕЧАНИЕ: Если в фазе проверки были обнаружены ошибки, то PIP изменяться не будут. Потому что нет шансов проверить все страницы, если обнаружены поврежденные структуры.

V. Фаза просмотра

A. Выбор страниц

Для проверки того, что все страницы были считаны, следующие страницы проходят следующие проверки:
1. Заголовок базы данных (нулевая страница).
2. Page Inventory pages.
3. Transaction Inventory pages. Если невозможно просмотреть системную таблицу RDB$PAGES, или эта таблица не содержит ни одного номера страницы TIP, выдается сообщение: «Transaction inventory pages lost». Если какая то страница пропала из списка, указанного в RDB$PAGE_SEQUENCE, будет выдано следующее сообщение: «Transaction inventory page lost, sequence xxx». Если указан ключ -mend, то распределяется новая страница TIP, и корректно записывается в RDB$PAGES. Все транзакции, которые находились на потерянной таким образом странице, считаются завершенными по committ. Если страница TIP не указывает на следующую в последовательности страницу, то выдается сообщение: «Transaction inventory pages confused, sequence xxx» 5. Страницы генераторов, как указано в RDB$PAGES.

B. Проверка таблиц

Проверяются все таблицы в базе данных. Для каждой таблицы выбираются все индексы, и все страницы указателей и данных (см. дальше). Но вначале, сканируются метаданные в RDB$RELATIONS для определения формата таблицы. Если эта информация утеряна или повреждена, таблица не может быть поверена. Если любые ошибки бнаружены в момент проверки, выдается сообщение: «bugcheck during scan of table xxx ()» После этого любая дальнейшая проверка таблицы прекращается.
ЗАМЕЧАНИЕ: Для views производится только проверка метаданных.

C. Проверка индексов

До версии 4.5 (NevaStone) индексы проверялись перед страницами данных. В 4.5 проверка индексовы была перенесена после проверки страниц данных. См. дальше раздел «Проверка индексов». (KDV: версию 4.5 практически никто не видел. Выпущена она была для какой то одной операционной системы, и поставлялась только клиентам, использующим 4.1. В продажу не поступала)

D. Страницы указателей

Проверяются все страницы указателей для таблицы. При их проходе проверяются все дочерние страницы (см. дальше). Если страница указателей не может быть найдена, выдается сообщение: «Pointer page (sequence xxx) lost». Если страница указателей оказалась не той таблицы, которую мы проверяем, или она не помечена в правильной последовательности, выдается сообщение: «Pointer page xxx is inconsistent». Для каждой страницы указателей, которая не указывает на следующую страницу указателей в соответствии с столбцом RDB$PAGE_SEQUENCE в RDB$PAGES, выдается ошибка: «Pointer page (sequence xxx) inconsistent».

E. Страницы данных

Проверяется каждая страница данных, на которую указывает страница указателей. Если найдены повреждения на уровне страницы, и указан ключ -mend, то страница удаляется со страницы указателей. Это приводит к потере данных, которые располагались на этой странице данных. Если страница данных повреждена на уровне страницы, и не помечена как часть проверяемой таблицы, или не помечена в правильной последовательности, выдается сообщение: «Data page xxx (sequence xxx) is confused».

F. Проверка слотов

Проверяется каждый слот на странице, и на ней обновляется количество хранимых записей. Если слот не нулевой, то выбирается фрагмент записи по указанному смещению. Если запись начинается перед концом массива слотов, или продолжается за пределами страницы, выдается сообщение: «Data page xxx (sequence xxx), line xxx is bad», где «line» означает номер слота.
ЗАМЕЧАНИЕ: Если обнаруживается такое условие, страница данных считается поврежденной на уровне страницы, и в результате будет удалена со страницы указателей, если указан ключ -mend.

G. Проверка записей

Проверяется запись в каждом слоте, независимо от указания ключа -full. Фрагмент записи может быть опознан как
1. Обратная версия
Если фрагмент помечен как «обратная» версия, то он пропускается. Он будет выбран как часть записи.
2. Поврежденный
Если обнаруживается, что фрагмент поврежден любым образом, и указан ключ -mend, то в заголовке запись помечается как «поврежденная».
3. Помеченный как поврежденный
Если фрагмент уже помечен как поврежденный в результате предыдущей проверки, то выдается сообщение: «Record xxx is marked as damaged», где xxx является номером записи.
4. От неверной транзакции
Если запись имеет номер транзакции больше, чем номер самой последней транзакции в базе данных, выдается сообщение: «Record xxx has bad transaction xxx».

H. Проверка записей

Если указан ключ -full, и фрагмент является первым фрагментом логической записи, то запись в соответствующем номере слота выбирается целиком. Это приводит к выборке всех версий, и всех фрагментов для каждой версии. Другими словами, запись извлекается целиком.
1. Обратные версии
Если есть обратные версии, то они в этот момент проверяются. Если обратная версия находится на другой странице, то страница выбирается, но не проверяется пока она не будет проверена отдельно (проверкой страниц). Если номер слота обратной версии больше количества записей на странице, или нет записи в таком номере слота, или это blob, или это фрагмент записи, или фрагмент поврежден, выдается сообщение: «Chain for record xxx is broken».
2. Неполные
Если заголовок записи имеет пометку «неполный», это означает что для выборки записи нужно обратиться к другим фрагментам – запись оказалась слишком большой, чтобы поместиться в один слот. Для фрагментированных записей все фрагменты выбираются для всех версий записей. Если любой из фрагментов не обнаруживается в ожидаемом месте, или имеет неверную длину, выдается сообщение: «Fragmented record xxx is corrupt». Как только запись выбрана целиком, проверяется длина формата, в соответствии с ожидаемым в RDB$FORMATS (номер формата хранится в заголовке записи, представляя точную структуру таблицы на тот момент, когда запись была сохранена на диск). Если длина реконструированной записи не соответствует длине, получаемой из формата, выдается сообщение: «Record xxx is wrong length». Для версий записей, которые представляют собой результат действия оператора update, данная проверка не производится.

I. Проверка блобов

Если слот на странице данных указывает на запись blob, то blob выбирается (даже без -full). При этом возникает ряд вариантов, в соответствии с различными уровнями blob (см. документацию «Особенности ядра»). (KDV: документация с названием Engine Internals (Особенности ядра) не опубликована до сих пор). Действия в зависимости от уровня
—— ——————————————————————
0 Данные blob находятся тут же, на странице данных. Дополнительные проверки не производятся.
1 Все страницы, на которые указывает запись blob, выбираются и проверяются.
2 Все страницы, на которые указывают страницы указателей blob, выбираются и проверяются..
3 Страница blob является указателем на страницы blob. Все дочерние страницы выбираются и проверяются. Для каждой найденной страницы blob производятся проверки. Если страница не указывает обратно на страницу-источник, то выдается сообщение: «Warning: blob xxx appears inconsistent» где xxx это номер записи blob. Если любая из страниц blob не отмечена в последовательности, как это ожидается, выдается сообщение: «Blob xxx is corrupt» Tip: сообщение для такой же ошибки, но на уровнях 2 и 3, немного отличается: «Blob xxx corrupt». Если потеряна любая из страниц blob в последовательности, выдается сообщение: «Blob xxx is truncated». Если обнаруживается, что выбираемый blob поврежден одной из вышеуказанных причин, и указан ключ -mend, то запись blob помечается как поврежденная. (KDV: иногда маркировка как «damaged» не помогает. Т. е. сообщение blob xxx corrupt выдается при backup независимо от того, как и с какими ключами производилась починка БД gfix. См. пример ручного исправления ситуации в начале статьи).

J. Проверка индексов

В версии 4.5 (NevaStone) проверка индексов выполняется после завершения проверки страниц данных. Проверяются индексы для конкретной таблицы. Если корневая страница индексов не обнаружена, выдается сообщение: «Missing index root page» и индексы не проверяется. Иначе считывается корневая страница все индексы, упомянутые на этой странице, проверяются. Для каждого индекса, страницы btree выбираются сверху вниз, слева направо. Базовая проверка нелистовых страниц проверяет, указывает ли каждый узел на другую страницу индекса. Если указано -full, то проверяется целостность страниц нижнего уровня в том, что они указывают на родительские страницы. На листовых страницах указатели на страницы индекс не проверяются. Ключи проверяются на правильный порядок следования (по возрастанию или убыванию). Если проверяемая страница не относится к проверяемой таблице или индексу, выдается сообщение: «Index xxx is corrupt at page xxx». Если есть осиротевшие дочерние страницы, т. е. дочерние страницы, на которые не указывает родительская страница, то обновляется btr_sibling дочерней страницы и выводится сообщение об ошибке: «Index xxx has orphan child page at page xxx». Если страница не содержит число ключей, которые мы ожидаем по подсчитанной длине, то выдаем сообщение: «Index xxx is corrupt on page xxx». При проходе листовых страниц, мы сохраняем битмап всех номеров записей, видимых в индексе. По окончании прохода по индексу мы сравниваем полученный битмап с битмапом записей в таблице (полученном при проходе страниц данных и проверке записей). Если эти битовые маски не эквивалентны, значит мы имеем дело с поврежденным индексом, и выдаем следующее сообщение: «Index %d is corrupt (missing entries)». Мы НЕ проверяем каждую версию каждой записи на существование соответствующего ключа в индексе. Также мы не проверяем что конкретный ключ индекса соответствует определенной записи или версии.

K. Проверка таблиц

Мы считаем количество обратных версий, видимых при просмотре pointer pages, и отдельно считаем количество обратных версий при просмотре цепочек записей. Если эти числа не совпадают, значит или появились «осиротевшие» обратные версии, или двусвязанные списки. В этом случае выводится сообщение: «Relation has %ld orphan backversions (%ld in use)». В настоящее время мы не исправляем такую ситуацию, а только сообщаем о ней. Занятое «осиротевшими» обратными версиями пространство может быть возвращено путем backup/restore. Для двусторонних цепочек SWEEP должен удалять все обратные версии.

VI. Дополнительные замечания

A. Поврежденные записи

Если при проверке обнаруживается повреждение любого фрагмента записи, то заголовок такой записи помечается как «damaged» (поврежденная). Насколько я могу видеть, это не имеет влияния на ядро. Записи помеченные таким образом будут и дальше выбираться ядром. А вот будет ли такая запись выбираться при backup – это вопрос. Если идет обращение к поврежденной записи, то выводится следующее сообщение: «Record xxx is marked as damaged» Обратите внимание, что когда обнаруживается поврежденная запись, это сообщение не выводится. Запись просто помечается как поврежденная. И только после того, как будет повторное обращение к такой записи, будет выведено это сообщение. Поэтому я утверждаю, что пока не будет сделана полная проверка БД, вы можете не увидеть такого сообщения. А вот после полной проверки БД такое сообщение будет появляться даже тогда, когда gfix запускается без ключа -full.

B. Поврежденные Blob

Записи Blob, помеченные как поврежденные, не могут быть открыты и будут удалены с диска. Это означает, что даже при backup помеченные как поврежденные структуры Blob не будут выбраны и сохранены в backup (почему сделано для blob и записей по разному, я не знаю. Может быть это показалось слишком сложным – извлекать поврежденный blob).

Заключение

Третий закон Чизхолма: Любые предложения люди понимают иначе, чем тот кто их вносит.
Следствие 1: Даже если ваше объяснение настолько ясно, что исключает всякое ложное толкование, все равно найдется человек, который поймет вас неправильно.

Спасибо за комментарии и поправки к статье:

Олегу Иванову, Альберту Губайдуллину, Алексею Емельянову, Игорю Захребеткову, Александру Невскому, Дмитрию Еманову

Литература

  1. Interbase Operations Guide Diagnosing and Repairing InterBase Database Corruption, Paul Beach
  2. Невосстановимый Backup или GBAK и уверенность в завтрашнем дне, А. Невский.
  3. CVS/interbase/jrd/val.c

Дополнительно читать

  1. А. Н. Ковязин, С. М. Востриков, «Мир InterBase», изд. Кудиц-Образ, 2002 г. 

Ссылки

  • Платный ремонт баз данных
  • Утилита (платная) для ремонта определенных повреждений БД

1. Introduction

Gfix allows attempts to fix corrupted databases, starting and stopping of databases, resolving ‘in limbo’ transactions between multiple databases, changing the number of page buffers and so on.
Gfix is a general purpose tool for system administrators (and database owners) to use to make various ‘system level’ changes to their databases.

Almost all the gfix commands have the same format when typed on the command line:

gfix [commands and parameters] database_name

The commands and their options are described in the following sections.
The database name is the name of the primary database file which for a single file database is simply the database name and for multi-file databases, it is the first data file added.

Coming up in the remainder of this manual, we will discuss the following:

  • Command line options for the gfix database utility.

  • Shadow file handling.

  • Cache and buffer handling.

  • Transaction management.

  • Cache management.

  • Starting and stopping a database.

  • And much, much more …​

2. Command Line Options

Running gfix without a command (or an invalid command), or with the -⁠? switch, results in the following screen of helpful information:

gfix usage instructions for Firebird 3.0.7

usage: gfix [options] <database>
plausible options are:
   -ac(tivate_shadow)   activate shadow file for database usage
   -at(tach)            shutdown new database attachments
   -b(uffers)           set page buffers <n>
   -co(mmit)            commit transaction <tr / all>
   -ca(che)             shutdown cache manager
   -fu(ll)              validate record fragments (-v)
   -fo(rce_shutdown)    force database shutdown
   -fe(tch_password)    fetch password from file
   -h(ousekeeping)      set sweep interval <n>
   -i(gnore)            ignore checksum errors
   -icu                 fix database to be usable with present ICU version
   -k(ill_shadow)       kill all unavailable shadow files
   -l(ist)              show limbo transactions
   -me(nd)              prepare corrupt database for backup
   -mo(de)              read_only or read_write database
   -nol(inger)          close database ignoring linger setting for it
   -n(o_update)         read-only validation (-v)
   -o(nline)            database online <single / multi / normal>
   -pr(ompt)            prompt for commit/rollback (-l)
   -pa(ssword)          default password
   -role                set SQL role name
   -r(ollback)          rollback transaction <tr / all>
   -sq(l_dialect)       set database dialect n
   -sw(eep)             force garbage collection
   -sh(utdown)          shutdown <full / single / multi>
   -tw(o_phase)         perform automated two-phase recovery
   -tra(nsaction)       shutdown transaction startup
   -tru(sted)           use trusted authentication
   -u(se)               use full or reserve space for versions
   -user                default user name
   -v(alidate)          validate database structure
   -w(rite)             write synchronously or asynchronously
   -z                   print software version number

    Options can be abbreviated to the unparenthesized characters

3. Gfix Commands

In the following discussion, I use the full parameter names in all examples.
This is not necessary as each command can be abbreviated.
When the command is shown with ‘[’ and ‘]’ in the name — the gfix -⁠? output uses parentheses instead — then these are the optional characters.

For example, the command -⁠validate is shown as -⁠v[alidate] and so can be specified as -⁠v, -⁠va, -⁠val and so on up to the full -⁠validate variant.

For almost all the options in the following sections, one or two of the above commandline options will be required.
These are -⁠u[ser] and -⁠pa[ssword], or -⁠tru[sted].
Optionally combined with -⁠role.
These can be supplied for every command as parameters on the commandline, or can be configured once in a pair of environment variables.

-?

This switch displays the command line options and switches.
It replaces the old method in which you had to supply an invalid switch (such as -⁠help) in order to see the list of valid ones.

-fe[tch_password] <password file name> | stdin | /dev/tty

This switch causes the password for the appropriate user to be read from a file as opposed to being specified on the command line.
The file name supplied is not in quotes and must be readable by the user running gfix.
If the file name is specified as stdin, then the user will be prompted for a password.
On POSIX systems, the file name /dev/tty will also result in a prompt for the password.

-u[ser] username

Allows the username of the SYSDBA user, or the owner of the database to be specified.
This need not be supplied if the ISC_USER environment variable has been defined and has the correct value.

-pa[ssword] password

Supplies the password for the username specified above.
This need not be supplied if the ISC_PASSWORD environment variable has been defined and have the correct value.

Up until Firebird 2, any utility which was executed with a password on the command line could result in other users of the server seeing that password using a command like ps -efx | grep -i pass.
From Firebird 2 onwards, this is no longer the case as the password on the command line can no longer be seen by the ps (or other) commands.

To define the username and password as environment variables on a Linux system:

linux> export ISC_USER=sysdba
linux> export ISC_PASSWORD=masterkey

Alternatively, on Windows:

C:> set ISC_USER=sysdba
C:> set ISC_PASSWORD=masterkey

This is very insecure as it allows anyone who can access your session the ability to perform DBA functions that you might not want to allow.

-role role

Supplies the role name to use for the username specified.

-tru[sted]

Use trusted authentication.

If you have not defined the above environment variables, some commands will not work unless you supply -⁠u[ser] and -⁠pa[ssword] on the command line.
For example:

linux> gfix -validate my_employee
linux> Unable to perform operation. You must be either SYSDBA -
or owner of the database

The line that starts with ‘Unable to perform’ above, has had to be split to fit on the page of the PDF file.
In reality, it is a single line.

However, passing the username and password works:

linux> gfix -validate my_employee -user sysdba -password masterkey

You will notice, hopefully, that some commands do not give any printed output at all.
gfix, in the main, only reports when problems are encountered.
Always check the response code returned by gfix to be sure that it worked.
However, see the caveats section below for details because it looks like the response code is always zero — at least up until Firebird 2.0.

When logging into a database on a remote server, you will always be required to pass the -⁠u[ser] and -⁠pa[ssword] parameters.

4. Shadow Files

A shadow file is an additional copy of the primary database file(s).
More than one shadow file may exist for any given database and these may be activated and de-activated at will using the `gfix`utility.

The following descriptions of activating and de-activating shadow files assume that a shadow file already exists for the database.
To this end, a shadow was created as follows:

linux> isql my_employee;
SQL> create shadow 1 manual '/home/norman/firebird/shadow/my_employee.shd1';
SQL> create shadow 2 manual '/home/norman/firebird/shadow/my_employee.shd2';
SQL> commit;
SQL> show database;
Database: my_employee
 Owner: SYSDBA
 Shadow 1: "/home/norman/firebird/shadow/my_employee.shd1" manual
 Shadow 2: "/home/norman/firebird/shadow/my_employee.shd2" manual
...
SQL> quit;

It can be seen that the database now has two separate shadow files created, but as they are manual, they have not been activated.
We can see that shadows are in use if we use gstat as follows:

linux> gstat -header my_employee | grep -i shadow
Shadow count 2

Sometimes, it takes gstat a while to figure out that there are shadow files for the database.

Shadow file details can be found in the RDB$FILES table within the database.

4.1. Activating Shadows

The command to activate a database shadow is:

gfix -ac[tivate_shadow] shadow_file_name
Before Firebird 3, the command line switch was -⁠ac[tivate].

This makes the shadow file the new database file, and the users are able to continue processing data as normal and without loss.

In the event that your main database file(s) become corrupted or unreadable, the DBA can activate a shadow file.
Once activated, the file is no longer a shadow file, and a new one should be created to replace it.
Additionally, the shadow file should be renamed (at the operating system prompt) to the name of the old database file that it replaces.

It should be noted that activating a shadow while the database itself is active can lead to corruption of the shadow.
Make sure the database file is really unavailable before activating a shadow.

Once a shadow file has been activated, you can see the fact that there are active shadows in the output from gstat:

linux> gstat -header my_employee | grep -i shadow
Shadow count 2
Attributes   active shadow, multi-user maintenance

The DBA can set up the database to automatically create a new shadow file in the event of a current shadow being activated.
This allows a continuous supply of shadow files and prevents the database ever running without one.

4.2. Killing Shadows

The command to kill all unavailable database shadows, for a specific database, is:

gfix -k[ill_shadow] database_name
Before Firebird 3, the command line switch was -⁠k[ill].

In the event that a database running with shadow files loses a shadow, or a shadow becomes unusable for some reason, the database will stop accepting new connections until such time as the DBA kills the faulty shadow and, ideally, creates a new shadow to replace the broken one.

The following (contrived) example, shows what happens when the database loses a shadow file, and an attempt is made to connect to that database.
There are two sessions in the following example, one is connected to the database while the second deletes a shadow file and then tries to connect to the database.
The command line prompts shows which of the two sessions we are using at the time.

First, the initial session is connected to the database and can see that there are two shadow files attached:

linux_1>isql my_employee
Database: my_employee
SQL> show database;
Database: my_employee
   Owner: SYSDBA
Shadow 1: "/home/norman/firebird/shadow/my_employee.shd1" manual
Shadow 2: "/home/norman/firebird/shadow/my_employee.shd2" manual
 ...

In the second session, we delete one of the shadow files, and then try to connect to the database

linux_2> rm /home/norman/firebird/shadow/my_employee.shd2
linux_2> isql_my_employee
Statement failed, SQLCODE = -901
lock conflict on no wait transaction
-I/O error for file "/home/norman/firebird/shadow/my_employee.shd2"
-Error while trying to open file
-No such file or directory
-a file in manual shadow 2 in unavailable
Use CONNECT or CREATE DATABASE to specify a database
SQL> quit;

The second session cannot connect to the database until the problem is fixed.
The DBA would use the gfix -⁠k[ill_shadow] command to remove details of the problem shadow file from the database and once completed, the second (and subsequent) sessions would be able to connect.

linux_2> gfix -kill_shadow my_employee

linux_2> isql my_employee
Database: my_employee
SQL> show database;
Database: my_employee
   Owner: SYSDBA
Shadow 1: "/home/norman/firebird/shadow/my_employee.shd1" manual
...

The database now has a single shadow file where before it had two.
It is noted, however, that gstat still shows the database as having two shadows, even when one has been removed.

linux> gstat -header my_employee | grep -i shadow
Shadow count 2
Attributes   active shadow, multi-user maintenance

In addition to the above strange result, if I subsequently DROP SHADOW 1 and COMMIT, to remove the remaining shadow file, gstat now shows that the shadow count has gone up to three when it should have gone down to zero!

5. Set Database Page Buffers

The database cache is an area of RAM allocated to store (cache) database pages in memory to help improve the efficiency of the database performance.
It is far quicker to read data from memory that it is to have to physically read the data from disc.

The size of the database cache is dependent on the database page size and the number of buffers allocated, a buffer is the same size as a database page, and whether the installation is using Classic or Superserver versions of Firebird.

In a Classic and SuperClassic Server installation, by default, each connection to the database gets its own relatively small cache of 256 pages (was 75 before Firebird 3.0) while Superserver creates a much larger cache of 8196 pages (was 2048 before Firebird 3.0) which is shared between all the connections.

Usually, page buffers value is set as parameter DefaultDbCachePages in firebird.conf (and, in databases.conf since Firebird 3.0), but it also can be set directly in the database header using gfix tool (and this setting will override firebird.conf and databases.conf, so be careful).

The command to set the number of cache pages is:

gfix -b[uffers] BUFFERS database_name

This command allows you to change the number of buffers (pages) allocated in RAM to create the database cache.

You cannot change the database page size in this manner, only the number of pages reserved in RAM.

One parameter is required which must be numeric and between 50 (the minimum) and 2147483646 (the maximum since Firebird 2.5, before it was 131072).

Please don’t consider page buffers value as an ultimate parameter to improve Firebird’s performance, and don’t set it too high without clear understanding what are you doing.

The setting applies only to the database you specify.
No other databases running on the same server are affected.

The new value of page buffers will be allocated at the first connection to the database.

There is a difference in behaviour between Firebird architectures: for SuperServer, if number of page buffers was changed while database had active connection, the old value will be used until all connections disconnect;
for Classic/SuperClassic, new connections will use the new value of page buffers immediately after the change.

The following example shows the use of gstat to read the current number of buffers, the gfix utility being used to set the buffers to 4,000 pages and gstat being used to confirm the setting.
The value of zero for page buffers indicates the default setting for the server type is in use.

You can use the gstat command line utility to display the database details with the command line: gstat -header db_name however, to run gstat, you need to be logged into the server — it cannot be used remotely.

linux> gstat -header my_employee | grep -i "page buffers"
Page buffers    0

linux> gfix -buffers 4000 my_employee

linux> gstat -header my_employee | grep -i "page buffers"
Page buffers 4000

6. Limbo Transaction Management

Limbo transactions can occur when an application is updating two (or more) databases at the same time, in the same transaction.
At COMMIT time, Firebird will prepare each database for the COMMIT and then COMMIT each database separately.

In the event of a network outage, for example, it is possible for part of the transaction to have been committed on one database but the data on the other database(s) may not have been committed.
Because Firebird cannot tell if these transactions (technically sub-transactions) should be committed or rolled back, they are flagged as being in limbo.

Gfix offers a number of commands to allow the management of these limbo transactions.

The following examples of limbo transactions are based on Firebird 1.5 and have kindly been provided by Paul Vinkenoog.
Because of the limitation of my setup, I am unable to create limbo transactions in my current location.

In the spirit of consistency, however, I have renamed Paul’s servers and database locations to match the remainder of this document.

6.1. Listing Limbo Transactions

The gfix command -⁠l[ist] will display details of transactions that are in limbo.
If there is no output, then there are no transactions in limbo and no further work need be done.
The command is:

gfix -l[ist] database_name

An example of listing limbo transactions is shown below.
This command is run against the local database on the server named linux where a multi-database transaction had been run connected to databases [email protected]_employee and remote:testlimbo.
Both of these database names are aliases.

linux> gfix -list my_employee
Transaction 67 is in limbo.
 Multidatabase transaction:
 Host Site: linux
 Transaction 67
has been prepared.
 Remote Site: remote
 Database path: /opt/firebird/examples/testlimbo.fdb

If the command is run against the remote database then nothing will be listed because that database does not have any limbo transactions — the transaction that went into limbo, when the network failed, for example, was initiated on the local database.

You may also supply the -⁠pr[ompt] option to the command, and you will be prompted to COMMIT or ROLLBACK each detected limbo transaction.
In this case, the command would be:

gfix -l[ist] -pr[ompt] database_name

An example of this is shown below.

linux> gfix -list -prompt my_employee
Transaction 67 is in limbo.
 Multidatabase transaction:
 Host Site: linux
 Transaction 67
has been prepared.
 Remote Site: remote
 Database path: /opt/firebird/examples/testlimbo.fdb
Commit, rollback or neither (c, r, or n)?

6.2. Committing Or Rolling Back

When a limbo transaction has been detected, the DBA has the option of committing or rolling back one or more of the transactions reported as being in limbo.

When more than one transaction is listed, the DBA can either commit or roll back all transactions in limbo, or a specific transaction number.

The following commands show the -⁠co[mmit] option being used, but the -⁠r[ollback] option applies as well, it all depends on what the DBA is trying to achieve.

To commit every limbo transaction on the database, the following command would be used:

gfix -co[mmit] all database_name

If the DBA wanted to commit a single transaction, then the command would change to the following:

gfix -co[mmit] TXN database_name

Where TXN is the transaction number to be committed.

When either of these options are used, there is no feedback from gfix to advise you that the commit actually worked.
You would need to rerun the gfix -⁠list command to make sure that all, or the selected, limbo transactions had indeed gone.

You cannot commit or rollback a transaction that is not in limbo.
If you try, the following will occur:

linux> gfix -commit 388 my_employee
failed to reconnect to a transaction in database my_employee
transaction is not in limbo
-transaction 388 is active
unknown ISC error 0

When committing or rolling back all limbo transactions, the -⁠pr[ompt] option can be specified.
It is, however, not permitted when processing a single transaction.
An example of using the -⁠pr[ompt] option has been shown above under listing limbo transactions.

6.3. Automatic Two-phase Recovery

Gfix can be used to perform automatic two-phase recovery.
The command for this is -⁠tw[o_phase] and, like -⁠co[mmit] and -⁠r[ollback] above, requires either all or a transaction number.

The output of the -⁠l[ist] command shows what will happen to each listed transaction in the event that the DBA runs the -⁠tw[o_phase] command.

The command also takes the -⁠pr[ompt] option, as above, when used to process all transaction.

The command line to carry out automatic two-phase recovery is:

gfix -tw[o_phase] TXN database_name

or

gfix -tw[o_phase] all database_name

As above, TXN is a single transaction number from the list of limbo transactions.

Paul has noted that when using the -⁠co[mmit], -⁠r[ollback] or -⁠tw[o_phase] options, the output is exactly the same and appears to show that these three are all just synonyms for the -⁠l[ist] -⁠pr[ompt] pair of options.
This occurred whether or not Paul used the transaction number, 67, or ‘all’ in the command line.

7. Cache Manager

When the help page for gfix is displayed there is a message in the output for the -⁠ca[che] option which states:

...
-ca[che]      shutdown cache manager
...

However, when called this option simply displays the help page again.

The question that immediately springs to my mind is, if we can shutdown the cache manager with this option, how do we start it back up again?

8. Changing The Database Mode

Databases can be set to run in one of two modes, read only — where no updates are permitted, and read/write — where both reading and writing of data is permitted.
By default, Firebird creates read/write databases and as such, all read/write databases must be placed on a file system which allows writing to take place.

Should you wish to put a Firebird database on a CD, for example, you wouldn’t be able to do so.
After a new database has been populated with data it can be changed to read only mode, and then used on a CD (or other read only file systems) with no problems.

Firebird uses SQL internally to maintain its internal structures with details about transactions, for example, and this is the reason that a database must be placed on a read/write file system regardless of whether only SELECT statements are run or not.

Only databases in dialect 3 can be changed to read only mode.

The command to set the required mode for a database is:

gfix -mo[de] MODE database_name

The command takes two parameters, the MODE which must be one of the following:

read_only

the database cannot be written to.

read_write

the database can be written to.

The meaning of the two modes should be quite meaningful.

The second parameter is a database name to apply the mode change to.

The following example shows how to put a database into read only mode, and then change it back again.
The example also shows what happens when you try to update the database while running in read only mode.

linux> gfix -mode read_only my_employee

linux> isql my_employee
Database: my_employee

SQL> create table test(stuff integer);
Statement failed, SQLCODE = -902
Dynamic SQL Error
-attempted update on read-only database

SQL> quit;

linux> gfix -mode read_write my_employee

linux> isql my_employee
Database: my_employee

SQL> create table test(stuff integer);

SQL> show table test;
STUFF      INTEGER Nullable

SQL> quit;

If there are any connections to the database in read/write mode when you attempt to convert the database to read only, the attempt will fail as shown below with Firebird 1.5.

linux> gfix -mode read_only my_employee
lock time-out on wait transaction
-lock time-out on wait transaction
-object my_employee is in use

linux> echo $?
0

As with many failures of gfix, the response code returned to the operating system is zero.

Under Firebird 2, the error message is more self-explanatory:

linux> gfix -mode read_only my_employee
lock time-out on wait transaction
-object /opt/firebird/databases/my_employee.fdb is in use

linux> echo $?
0

9. Setting The Database Dialect

The dialect of the database is simply a term that defines the specific features of the SQL language that are available when accessing that database.
There are three dialects at present (Firebird version 2.0), these are:

  • Dialect 1 stores date and time information in a DATE data type and has a TIMESTAMP data type which is identical to DATE.
    Double quotes are used to delimit string data.
    The precision for NUMERIC and DECIMAL data types is less than a dialect 3 database and if the precision is greater than 9, Firebird stores these as DOUBLE PRECISION.
    BIGINT is not permitted as a data type.

  • Dialect 2 is available only on the Firebird client connection and cannot be set in the database.
    It is intended to assist debugging of possible problems with legacy data when migrating a database from dialect 1 to 3.
    This dialect cannot be set for a database using gfix.
    (See below.)

  • Dialect 3 databases allow numbers (DECIMAL and NUMERIC data types) to be stored as BIGINT when the precision is greater than 9.
    The TIME data type is able to be used and stores time data only.
    The DATE data type stores on date information.
    Double quotes can be used but only for identifiers that are case dependent, not for string data which has to use single quotes.

The command to change the SQL dialect for a database is:

gfix -s[ql_dialect] DIALECT database_name

The DIALECT parameter is simply 1 or 3.

The following example changes a database to use dialect 3 which will allow many newer features of SQL 92 to be used.

linux> gfix -sql_dialect 3 my_employee

linux> gstat -header my_employee | grep dialect
Database dialect    3

linux> gfix -sql_dialect 1 my_employee

linux> gstat -header my_employee | grep dialect
Database dialect    1

Because you cannot use gstat remotely, you may also use the isql command SHOW SQL DIALECT from a remote location to see which dialect your client and database are using, as follows:

remote> isql my_employee -user norman -password whatever
Database: my_employee

SQL> show sql dialect;
Client SQL dialect is set to: 3 and database SQL dialect is: 3

Although dialect 2 is possible on the client, trying to set a dialect of 2 will fail on the server as the following example shows.

linux> gfix -sql_dialect 2 my_employee
Database dialect 2 is not a valid dialect.
-Valid database dialects are 1 and 3.
-Database dialect not changed.

To set dialect 2 for your client connection, you use isql as follows:

linux> isql my_employee
Database: my_employee

SQL> set sql dialect 2;
WARNING: Client SQL dialect has been set to 2 when connecting -
to Database SQL dialect 3 database.

SQL> show sql dialect;
Client SQL dialect is set to: 2 and database SQL dialect is: 3

The WARNING line above has had to be split to fit on the page of the PDF version of this manual.
In reality, it is a single line of text.

10. Database Housekeeping And Garbage Collection

10.1. Garbage

Garbage, for want of a better name, is the detritus that Firebird leaves around in the database after a rollback has been carried out.
This is basically a copy of the row(s) from the table(s) that were being updated (or deleted) by the transaction prior to the rollback.

Almost all garbage is created by committed transactions.
Since around V2.5 transactions that rollback are cleaned up immediately — assuming that Firebird is still running.

The major cause of garbage build-up is long-running transactions that require Firebird to keep old versions of records that are frequently updated.
Another source of garbage is an application strategy that deletes records and never revisits them.

What actually happens on delete is that Firebird stores a «deleted stub» with the full record as a back version.
Until the delete is mature — meaning that all active transactions started after the delete was committed — the old version must be preserved.

Imagine a table that’s indexed and accessed by date.
On some schedule, records age out and are deleted.
In the application, records are accessed by date, and the deleted records are so old no query ever asks for them.
So there they sit, taking up space and doing no good to anyone.
Even with a garbage collect thread, some active transaction has to stumble over deleted records or records with unneeded back versions before the record will be garbage collected.

In cooperative garbage collection, that particular record will be cleaned up immediately (or at least when the transaction gets some cycles).
The dedicated garbage collection thread should clean up all the records on a page, but not until an active transaction tells it that there’s a page that needs cleaning.

Because Firebird uses multi-generational architecture, every time a row is updated or deleted, Firebird keeps a copy in the database.
These copies use space in the pages and can remain in the database for some time, especially if there are no active transactions stumbling across them!

There are a number of causes of garbage:

  • Remnants from a committed transaction.
    This is the main cause of garbage since around Firebird version 2.5.

  • Remnants from an aborted (rolled back) transaction may exist in Firebird versions prior to 2.5, newer versions perform immediate clean up after a rollback however, if the Firebird Server, the Operating System or the physical server crashed, then these remnants may still exist, even in later versions of Firebird.

  • Applications, described above, which delete database records, but then, subsequently, never revisit those deleted versions to garbage collect them automatically.

With regard to the remnants from aborted or rolled back transactions, Firebird (now) carries out record-keeping to facilitate save points.
This housekeeping allows Firebird to identify and, if necessary, undo all changes made by a transaction in the event that it is rolled back, or which failed due to a lost connection.

If a failed transaction is rolled back in either manner, its state is set to committed as there are no differences between a failed transaction and one in which it committed after making no changes.

These remnants are simply older copies of the rows that were being updated by the respective transactions.
The differences are that:

  • Whenever a subsequent transaction reaches garbage from a committed transaction, that garbage is automatically cleared out, but see above for reasons where this may not take place often enough.

  • Rolled back garbage looks just like record versions created by active transactions.
    Those records can be accessed either sequentially (during a full table scan) or by index — assuming that the index entry was made before the crash that left the garbage around.
    The index entries will exist in the case of all but the last change made.
    When one transaction reads a record version created by a transaction that’s listed in the transaction bit vector as active, the reader attempts to get a lock on the apparently active transaction id.
    If the lock request succeeds, then the other transaction is dead and the reader will either clean up the mess or notify the garbage collect thread to do so.

Firebird will automatically sweep through the database and remove the remnants of rolled back transactions and this has two effects:

  • The space recovered is made available for reuse by the same table, however, if this results in the page becoming completely empty, then it can be used for any purpose within the database.

  • The performance of the database may be affected while the sweep is in progress.

Theoretically, it is possible to clear out old rolled back transactions’ garbage with a database backup by gbak tool.
Gbak reads every table sequentially and thus visits every row in every table.
Applications which also visit every row in one or more tables, will also cause the garbage in those tables to be collected.
Neither will affect the database’s OIT (Oldest Intersting Transaction) or OST (Oldest Snapshot) settings however.

In practice, better do not consider backup as an alternative for sweep, because sweep does more things than collective garbage collection.

In the Super Server version of Firebird 2.0, garbage collection has been vastly improved.
There are now three different ways of operation and these are configurable by setting the GCPOLICY parameter in the firebird.conf configuration file.
By default, Super Server uses combined while Classic Server uses cooperative.
The other option is background.

Classic Server ignores the setting and always uses cooperative garbage collection.

10.1.1. Record Versions

Normally, when a «back» or old version of a row in a table is created, it will be stored on the same page as the newest version.
This is usually fine as the back version is not normally a complete copy of the old version, merely a list of differences from the newest version.
Enough information is retained in the old version, to be able to recreate it, if necessary.

If the database is suffering from a lack of garbage collecting, either deliberately, or down to the application design, then it is possible that there will be a build up of enough back versions to fill the target page.
When the chain of old versions gets too big, Firebird has to move the old versions to a different page which, if it occurs as part of an UPDATE statement, as it normally will, the UPDATE will run a lot slower than usual and will greatly increase the cost of subsequent garbage collection against that table.

10.1.2. Cooperative Garbage Collection

This is the default setting, indeed the only setting, that Classic Server uses.
In this mode, the normal operation — as described above — takes place.
When a full scan is performed (perhaps during a backup) old versions of the rows are deleted at that point in time.
Record versions which are old enough that no active transactions have any interest in them will be removed, as will any versions created by failed transactions, if there are any present.
(Which there shouldn’t be!)

10.1.3. Background garbage Collection

Super Server has, even since before version 1.0, performed background garbage collection where the server informs the garbage collector about old versions of updated and deleted rows when they are ready to be cleaned up.
This helps avoid the need to force a full scan of each record in the database tables to get the garbage collector to remove these old versions.
An active transaction has to recognize the need for garbage collection and notify the server which puts that record id on a list for the garbage collect thread.
So an unvisited record will not attract the garbage collector unless another record on that page is read and does need cleanup.

When all rows in a table are read by the server, any old record versions are flagged to the garbage collector as being ready to be cleared out.
They are not deleted by the scanning process as in the cooperative method.
The garbage collector runs as a separate background thread and it will, at some point, remove these older record versions from the database.

10.1.4. Combined Garbage Collection

This is the default garbage collection method for Super Server installations.
In this mode, both the above methods are used together.

10.2. Setting Sweep Interval

The default sweep interval for a new database is 20,000.
The sweep interval is the difference between the Oldest Snapshot Transaction, or OST and the Oldest Interesting Transaction or OIT.

This doesn’t mean that every 20,000 transaction a sweep will take place.
It will take place when the difference between the OST and the OIT is greater than the sweep interval.

An interesting transaction is one which has not yet committed.
It may be still active, in limbo or may have been rolled back.
(Limbo transactions are never garbage collected.)

The sweep facility runs through the database and gets rid of old rows in tables that are out of date.
This prevents the database from growing too big and helps reduce the time it takes to start a new transaction on the database.

If you find that starting a new transaction takes a long time, it may be a good idea to run a manual sweep of the database in case the need for a sweep is causing the hold-up.

You can check if a manual sweep may be required by running the gstat utility to check the database header page and extract the Oldest Transaction (OIT) and Oldest Snapshot (OST) numbers from the output.
If OST — OIT is small (less than the sweep interval) then a manual sweep may be in order.
The SHOW DATABASE command in isql will also show the details you need.

Alternatively, another idea is to run gstat with the switches set to show old record versions.
If that shows a problem, then it may be a good idea to start looking for long running transactions.

The options for this are:

gstat database -r[ecord]
gstat database -d[ata] -r[ecord]
gstat database -r[ecord] -t[able] table_names

For example:

tux> gstat test.fdb -r -t NORMAN

...
Analyzing database pages ...
NORMAN (142)
    Primary pointer page: 268, Index root page: 269
    Average record length: 0.00, total records: 15
    Average version length: 9.00, total versions: 15, max versions: 1
    Data pages: 1, data page slots: 1, average fill: 16%
...

The information is shown in the ‘record versions’ statistic.
In this example, there are 15 versions and as the ‘total records’ is also 15, then all the records have been deleted and need garbage collecting.

A manual sweep can be run by using the -⁠s[weep] command.
(See below).

To alter the database’s automatic sweep interval, use the following command:

gfix -h[ousekeeping] INTERVAL database_name

The INTERVAL parameter is the new value for the sweep interval.
The database name parameter is the database upon which you wish to alter the setting for automatic sweeping.
The following example shows the setting being changed from the default to a new value of 1,000.

linux> gfix -h 1000 my_employee

linux> gstat -header my_employee | grep Sweep
Sweep interval:     1000

10.3. Manual Garbage Collection

If automatic sweeping has been turned off, or only runs rarely because of the sweep interval setting, the DBA can manually force a sweep to be performed.
The command to carry out this task is:

gfix -s[weep] [-i[gnore]] database_name

This command will force the garbage left over from old rolled back transactions to be removed, reducing the database size and improving the performance of new transactions.
Rolled back transactions are less of a problem than old versions from committed transactions, however, when the newest versions is being used by all current and future active transactions.

The -⁠i[gnore] option may be supplied.
This forces Firebird to ignore checksum errors on database pages.
This is not a good idea and should rarely need to be used, however, if your database has suffered some problems it might be necessary to use it.

Checksums have not been used for a number of years as it was found that a significant percentage of CPU was consumed by check summing to find partial page writes — none of which were ever found!

The following example shows a manual database sweep being implemented:

linux> gfix -sweep my_employee

10.4. Disabling Automatic Sweeping

If you set the sweep interval to zero then automatic sweeping will be disabled.
This implies that there will be no automatic housekeeping done so your database performance will not suffer as a result of the processing requirements of the automatic sweep.

If you disable sweeping you are advised to run a manual sweep at regular intervals when the database is quiet.
Alternatively, simply make sure you take regular backups of the database and as this is something you should be doing anyway, it shouldn’t be a problem.

11. Database Startup and Shutdown

The first part of this section describes the shutdown and startup options up to Firebird 2.0.
There is a separate section at the end which discusses the new states for starting and stopping a database using Firebird 2.0 onwards.

11.1. Database Shutdown

If there is maintenance work required on a database, you may wish to close down that database under certain circumstances.
This is different from stopping the Firebird server as the server may well be running other databases which you do not wish to affect.

The command to close a database is:

gfix -sh[utdown] OPTION TIMEOUT database_name
Before Firebird 3, the command line switch was -⁠shut.

The TIMEOUT parameter is the time, in seconds, that the shutdown must complete in.
If the command cannot complete in the specified time, the shutdown is aborted.
There are various reasons why the shutdown may not complete in the given time, and these vary with the mode of the shutdown and are described below.

The OPTION parameter is one of the following:

-⁠at[tach]

prevents new connections.

-⁠tr[an]

prevents new transactions.

-⁠fo[rce_shutdown]

simply aborts all connections and transactions.

Before Firebird 3, the command line switch was -⁠f[orce].

When a database is closed, the SYSDBA or the database owner can still connect to perform maintenance operations or even query and update the database tables.

If you specify a long time for the shutdown command to complete in, you can abort the shutdown by using the -⁠o[nline] command (see below) if the timeout period has not completed.

11.1.1. Preventing New Connections

-⁠at[tach] prevents any new connections to the database from being made with the exception of the SYSDBA and the database owner.
The shutdown will fail if there are any sessions connected after the timeout period has expired.
It makes no difference if those connected sessions belong to the SYSDBA, the database owner or any other user.
Any connections remaining will terminate the shutdown with the following details:

linux> gfix -shut -attach 5 my_employee
lock conflick on no wait transaction
-database shutdown unsuccessful

Anyone other than the SYSDBA or database owner, attempting to connect to the database will see the following:

linux> isql my_employee -user norman -password whatever
Statement failed, SQLCODE = -901
database my_employee shutdown
Use CONNECT or CREATE DATABASE to specify a database
SQL>

Connections in the database will still be able to start new transactions or complete old ones.

11.1.2. Preventing New Transactions

-⁠tr[an] prevents any new transactions from being started and also prevents new connections to the database.
If there are any active transactions after the timeout period has expired, then the shutdown will fail as follows:

linux> gfix -shut -tran 5 my_employee
lock conflick on no wait transaction
-database shutdown unsuccessful

If any user connected to the database being shutdown with the -⁠tr[an] tries to start a new transaction during the shutdown timeout period, the following will result:

SQL> select * from test;
Statement failed, SQLCODE = -902
database /home/norman/firebird/my_employee.fdb shutdown in progress
Statement failed, SQLCODE = -902
database /home/norman/firebird/my_employee.fdb shutdown in progress
Statement failed, SQLCODE = -901
Dynamic SQL Error
-SQL error code = -901
-invalid transaction handle (expecting explicit transaction start)

11.1.3. Force Closure

-⁠fo[rce_shutdown] shuts down with no regard for the connection or transaction status of the database.
No new connections or transactions are permitted and any active sessions are terminated along with any active transactions.

Anyone other than SYSDBA or the database owner trying to connect to the database during the timeout period will not be able to connect successfully or start any (new) transactions.

Be nice to your users, use the -⁠fo[rce_shutdown] option with great care.

There is a bug in Classic Server which still exists at version 2.0.
The bug is such that the -⁠f[orce] option behaves in exactly the same way as the -⁠at[tach] option.

11.2. Starting a Database

Once all maintenance work required on a database has been carried out, you need to restart the database to allow normal use again.
(See shutdown option above for details of closing a database.)

The -⁠o[nline] command allows a database to be restarted.
It takes a single parameter which is the database name as follows:

gfix -o[nline] database_name

The following example shows a closed database being started.

linux> gfix -online my_employee

11.3. New Startup and Shutdown States in Firebird 2.0

The above discussion of stopping and starting a database apply to all versions of the server up to version 2.0.
From 2.0 the commands will work as described above, but a new state has been added to define exactly how the database is to be stopped or started.
The commands change from those described above to the following:

gfix -sh[utdown] STATE OPTION TIMEOUT database_name

gfix -o[nline] STATE database_name

STATE is new in Firebird 2.0 and is one of the following:

normal

This is the default state for starting the database backup.
It allows connections from any authorised users — not just SYSDBA or the database owner.
This option is not accepted for shutdown operations.

multi

This is the default mode as described above.
When the database is shutdown as above, or using the multi state, then unlimited connections can be made by the SYSDBA or the database owner.
No other connections are allowed.

single

Similar to the multi option above, but only one SYSDBA or database owner connection is allowed.

full

Shutdown and don’t allow any connections from anyone, even SYSDBA or the database owner.
This is not an acceptable option for starting up a database.

There is no leading dash for the state parameters, unlike the command itself and the -⁠shut OPTION.

There is a hierarchy of states for a database.
The above list shows them in order with normal at the top and full at the bottom.

This hierarchy is important, you cannot shutdown a database to a higher or equal level that it currently is, nor can you startup a database to a lower or equal level.

If you need to identify which level a database is currently running at, gstat will supply the answers.
The following example puts a database fully online then progressively shuts it down to fully offline.
At each stage, gstat is run to extract the Attributes of the database.

linux> gfix -online normal my_employee
linux> gstat -header my_employee | grep Attributes

        Attributes

linux> gfix -shut multi -attach 0 my_employee
linux> gstat -header my_employee | grep Attributes

        Attributes              multi-user maintenance

linux> gfix -shut single -attach 0 my_employee
linux> gstat -header my_employee | grep Attributes

        Attributes              single-user maintenance

linux> gfix -shut full -attach 0 my_employee
linux> gstat -header my_employee | grep Attributes

        Attributes              full shutdown

linux>

12. Database Page Space Utilisation

Firebird reserves some space on the data page for the possible future updates or deletes of records on this page, to reduce possible fragmentation.
The amount of reserved space can be approximately considered as a number of records, multiplied by the record header size.
Therefore, for many very small records, reserve can be close to 50% of data page, and for the single huge record can be 22 bytes or so (depends on the version of On-Disk Structure of the database).

If you wish to use all the available space in each database page, you may use the -⁠u[se] command to configure the database to do so.
If you subsequently wish to return to the default behaviour, the -⁠u[se] command can be used to revert to leaving 20% free space per page.

Once a page has been filled to ‘capacity’ (80 or 100%) changing the page usage setting will not change those pages, only subsequently written pages will be affected.

The -⁠u[se] command takes two parameters as follows:

gfix -u[se] USAGE database_name

The USAGE is one of:

full

use 100% of the space in each database page.

reserve

revert to the default behaviour and only use 80% of each page.

The following example configures a database to use all available space in each database page:

linux> gfix -use full my_employee
linux> gstat -header my_employee | grep Attributes
Attributes no reserve

The following example sets the page usage back to the default:

linux> gfix -use reserve my_employee
linux> gstat -header my_employee | grep Attributes
Attributes

If you are using full page utilisation then the Attributes show up with ‘no reserve’ in the text.
This doesn’t appear for normal 80% utilisation mode.

13. Database Validation and Recovery

13.1. Database Validation

Sometimes, databases get corrupted.
Under certain circumstances, you are advised to validate the database to check for corruption.
The times you would check are:

  • When an application receives a database corrupt error message.

  • When a backup fails to complete without errors.

  • If an application aborts rather than shutting down cleanly.

  • On demand — when the SYSDBA decides to check the database.

Database validation requires that you have exclusive access to the database.
To prevent other users from accessing the database while you validate it, use the gfix -⁠sh[utdown] command to shut down the database.

When a database is validated the following checks are made and corrected by default:

  • Orphan pages are returned to free space.
    This updates the database.

  • Pages that have been misallocated are reported.

  • Corrupt data structures are reported.

There are options to perform further, more intensive, validation and these are discussed below.

13.1.1. Default Validation

The command to carry out default database validation is:

gfix -v[alidate] database_name

This command validates the database and makes updates to it when any orphan pages are found.
An orphan page is one which was allocated for use by a transaction that subsequently failed, for example, when the application aborted.
In this case, committed data is safe but uncommitted data will have been rolled back.
The page appears to have been allocated for use, but is unused.

This option updates the database and fixes any corrupted structures.

13.1.2. Full Validation

By default, validation works at page level.
If no need to go deeper and validate at the record level as well, the command to do this is:

gfix -v[alidate] -fu[ll] database_name

using this option will validate, report and update at both page and record level.
Any corrupted structures etc will be fixed.

13.1.3. Read-only Validation

As explained above, a validation of a database will actually validate and update the database structures to, hopefully, return the database to a working state.
However, you may not want this to happen and in this case, you would perform a read only validation which simply reports any problem areas and does not make any changes to the database.

To carry out a read only validation, simply supply the -⁠n[o_update] option to whichever command line you are using for the validation.
To perform a full validation, at record and page level, but in reporting mode only, use the following command:

gfix -v[alidate] -fu[ll] -n[o_update] database_name

On the other hand, to stay at page level validation only, the command would be:

gfix -v[alidate] -n[o_update] database_name

13.1.4. Ignore Checksum Errors

Checksums are used to ensure that data in a page is valid.
If the checksum no longer matches up, then it is possible that a database corruption has occurred.
You can run a validation against a database, but ignore the checksums using the -⁠i[gnore] option.

This option can be combined with the -⁠n[o_update] option described above and applies to both full and default validations.
So, to perform a full validation and ignore checksums on a database, but reporting errors only, use the following command:

gfix -v[alidate] -fu[ll] -i[gnore] -n[o_update] database_name

Alternatively, to carry out a page level validation, ignoring checksum errors but updating the database structures to repair it, the command would be:

gfix -v[alidate] -i[gnore] database_name

Ignoring checksums would allow a corrupted database to be validated (unless you specify the -⁠n[o_update] option), but it is unlikely the recovered data would be usable, if at all, present.

13.2. Database Recovery

If the database validation described above produces no output then the database structures can be assumed to be valid.
However, in the event that errors are reported, you may have to repair the database before it can be used again.

13.2.1. Recover a Corrupt Database

The option required to fix a corrupted database is the gfix -me[nd] command.
However, it cannot fix all problems and may result in a loss of data.
It all depends on the level of corruption detected.
The command is:

gfix -me[nd] database_name
Before Firebird 3, the command line switch was -⁠m[end]

This causes the corruptions in data records to be ignored.
While this sounds like a good thing, it is not.
Subsequent database actions (such as taking a backup) will not include the corrupted records, leading to data loss.

13.2.2. Practical tips for databases recovery

In practice, to recover database it is necessary to use gfix and gbak tools.

First of all, before the recovery, it is necessary to create a file copy of the corrupted database file, to be able to repeat recovery procedure with some variations.

Then, run gfix full validation command with disabled cheksums validation:

gfix -v[alidate] -fu[ll] -i[gnore] database_name

After that, run mend, also with disabled checksum validation:

gfix -me[nd] -i[gnore] database_name

In case of a corruption, gfix returns the summary of errors found:

  • Number of record level errors — number of corrupted records found during gfix work.
    These records are not correct — essentially, lost.

  • Number of index page errors — number of index pages in bad indices.
    When even the only key is incorrect in index, gfix marks whole index as bad, so number of pages usually is high.
    However, since it does not affect user’s data, and due to the fact that corrupted indices will be recreated during backup/restore, this can be considered as for your information only.

  • Number of transaction page errors — number of transaction pages which were fixed by gfix.
    Usually if you see this message it means that gfix did its job and now transactions are Ok.

  • Number of BLOB errors — number of bad BLOB pages, it indicates number of bad BLOBs.

  • Number of database page errors — this is the overall number of database pages, which were visited and changed/marked as bad by gfix.
    Again, this is mostly for your information.

Gfix prints the detailed information about found errors to the firebird.log

After that, it is necessary to do backup and restore of the corrupted database using gbak command:

gbak -backup -ignore database_name  backup1.fbk

gbak -create  backup1.fbk new_database_name

If backup and restore with gbak completed successfully, the database is recovered.

Since Firebird 3.0, gbak tools has very useful option: -SKIP_D[ATA].
It allows to specify comma-separated list of tables to be skipped from the backup:

gbak -b -skip_data country,currency  localhost/3050:C:Datainventory.fdb C:data111.gbk  -user SYSDBA -pass masterkey

For recovery purposes, it allows to exclude tables with reported corruptions from the backup.

If the process above did not fix the corruption, or error still persists, consider the following alternatives:

  • If database is readable, pump data from the corrupted database to the empty database with the same structure

  • Use third-party Firebird recovery tools

  • Revert to the last backup

The best way to avoid data loss is to make sure that you have enough regular backups of your database and to regularly carry out test restorations.
There is no point taking backups every night, for example, if they cannot be used when required.
Test always and frequently.

Equally, when attempting to recover a potentially corrupted database, always work with a copy of the main database file and never with the original.
Using the -⁠mend option can lead to silent deletions of data because gfix doesn’t care about internal database constraints like foreign keys etc, the -⁠mend option simply says to gfixgo ahead and clean out anything you don’t like”.

14. Database Write Mode

Many operating systems employ a disk cache mechanism.
This uses an area of memory (which may be part of your server’s overall RAM or may be built into the disk hardware) to buffer writes to the hardware.
This improves the performance of applications that are write intensive but means that the user is never certain when their data has actually been written to the physical disc.

With a database application, it is highly desirable to have the data secured as soon as possible.
Using Firebird, it is possible to specify whether the data should be physically written to disc on a COMMIT or simply left to the operating system to write the data when it gets around to it.

To give the DBA or database owner full control of when data is written, the gfix -w[rite] command can be used.
The command takes two parameters:

gfix -w[rite] MODE database_name

The MODE parameter specifies whether data would be written immediately or later, and is one of:

sync

Data is written synchronously.
This means that data is flushed to disc on COMMIT.
This is safest for your data.

async

Data is written asynchronously.
The operating system controls when the data is actually written to disc.

If your system is highly robust, and protected by a reliable UPS (Uninterruptible Power Supply) then it is possible to run asynchronously but for most systems, synchronous running is safest this will help prevent corruption in the event of a power outage or other uncontrolled shutdown of the server and/or database.

Firebird defaults to synchronous mode (forced writes enabled) on Linux and all recent Windows versions.

This command has no effect on Windows 95, 98 and ME.

Cache flushing on Windows servers (up to but not including Vista — which has not been confirmed yet) is unreliable.
If you set the database to async mode (forced writes disabled) then it is possible that the cache will never be flushed and data could be lost if the server is never shutdown tidily.

If your database was originally created with Interbase 6 or an early beta version of Firebird, then the database will be running in asynchronous mode — which is not ideal.

15. Close Database Ignoring Linger Setting

Firebird 3 introduced the database “linger” capability, which allows the SuperServer database engine to keep a database file (and its page cache, etc) open for a configured number of seconds after the last connection was closed.
This is generally beneficial for performance, but not ideal for situations where a database needs a forced shutdown, and the file needs to be accessed by applications other than Firebird.

When the database has no connections, the -⁠nol[inger] option will close the database file immediately.
The LINGER setting of the database is retained and will work normally for the next connection.

gfix -nol[inger] database_name
The `-⁠nol[inger] option was introduced in Firebird 3.

16. Rebuild ICU dependencies

Firebird uses the ICU library for some of its collations, and for indices on fields using those collations.
When moving a database between different Firebird instances — without using backup and restore — and the destination Firebird instance uses a different ICU version, querying on fields with those collations or using those indices will fail.
A similar situation can occur — on Linux — when an operating system update updated the ICU version, and Firebird uses the ICU version provided by the OS.
Before Firebird 3, the only option was to backup and restore the database.

The -⁠icu option will update collations and rebuild dependent indices in a database.

The -⁠icu option was introduced in Firebird 3.

17. Version Number

The -⁠z option to gfix simply prints out the version of the Firebird utility software that you are running.
It takes no parameters as the following example (running on Linux) shows.

linux> gfix -z
gfix version LI-V2.0.0.12748 Firebird 2.0

18. Caveats

This section summarises the various problems that you may encounter from time to time when using gfix.
They have already been discussed above, or mentioned in passing, but are explained in more details here.

18.1. Shadows

The gstat seems to take some time to respond to the addition of shadow files to a database.
After adding two shadows to a test database, gstat still showed that there was a Shadow count of zero.

Even worse, after killing the second shadow file and running the DROP SHADOW command in isql to remove the one remaining shadow file, gstat decided that there were now three shadow files in use.

18.2. Response Codes Are Usually Zero

Even using Firebird version 2 it appears that many commands, which fail to complete without an error, return a response of 0 to the operating system.

This problem was fixed in Firebird 2.1 RC1.
It has been tested and a successful operation returns zero to the shell while a failure returns 1.

This section will remain in the manual as there are still a large number of users with older versions of Firebird.

For example, the following shows two attempts to shut down the same database, the second one should fail — it displays an error message — but still returns a zero response to the operating system.
This makes it impossible to built correctly error trapped database shutdown scripts as you can never tell whether it actually worked or not.

linux> gfix -shut -force 5 my_employee
linux> echo $?
0

linux> gfix -shut -force 5 my_employee
Target shutdown mode is invalid for database -
"/home/norman/firebird/my_employee.fdb"
linux> echo $?
0

As mentioned above, this is no longer a problem from release 2.1 RC1 onwards.
The second attempt to close the database will correctly return 1 to the shell.

18.3. Force Closing a Database

Under classic server, using the -⁠fo[rce_shutdown] option to the -⁠sh[utdown] command acts exactly the same as the -⁠at[tach] option.

18.4. Limbo Transactions

There are a couple of problems with limbo transactions as discovered by Paul in his testing.

18.4.1. Limbo Transaction Options — All The Same?

When processing limbo transactions, it appears under Firebird 1.5 at least, that the -⁠l[ist] -⁠pr[ompt] option is called regardless of whether you use -⁠co[mmit], -⁠r[ollback] or -⁠tw[o_phase].
The outcome is the same regardless of whether the DBA specifies a specific transaction number or ‘all’ on the command line — a prompt is given with the option to commit, rollback or neither.

18.4.2. Limbo Transactions — Can Be Backed Up

Paul’s testing of limbo transactions revealed that it is possible to make a backup of a database with limbo transactions.
This backup can then be used to create a new database and the limbo transactions will still be able to be listed.
This applies to a file system copy of the database and to version 1.5 of Firebird.

If you attempt to list the limbo transactions in the copy database and the original database has been deleted, renamed or has been set to read-only, then gfix will present you with a request to supply the correct path to the original database

linux>cd /home/norman/firebird
linux>cp my_employee.fdb my_new_employee.fdb

linux> mv my_employee.fdb my_old_employee.fdb

linux> gfix -list /home/norman/firebird/my_new_employee.fdb
Transaction 67 is in limbo.
Could not reattach to database for transaction 67.
Original path: /home/norman/firebird/my_employee.fdb

Enter a valid path: /home/norman/firebird/my_old_employee.fdb

 Multidatabase transaction:
 Host Site: linux
 Transaction 67
has been prepared.
 Remote Site: remote
 Database path: /opt/firebird/examples/testlimbo.fdb

In the above example, the original database my_employee.fdb was first of all copied using the operating system command cp to my_new_employee.fdb and then renamed to my_old_employee.fdb.

Gfix was then run on the copy named my_new_employee.fdb and it noted the limbo transaction.
However, it could not find the original database file as it had been renamed, so gfix prompted for the path to the original database file.
When this was entered, gfix happily listed the details.

This implies that if you have a database with limbo transactions and you copy it using the operating system utilities and subsequently run gfix against the new database, it is possible to have gfix fix limbo transactions in the original database file and not in the one you think it is updating — the copy.

It is also a good warning about making copies of databases without using the correct tools for the job.

Appendix A: Document history

Revision History

1.0

19 Jun 2007

ND

Created as a chapter in the Command Line Utilities manual.

1.1

20 Oct 2009

ND

More minor updates and converted to a stand-alone manual.

1.2

25 Jun 2010

ND

Fixed spacing on a couple of lists.
Added an enhancement to the details of the -⁠mend recovery option.
It can lead to a loss of data.

1.3

11 Oct 2011

ND

Spelling errors corrected.

Updated for Firebird 2.5.

1.4

09 Apr 2013

ND

Updated to note that gfix returns correct error codes to the shell from release 2.1 RC1 onwards.

1.5

13 Feb 2018

ND

DOC-129 — Updated to correct details of the Sweep Interval and how to check what the current interval is.

1.6

21 Nov 2019

ND

Updated the Garbage section to better explain garbage causes etc.
Courtesy of Ann Harrison.

1.7

06 Jan 2020

AP

Fixed repository location from CVS to Github

1.8

19 Jun 2020

MR

Conversion to AsciiDoc, minor copy-editing

1.9

25 Jul 2020

AK

Changed description for gfix -⁠buffers, gfix -⁠reserve, gfix recovery, and some small fixes.

2.0

13 Nov 2020

MR

  • Documented new options introduced in Firebird 3: -⁠tru[sted], -⁠role, -⁠nol[inger], and -⁠icu.

  • Updated usage instruction printout with gfix of Firebird 3.0.7

  • Updated commandline option abbreviations based on gfix of Firebird 3.0.7

  • Added word-joiner to commandline options in text to prevent linewraps inside options.

  • Misc. copy-editing

Appendix B: License notice

The Original Documentation is titled Firebird Database Housekeeping Utility.

The Initial Writer of the Original Documentation is: Norman Dunbar.

Copyright © 2007–2019.
All Rights Reserved.
Initial Writer contact: NormanDunbar at users dot sourceforge dot net.

Contributor(s): Alexey Kovyazin

Понравилась статья? Поделить с друзьями:
  • Проверка базы 1с на ошибки через конфигуратор
  • Проверка англоязычного текста на ошибки
  • Проверка английского текста на ошибки и грамматику
  • Проверка айфона на ошибки и неисправности через пк
  • Проверка авто на наличие ошибок