Ibexpert проверка базы данных на ошибки

<< Trace and audit | IBExpert | Database Statistics >>

Firebird and InterBase databases are renowned for their stability. However there are a few things that could potentially lead to problems. Database validation involves checking the database file to ensure that the various data structures retain their integrity and internal consistency. The validation process checks for three different types of problems:

  • Corrupt data structures: for example, if a database row spans more than one page and the pointer that links the first page to the second is damaged or missing, there is a corrupt data structure. Firebird/InterBase is able to correct this situation, but the damaged row might be lost.
  • Misallocated data pages: for example, a page can be used for transaction inventory, header information, data, blob pointers, or indices. If a page has been flagged as one type, but actually stores data of another type, Firebird/InterBase detects the problem. However Firebird/InterBase cannot recover from this type of problem, so it will probably be necessary to restore from a backup.
  • Orphaned data pages, which are automatically returned to the free space pool. By default, Firebird/InterBase does not completely fill data pages with records, to allow space for new records to be quickly inserted. As records are added and deleted, some pages are likely to end up with no active records on them. Older Firebird/InterBase versions do not automatically reallocate these pages to the free space pool.

The IBExpert Database Validation menu item offers those options also available in the Firebird/InterBase GFIX.

It is advisable to back up the database before validating. If possible it should also be shut down, so that the backup can be restored if necessary without any loss of transactions which may have been performed since the backup.

The Database Validation menu item can be found in the IBExpert Services menu. It enables the database to be validated and verifies the integrity of data structures.

Before starting the validation process you need to close the connection to the database with IBExpert (right click Disconnect… in the DB Explorer tree) as Firebird/InterBase needs exclusive access to a database for fixing any errors. Select the registered database to be validated. The following options are none other than the GFIX parameters and may be specified as wished:

  • Limbo Transactions: If this option is checked, the database is checked for transactions in limbo, i.e. transactions, that can’t be defined as executed or aborted. Please refer to Transactions in limbo for further information.
  • Check Database: This option validates the database, but doesn’t repair it.
  • Ignore Checksums: This option ignores all checksum errors. A checksum is a page-by-page analysis of data to verify its integrity. A bad checksum means that a database page has been randomly overwritten (for example, due to a system crash).
  • Kill Shadows: This option kills all unavailable shadow files.
  • Mend Database: This prepares a corrupt database for backup and repairs any database corruption if possible.
  • Sweep Database: This option can be checked to perform a database sweep (see database sweep for more information about sweeps).
  • Validate Database: (default value). This option validates the database structure.
  • Validate Full: This validates record fragments. Note: This feature is not available in InterBase versions older than the version 6.
  • Output: Check Verbose to receive an extended report about the current database validation process. Select whether this report should be displayed on screen or saved to file (not forgetting of course to specify drive, path and file name).

Then start the database validation using the green arrow icon or [F9]. You will be asked to log in

before the validation is started.

Output

If no corruption is detected, a message is displayed informing that no database validation errors were detected. If corruption is detected that can be repaired, a report is displayed showing the number and types of errors found. Note that sometimes irreparable database corruption is found, such as damage to the database header or space allocation tables.

Please refer to Database Corruption for further information concerning the recovery of corrupt databases.

See also:
Database repair
Database properties
Firebird Database Housekeeping Utility: Database validation and recovery
How to analyse and repair a corrupted database
Backup database
Restore database
Database corruption
GFIX
GBAK and GSPLIT
Forced writes
Firebird for the database expert: Episode 3 — On Disk Consistency
Preventing data loss
Alternative database repair methods
Detect and avoid database errors
Tracking down crashes on Win32 systems
Tracking down crashes on Linux

back to top of page
<< Trace and audit | IBExpert | Database Statistics >>

<< Database page space utilisation | Firebird Database Housekeeping Utility | Database write mode >>

tutorial available

Database validation and recovery

  1. Database validation
    1. Default validation
    2. Full validation
    3. Read-only validation
    4. Ignore checksum errors
  2. Database recovery
    • Recover a corrupt database

Database validation and recovery

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.

Note: 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 -shut command to shutdown 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.

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.

Full validation

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

 gfix -v[alidate] -full database_name

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

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] -full -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

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] -full -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 that the recovered data would be usable, if at all, present.

back to top of page

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.

Recover a corrupt database

The option required to fix a corrupted database is the gfix -m[end] 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 -m[end] database_name

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.

Important: 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 gfix go ahead and clean out anything you don’t like.

See also:
Database validation
Database corruption
How to analyse and repair a corrupted database

back to top of page
<< Database page space utilisation | Firebird Database Housekeeping Utility | Database write mode >>

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 г. 

Ссылки

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

ВАЖНО! Перед началом восстановления убедитесь что все пользователи отключены от сервера и сделайте копию файла базы данных. Если нет доступа ко всем компьютерам в сети на которых запущены приложения подключенные к базе данных, то на сервере запретите (сделайте неактивным) сетевое подключение или просто достаньте сетевой кабель из розетки на системном блоке. Подождите несколько минут пока сервер не закроет все неактивные коннекты и перезагрузите операционную систему.

Войдите в каталог Bin в папке, куда был установлен сервер Interbase/Firebird/Yaffil[1]. Для того, чтобы не работать с «голым» окном командной строки, рекомендуется использовать любую файловую утилиту вроде Far или Total Commander.

Проверим базу данных на наличие повреждений:

gfix -v -full -user SYSDBA -pas masterkey database.gdb

вместо gdbase.gdb укажите полный путь к своему файлу базы данных (хорошая идея: для того чтобы не обременять себя вводом длинного пути, скопировать файл базы данных непосредственно в каталог BIN). Имя сервера указывать не надо!

Если утилита отработала и не выдала ничего на экран, то с базой все нормально.

Если есть повреждения, то попытаемся исправить их:

gfix -mend -full -ignore -user SYSDBA -pas masterkey database.gdb

Проверим, исправились ли все повреждения:

gfix -v -full -user SYSDBA -pas masterkey database.gdb

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

gbak -b -v -ig -g -user SYSDBA -pas masterkey server:database.gdb database.gbk

Здесь применены следующие ключи:

  • -b — создавать архивную копию базы;
  • -v — выводить на экран подробный лог;
  • -ig — игнорировать ошибки в данных;
  • -g — запретить сборку мусора при чтении из базы.
gbak -c -v -user SYSDBA -pas masterkey database.gbk server:new.gdb

Обратите внимание, что при указании имени базы данных необходимо указать имя сервера и через двоеточие полный путь к файлу базы данных на сервере (обратите внимание, что если вы даже скопировали файл базы данных в одну папку с утилитой резервного копирования все равно необходимо указать полный путь к файлу). При указании имени архива следует указать только полный путь к файлу без указания имени сервера.

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

Спасти базу можно следующим образом: сначала восстановить ее без внешних ссылок (индексов) с помощью команды:

gbak -c -i -user SYSDBA -pas masterkey database.gbk server:new.gdb

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

Если база повреждена настолько, что одной деактивации индексов недостаточно, то можно попробовать ключи -n (отключение проверок целостности данных) и -o (комит данных после каждой таблицы при восстановлении).

Пример команды с вышеупомянутыми ключами:

gbak -c -i -n -o -user SYSDBA -pas masterkey database.gbk server:new.gdb

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

См. также

  • Пример ремонта поврежденной БД

Примечания

  1. По умолчанию, сервер Yaffil устанавливается в папку C:/Program Files/Yaffil.

Добавил admin | Категория Заметки про IBExpert, Firebird | 14 Декабря 2013


В прошлой статье мы с вами научились создавать резервную копию нашей с вами базы данных, в этой статье мы рассмотрим восстановление данных из нашей архивной копии базы данных. И так для того что бы запустить инструмент восстановления данных в IB Expert нужно запустить IB Expert, подключится к необходимой БД, или не подключаться если нужно создать новую БД. Давайте попробуем создать новую БД, из имеющейся у нас архивной копии нашей базы данных. Для этого после того как вы запустили утилиту необходимо в меню «Службы» выбрать «Восстановление данных», появится окно восстановления данных.

Окно восстановления данных в IB Expert выглядит следующим образом:

Параметр Restore into определяет будем ли мы переписывать существующею БД или создадим новую базу. Если параметр имеет значение «Переписать существующую» то параметр Select database будет показывать полный путь к файлу существующей базы данных.

Так как нас интересует создание базы данных из нашего архива, или другими словами восстановление чистой базы данных. Мы укажем параметры как на рисунке ниже:

Параметр Restore into указываем в «Новую базу», параметр Database File отвечает за сохранение нашего нового файла базы данных. Выбираем новое место и имя для файла воспользовавшись кнопкой после чего нажимаем на что бы стал доступен File Name path после чего нажимаем на и выбираем нашу резервную копию. После чего мы можем указать дополнительные параметры, обычно для стандартного восстановления БД параметры стоят по умолчанию поэтому нам остается нажать кнопку Start restore и появится окно подключения к серверу Firebird, тут нам нужно ввести пароль и имя пользователя, роль вводить необязательно так как если не указана роль сервер берет первую назначенную для данного логина и подставляет ее в строку соединения. 

После того как введен логин и пароль жмем ок и восстановление БД запускается, результат восстановления отобразится на странице «протокол» окна восстановления БД.

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

Понравилась статья? Поделить с друзьями:
  • Ibexpert ошибка gds32 dll
  • Ibb 502 ошибка
  • Iaudioclient initialize ошибка aimp
  • Iastorvd sys ошибка при установке windows 7
  • Iastorafs sys 0xc0000098 ошибка