длина файла базы не кратна длине блока 0х1000 что делать
1С 8.3 : Опыт восстановления файловой базы 1Cv8.1CD
Именно 13-го июня в первый рабочий день база и слетела. Прямо с утра. При запуске пишет: «Файл базы данных поврежден. 1cv8.1CD» и все тут. Ни в конфигуратор ни в предприятие не пускает.
Последний бэкап понятно как обычно старый, ибо при последнем обновлении 1С рабочую базу перенесли в другую папку, которая соответственно в архив не попадала.
В общем вот исходные данные:
2. убитый файл 1Сv8.1CD весом 900 МБ датой от 12.06.2012;
3. рабочий файл 1Сv8.1CD весом 900 МБ датой от 26.04.2012;
На уровне подсознания понятно что из этого что то можно получить но пока не ясно как.
ИТАК:
Еще до поиска в Сети пришла в голову мысль воспользоваться стандартной утилитой 1С CHDBFL.EXE для проверки и исправления файла базы.
Ладно, заменяем жертву эксперимента файлом из «резервного хранилища».
Теперь читаем статью по формату 1Cv8.1CD и проникаемся. Ага, теперь более-менее понятно для чего и как можно использовать программу tool_1CD. Запускаем 2 экземпляра:
Ну вот теперь мы знаем что файл 1Cv8.1CD структурирован и хранит в себе описание и содержимое всех таблиц, а в начале файла есть основная секция где указано размещение этих таблиц.
Тут нам без HEX-редактора не обойтись. А сейчас что-то мало бесплатных то ((((. А у меня еще с давних темных времен припасена коллекция редакторов и дебаггеров. Уж и не помню для чего)))).
Для тех, кто внимательно прочитал статью не секрет, что блок, где хранится размещение таблиц №2 и найти его можно по смещению 0х4000:
Так же видим что смещения одинаковы в обоих файлах. Это значит, что все вообще просто:
1. идем по указанному смещению в целом файле;
2. выделяем полностью фрагмент кода с начального смещения данной таблицы до начального смещения следующей;
3. копируем с заменой в убитый файл точно на те же адреса.
4. сохраняем изменения в бывшем убитом файле.
5. проверяем tool_1CD что таблицы появились. Прога ругаться может на индексы, но они после восстановятся.
6. (по своему усмотрению) прогоняем утилитой CHDBFL.EXE (она там поругается немного, можно не обращать внимания).
Все. Время принимать поздравления и обещания расцеловать от бухгалтерии и наставления от начальства по поводу необходимости ежедневного архивирования. В который раз ))))).
опять «Ошибка формата потока»
Описание проблемы: при открытии базы, как в режиме Конфигуратора, так и в режиме Предприятия возникает «Ошибка потока формата» с предложением закрыть или перезапустить. Информационная база файловая, находится на локальном диске, версия платформы 8.2.18.109.
Ошибка возникла при следующих обстоятельствах: база была открыта в режиме конфигуратора, снята с поддержки и просматривался макет какого-то документа, потом бухгалтеру понадобилось закрыть 1С, конфигуратор спросил обновить ли конфигурацию, т.к. были внесены изменения, на что она ответила Да. После этого базу уже не удалось открыть.
Подобная ситуация («Ошибка формата потока») подробно и неоднократно описана на многих ресурсах, в том числе на инфостарте, так что в первую очередь были опробованы «простые» способы решения: удаление/добавление базы из списка, перенос на другую машину, прогон chdbfl, которые ни к чему не привели, chdbfl выявляла ошибку
, при попытке исправления которой chdbfl выкидывала из базы около 100 Мб, писала что ошибок не обнаружено, но проблему не исправляла.
Попробовал следующий вариант, через «Tool1c» выгрузил конфигурацию БД и загрузил ее в пустую информационную базу, база открылась без проблем (что говорит о том что повреждения не в системных таблицах, хотя могу ошибаться), потом из испорченной базы сделал Экспорт таблиц данных и импортировал их в пустую базу в которую была загружена конфа из поврежденной базы. Так же в базу была подгружена таблица V8USERS.
После этих действий при попытке загрузить базу в режиме предприятия появляется ошибка
В конфигуратор заходит, но при попытке сделать Тестирование и исправления возникает та же ошибка (Ошибка СУБД), а при реструктуризации таблиц возникает
Подскажите как локализовать возникновение ошибки, возможно ли восстановление работоспособности, даже с частичной потерей данных. Нет ни одного более менее актуального бэкапа базы.
Дополнение: При открытии через tool1c базы с импортрованными таблицами данных в логах возникает сообщение «Длина таблицы не кратна длине записи», версия tool1c 0.3.0 alpha, с возможностью редактирования базы.
Восстановление базы 1С Предприятие (DBF) после форматирования
Как выяснилось, резервная копия данной базы у клиента более, чем годовой давности.
Первый этап в решении подобных задач – это создание поблочной копии оригинального накопителя (или как принято писать со времен, когда носителями были только накопители на гибких и жестких магнитных дисках – посекторной). При вычитывании обнаруживается нестабильная скорость чтения, что говорит о серьезном износе NAND памяти (многократное чтение NAND контроллером страниц NAND памяти и коррекция ошибок за счет избыточности кодов коррекции ошибок (ECC ) весьма ресурсоемкая операции, что в итоге влияет на скорость чтения). При наличии непрочитанных участков необходимо заполнить их паттерном, который в дальнейшем нам поможет идентифицировать файлы, которые не были вычитаны целиком.
Далее приступаем к анализу. Необходимо установить, какая файловая система и в каких границах ранее была на USB flash. То есть, необходимо выполнить поиск регулярных выражений, характерных для различных метаданных файловых систем, но прежде, чем его начать, проверим простой вариант, который подразумевает, что границы разделов прежние. Для этого установим текущие параметры файловой системы.
рис. 2
В нашем случае видим по смещению 0x1C2 типа раздела 0x0B, означающее, что на данный момент на USB накопителе есть раздел FAT32, который начинается с 0x80 сектора (DWORD по смещению 0x1C6), длиной 0x003C2000 секторов (DWORD по смещению 0x1CA). Переходим к boot сектору описанного раздела в сектор 0x80 (в файле образа байтах 0x10000)
рис. 3
Необходимо вычислить начальную точку отсчета, то есть место нулевого кластера, относительно которого рассчитывается пространство, а также определить размер кластера.
Выполним проверку на предмет отсутствия записей в таблице размещения файлов и проведем процедуру сравнения копий на предмет разночтений.
Рис. 4
Сравнение копий FAT показало, что разночтения отсутствуют. Анализ содержимого одной из копий FAT показал, что согласно таблицы на разделе заполнен только один кластер.
Далее необходимо оценить корневой каталог на предмет удаленных записей. Позиция первого кластера корневого каталога указывается в boot сектор по смещению 0x2C=0x00000002. Для второго кластера в FAT указано FF FF FF 0F, что означает конец цепочки, то есть корневой каталог состоит из одного кластера.
рис. 5
По адресу, рассчитанному выше, мы видим корневую директорию (корневой каталог), в которой содержится единственная 32-байтная запись. По смещению 0x0B мы видим значение 0x08, которое указывает на тип записи – метка тома. Тот факт, что таблицы размещения файлов заполнены нулями, и в корневом каталоге нет намека на какие-либо иные записи, говорит о том, что данный раздел был отформатирован.
Для проверки предположения о том, что раздел не пересоздавался и все параметры файловой системы корректны, необходимо произвести поиск регулярного выражения 0x2E 0x2E 0x20 0x20 0x20 0x20 0x20 0x20 со смещением внутри сектора 0x20 (данное выражение признак начала директории FAT32).
рис. 6
При нахождении регулярного выражения необходимо удостовериться, что это действительно директория, по иным признакам, так как в некоторых случаях возможно совпадение и найденное регулярное выражение не является элементом директории. Согласно информации на рис. 6, можно сказать, что данная директория начиналась с 3 кластера (номер текущего кластера директории DWORD содержится в WORD по смещению 0x1A (младшая часть) и WORD по смещению 0x14 (старшая часть)) и описывалась в корневом каталоге, так как по смещениям 0x3A и 0x34 содержатся нули (начальный кластер родительской директории). Проверим, соответствует ли номер кластера данной директории нулевой точке отсчета файловой системы, созданной после форматирования. Для этого номер кластера директории умножим на размер текущего кластера и прибавим к нулевой точке 0x03*0x1000+0x40E000=0x411000. Как видим, расчетный адрес соответствует фактическому нахождению. Установить имя данной директории возможно только в случае, если ранее корневой каталог состоял более, чем из одного кластера, и ссылка на данную директорию была не в первом кластере, так как содержимое первого кластера при форматировании было полностью уничтожено вместе с таблицами размещения файлов.
Далее продолжим поиск регулярного выражения 0x2E 0x2E 0x20 0x20 0x20 0x20 0x20 0x20 со смещением внутри сектора 0x20.
рис. 7
Повторяем все проверки: 0x04*0x1000+0x40E000=0x412000. Снова видим соответствие положения директории параметрам текущей файловой системы. Но, кроме этого, видим, что есть номер кластера родительской директории 0x03, что говорит о том, что данная директория была вложенной, и взглянув на рис. 6, можно установить имя директории, которая изображена на рис. 7. Итак, согласно рис. 6, по смещению 0x4B видим значение 0x10 — это означает, что данная запись указывает на директорию, а по смещениям 0x5A и 0x54 число 0x00000004 – указатель на 4-й кластер. По смещению 0x40 – имя директории «BIN». Именно таким образом устанавливается взаимосвязь директорий в поврежденном FAT разделе. После выполнения еще некоторого числа проверок директорий в разных участках образа можно сделать окончательный вывод о том, что на данном накопителе состоялось форматирование в границах предшествующей файловой системы и параметры вновь созданной файловой системы унаследованы от предыдущей, то есть дальнейшие аналитические операции нужно проводить в рамках раздела, описанного в таблице разделов с учетом параметров текущей файловой системы.
Зная, что 1С база, состоящая из DBF файлов, должна содержать файл конфигурации 1CV7.MD, выполним поиск последовательности 0x31 0x43 0x56 0x37 0x20 0x20 0x20 0x20 0x4D 0x44. Для того, чтобы уменьшить количество заведомо ложных результатов, поиск лучше выполнять в рамках 32-байтных блоков с нулевым смещением.
Рис. 8
Таким образом, находим все директории, содержащие в себе указатель на файл 1CV7.MD. В нашем случае обнаружилась только одна такая директория, что позволяет предполагать, что мы нашли первый кластер необходимой директории. Далее следует анализ положения родительских директорий, вплоть до корневой директории. Каждая найденная директория прописывается в таблицу FAT (сначала как директория из одного кластера, посредством записи FF FF FF 0F для соответствующего элемента таблицы). Также в корневой директории прописывается ссылка на дочерний объект.
На текущем этапе мы выполним копирование найденных файлов с предположением об их непрерывности, так как обе копии FAT не содержат информации о фрагментации (напомним, что они были безвозвратно уничтожены системным администратором в результате необдуманного форматирования USB flash). После копирования директории 1С базы анализируем количество файлов. Учитывая, что фрагмент директории был размером в один кластер, то извлекли мы не более 126 файлов, что явно намного меньше, чем должно быть в директории с DBF и CDX файлами, относящимися к 1С базе. Примерно такой же результат выдадут программы автоматического восстановления, о чем свидетельствует результат, полученный системным администратором посредством использования R-Studio.
Среди извлеченных файлов есть 1CV7.MD (файл конфигурации) и 1СV7.DD (файл словаря данных). После выполнения проверки целостности создадим у себя на диске временную папку, куда поместим 1CV7.MD. Укажем данный путь при добавлении новой базы и откроем конфигуратор, посредством которого создадим чистую базу на основании этой конфигурации. Сравним сформированный DD файл с восстановленным, если описания и количество справочников идентичны, то никаких дополнительных действий не требуется, и, имея полный список файлов, можно приступать к поиску остальных фрагментов директории 1С базы. Для этого необходимо осуществить поиск последовательностей из ASCII кодов символов, используемых в именах недостающих DBF файлов. По мере обнаружения фрагментов директории дописывать в таблицу размещения файлов продолжение цепочки. После каждой операции дополнения цепочки директории выполнять копирование файлов и анализировать, насколько сократилась количество недостающих DBF файлов, и вновь формировать последовательность ASCII кодов символов для поиска следующего фрагмента.
рис. 9
Также необходимо помнить, что при записи цепочки фрагментов директории в таблицу размещения файлов, необходимо анализировать фрагменты, чтобы стыковались LFN записи. В случае только коротких записей цепочку можно писать с любым порядком фрагментов.
В данном случае выполнив поиск 5 последовательностей удалось найти все остальные фрагменты директории с базой 1С.
После того, как построена полная цепочка фрагментов директории, выполняем повторное копирование уже всех файлов 1С базы с предположением об их непрерывности. Пользовательская информация содержится в DBF файлах, поэтому необходимо проверить их целостность.
Основной метод контроля целостности DBF файла – это проверка информации, содержащейся в служебном заголовке и соответствует ли содержимое файла описанию в заголовке.
рис. 10
Первоначально проводится оценка заголовка: проверяется его длина, указанная по смещению 0x08, приводит ли указанное в нем смещение на конечный маркер 0x0D. Записи полей базы, начиная со смещения 0x20, описываются 32-байтовыми записями, в которых по смещению 0x00 следует имя поля, по смещению 0x0B тип поля, по смещению 0x10 – размер поля. Сумма размеров полей +1 (один дополнительный байт для каждой записи в базе является статусом записи в DBF) должна равняться содержимому по смещению 0x0A (размер одной записи в базе). На рисунке DBF файлы мы видим следующие длины полей: 0x09+0x10+0x10+0x10+0x10+0x10+0x01=0x5A.
Проведем проверку корректности размера файла. Для этого выполняем умножение количества записей, которое указано в заголовке по смещению 0x04 на размер одной записи в базе по смещению 0x0A с последующим сложением с содержимым по смещению 0x08.
0x00000003*0x005A+0xE1=0x01EF. По полученному смещению должен находиться маркер окончания файла 0x1A.
Для контроля целостности содержимого полей можно использовать визуальный метод.
рис. 11
В таком вариант просмотра нужно пролистывать содержимое записей от начала и до конца. В случае если заполнение однородное, в каждом поле располагаются типы данных, характерные для описанного в заголовке и инородного содержимого нет, то по завершении просмотра DBF файла можно сделать вывод о корректности его содержимого.
При обнаружении содержимого, не соответствующего описанию поля в заголовке базы, необходимо установить точное место, с которого начинаются некорректные данные.
Рис. 12
Исходя из описания полей в заголовке и содержимого конкретного DBF файла, можно формировать предположительные ASCII последовательности, которые должны находиться по заданным смещениями в недостающих фрагментах. При отсутствии однотипных баз на одном из накопителей (в том числе и файловых копий одной и той же базы) такой метод позволит относительно быстро найти все недостающие фрагменты в образе накопителя. Отдельно отметим, что возникнут дополнительные сложности в стыковке фрагментов, если размер записи в DBF файле маленький или кратен 16. При наличии иных однотипных баз задача будет многократно усложнена (это утверждение справедливо на всех этапах работ, начиная с поиска фрагментов нужной директории).
Необходимо проверить целостность каждого DBF файла, коих в одной 1С базе несколько сотен. По прохождении всех проверок и сборов фрагментов файлов последует финальная проверка в конфигураторе 1С Предприятия.
рис. 13
В идеальном варианте по результатам тестирования должны пройти успешно все пункты, отмеченные в чекбоксах. Если обнаруживаются ошибки по первым двум пунктам, то необходимо проанализировать лог ошибок в конфигураторе и выяснить, в каких DBF файлах присутствуют инородные данные, которые не были обнаружены при проверках. Если обнаруживаются ошибки при проверке логической целостности, то опять же необходимо анализировать лог ошибок, чтобы выяснить, заключается ли проблема базы в качестве ее сбора, либо в ошибках, допущенных разработчиками конфигурации 1С.
Обратим внимание на тот факт, что если бы данная USB flash не была отформатирована, то после ее вычитки процедура восстановления данных была бы значительно более простой, что сильно бы отразилось на стоимости и сроке выполнения работ в меньшую сторону. В заключение, хотелось бы предостеречь всех пользователей и обслуживающий персонал от необдуманных действий в аварийных ситуациях, которые многократно усугубляют проблему, а также пожелать почаще выполнять операции резервного копирования.
Краткое описание формата файлов *.1CD (файловых баз 1Сv8)
После того, как я выложил свою программку Tool_1CD ( http://infostart.ru/projects/3851/ ), оказалось, что интерес к формату файлов 1CD достаточно велик. Поэтому для всех желающих продолжить разбираться с форматом, или желающих написать свою программу, выкладываю свои текущие знания об этом формате.
Описание формата приведено в терминах языка C. Размер типа char – 1 байт, размер типа short int – 2 байта, размер типа int и unsigned int – 4 байта. Префиксом 0x обозначаются шестнадцатеричные числа.
Файлы баз *.1CD состоят из блоков длиной 4096 байт (0x1000). Соответственно, длина файла всегда кратна 4096.
Блок 0
char sig[8]; // сигнатура “1CDBMSV8”
unsigned int length;
Первые 8 байт – сигнатура базы «1CDBMSV8».
Следующие 4 байт – длина базы (файла) в блоках.
Предназначение поля unknown неизвестно, всегда содержит 1.
Объекты
Структура первого блока каждого объекта такова:
char sig[8]; // сигнатура “1CDBOBV8”
int length; // длина содержимого объекта
unsigned int version;
unsigned int blocks[1018];
Первые 8 байт – сигнатура базы «1CDBOBV8».
Есть 2 типа объектов.
Блок 1. Таблица свободных блоков
В blocks содержатся номера блоков, в которых собственно и находится содержимое таблицы свободных блоков. Значащими являются ненулевые значения в массиве blocks. Содержимое таблицы свободных блоков – это просто массив номеров свободных блоков:
unsigned int free_blocks[length];
Таким образом, в базе содержатся ровно length свободных блоков.
Когда системе требуется новый блок для данных, то она берет последний свободный блок из массива free_blocks и уменьшает length на 1. Если свободных блоков нет, то он создается в конце файла базы. Блоки, содержащиеся в массиве blocks, не являются свободными, а принадлежат объекту – таблице свободных блоков. В blocks может содержаться больше блоков, чем необходимо для хранения массива free_blocks.
Остальные объекты
В поле length содержится длина в байтах данных объекта.
В массиве blocks находятся индексы блоков, содержащих таблицу размещения данных объекта. Каждый блок, указанный в blocks, и являющийся частью таблицы размещения, имеет следующую структуру:
unsigned int datablocks[1023];
Повторим, в заголовочном блоке объекта находится массив blocks, содержащий индексы блоков с таблицей размещения. А в таблице размещения находятся блоки, содержащие сами данные.
Блок 2. Корневой объект
Для версии «8.1.0.0» структура выглядит так:
Т.е. различаются эти структуры только длиной поля lang. В поле lang содержится код языка базы. Код языка базы представляет собой строку в ANSI-кодировке. Мне встречались только базы с кодами «ru_RU» и «en». На что влияют эти коды языка, я не знаю, возможно, на порядок сортировки строк при построении индексов.
В поле numblocks содержится количество элементов в массиве tableblocks. В массиве же tableblocks содержатся индексы объектов, содержащих все таблицы данных. Т.е. таблиц в базе ровно numblocks.
Объект таблицы
Как видно из этого примера, здесь присутствуют имя таблицы (_Reference4), раздел описания полей таблицы (Fields), раздел описания индексов (Indexes), параметр Recordlock и раздел Files.
В разделе Files всегда содержатся три числа, которые содержат индексы заголовочных блоков объектов (по порядку) с записями таблицы, Blob-данными (строки неограниченной длины и двоичные данные) и индексами. Если какого-либо объекта у таблицы нет, то соответствующее число равно нулю.
В разделе Fields содержатся описания полей таблицы. Описание каждого поля содержит (по порядку): имя поля (FieldName), тип поля (FieldType), признак использования NULL (NullExists), длину (FieldLength), точность (FieldPrecision) и признак регистрочувствительности (FieldCaseSensitive).
Сколько байт занимает каждое поле в записи, и как его интерпретировать, зависит от параметров поля. Во-первых, если NullExists у поля равен 1, то первый байт поля является признаком NULL. Значение 0 этого байта означает, что поле не содержит значение (т.е. содержит NULL). В противном случае, поле содержит значение. Если же NullExists равен 0, то такого байта в поле нет.
Далее, размер и формат поля зависит от типа поля. Типы поля бывают такими:
Объект записей таблицы
Объект Blob таблицы
unsigned int nextblock;
Поле nextblock содержит индекс следующего блока, содержащего продолжение данных, или 0, если следующего блока нет. Поле length содержит длину данных в этом блоке (максимум 250). Поле data содержит сами данные. Нулевой блок всегда считается свободным, в поле nextblock он содержит индекс следующего свободного блока. Таким образом, с нулевого блока начинается цепочка свободных блоков.
В записях таблицы в полях с типом «NT» и «I» содержится индекс первого блока, с которого начинаются данные, относящиеся к этому полю данной записи.
Объект индексов таблицы
ВНИМАНИЕ!
15 декабря на «Клерке» стартует обучение на онлайн-курсе повышения квалификации для получения удостоверения, которое попадет в госреестр. Тема курса: управленческий учет.
Повышайте свою ценность как специалиста прямо на «Клерке». Подробнее
Некоторые особенности устройства и работы файловой базы данных «1С:Предприятия 8»
В данном разделе рассматриваются некоторые особенности внутреннего устройства и работы механизмов файловой базы данных «1С:Предприятия 8», которые не освещены в документации, но могут быть интересны пользователям и разработчикам прикладных решений на платформе «1С:Предприятие 8». Приведенное описание соответствует платформе «1С:Предприятие» версии 8.3.4.
Устройство файла *.1CD
На самом нижнем уровне файл *.1CD или файл базы данных содержит внутри своего рода файловую систему, включающую в себя так называемые внутренние файлы. Файл *.1CD имеет страничную организацию, то есть состоит из страниц размером 4096 байт (4 К). Размер файла *.1CD всегда кратен 4 К.
Страницы адресуются их номерами. Номер страницы представлен 4-байтовым целым числом без знака. Следовательно, файл *.1CD может содержать не более чем 4 294 967 296 страниц.
Страница с номером 0 содержит служебные данные файла *.1CD, такие как версия формата файла базы данных, общее число страниц в файле и т. п.
Страница с номером 1 используется менеджером свободных страниц.
Каждая из остальных страниц может либо принадлежать какому-либо из внутренних файлов, либо находиться в списке свободных страниц.
Внутренние файлы
Страницы, относящиеся к внутреннему файлу, бывают трех видов:
Эти страницы образуют дерево, корнем которого является корневая страница, промежуточными узлами являются индексные страницы, а листьями – страницы данных.
Корневая страница содержит служебную информацию внутреннего файла, такую как длина файла, номер версии данных файла и т. п. Кроме того, на корневой странице содержится до 1018 номеров индексных страниц.
Индексные страницы образуют промежуточный уровень дерева. Индексная страница содержит число страниц данных, адресуемых данной индексной страницей, и до 1023 номеров страниц данных.
Страница данных содержит только данные.
Из сказанного выше следует, что внутренний файл может включать не более чем 1 041 414 (1018 * 1023) страниц данных. Следовательно, максимальный размер внутреннего файла не может превышать 4 265 631 744 (1018 * 1023 * 4096) байта. Для адресации отдельных байтов внутреннего файла используются 4-байтовые целые числа без знака.
Для представления внутреннего файла нулевой длины достаточно одной только корневой страницы. Если размер внутреннего файла составляет от 1 до 4096, то он представляется тремя страницами: одной корневой, одной индексной и одной страницей данных. При дальнейшем росте размера файла добавляются новые страницы данных, и их номера помещаются в индексную страницу. Как только индексная страница перестает вмещать номера страниц данных, добавляется новая индексная страница и ее номер добавляется в корневую страницу. И так далее.
Внутренние файлы не имеют имен. Для идентификации внутренних файлов используются номера их корневых страниц.
Список свободных страниц
Страницы, не относящиеся к какому-либо из внутренних файлов, находятся в списке свободных страниц. Свободные страницы могут образоваться при сокращении размера или удалении внутреннего файла. Любые освободившиеся страницы внутренних файлов помещаются в список свободных страниц.
При необходимости увеличения размера или создании нового внутреннего файла по возможности используются страницы из списка свободных страниц.
Устройство базы данных
Внутренние файлы в конечном счете предназначены для хранения базы данных. База данных представляет собой совокупность таблиц. Каждой таблице может соответствовать от двух до четырех внутренних файлов:
Файл описания и файл данных присутствуют обязательно для каждой таблицы. Файл индексов присутствует, если в таблице определен хотя бы один индекс. Файл данных неограниченной длины присутствует, если в структуре таблицы определена хотя бы одна колонка неограниченной длины.
Кроме того, имеется файл описания базы данных. Данный файл содержит информацию о локали базы данных, а также номера корневых страниц внутренних файлов описания для каждой из таблиц базы данных.
Таблицы
Файл описания таблицы
Файл описания таблицы содержит полное описание таблицы, которое включает:
При открытии базы данных считывается файл описания базы данных и адресуемые им файлы описания таблиц. На основании этой информации инициализируются внутренние структуры данных, необходимые во время выполнения. Прочие файлы таблиц на этом этапе не открываются. Их открытие выполняется по мере обращения к таблицам. Это сделано из соображения ускорения процесса открытия, а также из предположения, что в данном сеансе могут быть обращения не ко всем таблицам базы данных.
Файл данных
Номера записи представлены 4-байтовыми целыми числами. Запись с номером 0 используется для служебных целей. Номера «настоящих» записей начинаются с 1.
Длина записи может быть вычислена как сумма длин всех колонок плюс от 1 до 17 байт служебной информации. Ограничений на длину записи не накладывается.
Ниже приведена информация о типах данных и соответствующем размере колонок:
Десятичное число с фиксированной точкой. Хранится в десятичном виде по две десятичные цифры на один байт. Зарезервировано место для знака. Размер может быть вычислен по формуле:
Строка переменной длины, состоящая не более чем из n однобайтовых символов. Размер колонки равен n + 2 байта. Дополнительные 2 байта используются для хранения фактической длины.
Двоичные данные переменной длины не более n байт. Размер колонки равен n + 2 байта. Дополнительные 2 байта используются для хранения фактической длины.
Значение логического типа (true или false). Размер колонки равен одному байту.
Дата без времени. Размер колонки – 4 байта.
Дата и время. Размер колонки – 7 байт.
Текст неограниченной длины, состоящий из однобайтовых символов. В структуре записи колонка занимает два 4-байтовых целых числа: фактическая длина значения и адрес в файле данных неограниченной длины. Фактические значения хранятся в файле данных неограниченной длины.
Текст Unicode неограниченной длины, состоящий из символов в кодировке UTF-16. В структуре записи колонка занимает два 4-байтовых целых числа: фактическая длина значения и адрес в файле данных неограниченной длины. Фактические значения хранятся в файле данных неограниченной длины.
Двоичные данные неограниченной длины. В структуре записи колонка занимает два 4-байтовых целых числа: фактическая длина значения и адрес в файле данных неограниченной длины. Фактические значения хранятся в файле данных неограниченной длины.
Кроме того, к размеру колонок, которые могут содержать NULL, добавляется еще один байт.
Файл индексов
В файле индексов находятся все индексы, определенные для таблицы. Детальное рассмотрение структуры индексов не входит в цели данной статьи. Отметим только, что индекс представляет собой сбалансированное дерево. С точки зрения использования файловой базы данных важным является то, что, в отличие от размера записи, на длину ключа индекса наложено ограничение: длина не может превышать 1920 байт. Ключ представляет собой конкатенацию значений всех индексируемых колонок записи плюс 4-байтовый номер записи.
Индексироваться могут колонки типов Numeric, Char, NChar, Binary, VarChar, NVarChar, VarBinary, Logical, Date и DateTime. Значение каждой из индексируемых колонок типов Numeric, Binary, VarBinary, Logical, Date и DateTime помещается в ключ как есть. Соответственно, каждая из таких колонок добавляет к длине ключа свой собственный размер. А вот для колонок типов Char, NChar, VarChar и NVarChar вместо самой строки в ключ помещается ее ключ сортировки (collation key). Поэтому вклад колонок указанных типов в длину ключа определяется как n * 3 + 2 для колонок, не чувствительных к регистру букв. И n * 4 + 3 для колонок, чувствительных к регистру.
Файл данных неограниченной длины
В файле данных неограниченной длины хранятся фактические значения колонок типов Text, NText и Image. Для хранения таких значений файл организован как набор блоков длиной 256 байт. Каждое значение хранится как односвязный список блоков. В каждом блоке содержатся:
Блок с адресом 0 используется для служебных нужд, а если точнее он содержит адрес списка свободных блоков. В список свободных блоков помещаются освободившиеся блоки, которые могут быть использованы в дальнейшем.
Работа с базой данных
Чтение данных
Следует различать чтение данных, выполняемое вне транзакции, и чтение в рамках транзакции. Операция чтения (например, SQL-запрос SELECT), выполняемая вне транзакции, получает данные, соответствующие состоянию базы данных на момент выполнения операции. При использовании SELECT вне транзакции поведение файловой базы данных подобно поведению версионных СУБД, таких как Oracle. То есть все данные, полученные запросом SELECT, относятся к одному согласованному состоянию базы данных, имевшему место на начало выполнения операции. Чтение данных не может быть заблокировано никакой другой операцией чтения или записи. Но нужно понимать, что состояние, имевшее место на начало чтения, может быть изменено. Соответственно, считываемые данные могут оказаться устаревшими.
Если чтение выполняется в рамках транзакции, то гарантируется, что считанные данные не могут быть изменены никем другим до завершения транзакции. Для обеспечения этой неизменности используется механизм транзакционных блокировок. При первом обращении к таблице на чтение в рамках транзакции на таблицу накладывается Read-блокировка. И эта блокировка не снимается до завершения транзакции.
Запись данных
Запись данных всегда предполагает наличие транзакции. Если операция записи была вызвана вне объемлющей транзакции, то транзакция будет создана неявно в процессе выполнения операции. При выполнении операции записи на таблицы, в которые вносятся изменения, накладывается транзакционная Write-блокировка, препятствующая чтению или записи, выполняемой другими соединениями.
Если на таблицу уже была наложена Read-блокировка, то выполняется ее эскалация до Write-блокировки.
Операции записи данных, выполняемые в рамках транзакции, не приводят к немедленной записи изменений в файл *.1CD. Изменения, вызванные операциями записи, накапливаются в кеше модифицированных страниц и сбрасываются в файл базы данных при фиксации (commit) транзакции.
Таким образом, если в процессе выполнения транзакции, до ее фиксации, произойдет сбой и/или падение приложения, то все изменения, произведенные в транзакции, окажутся потерянными и файл базы данных останется в неизмененном состоянии.
Кеширование данных
Кеш считанных страниц
Для повышения эффективности операций чтения механизмы файловой базы данных стараются кешировать считанные данные и тем самым минимизировать число физических операций чтения из файла базы данных. Кеш считанных страниц содержит прочитанные страницы данных внутренних файлов. Общий размер кеша для каждого из соединений с файловой базой данных является ограниченным и может в зависимости от различных условий составлять от 2 до 200 Мбайт. Кеш наибольшего размера создается при работе с файлом базы данных, расположенным на сетевом диске.
Кеш организован по принципу LRU. То есть страницы, к которым дольше всего не было обращений, могут быть вытеснены из кеша вновь считанными страницами.
Другой причиной, по которой страницы могут быть исключены из кеша, является его обновление. Каждое зафиксированное состояние данных внутреннего файла имеет соответствующий номер версии. Все кешируемые страницы внутреннего файла соответствуют определенной версии внутреннего файла. Процесс обновления состоит в том, что из файла базы данных считывается текущая версия внутреннего файла и сравнивается с версией кешируемых страниц. Если выясняется, что версия кешируемых страниц устарела, то страницы соответствующего внутреннего файла исключаются из кеша.
Для каждой операции чтения, выполняемой вне транзакции, обновление кеша производится для внутренних файлов данных, индексов и данных неограниченной длины каждой из таблиц, задействованных в операции чтения.
В рамках транзакции обновление кеша производится непосредственно после наложения на таблицу Read-блокировки. В дальнейшем до завершения транзакции кеш остается актуальным, так как таблица не может быть модифицирована другими транзакциями. Соответственно, для последующих операций чтения в той же транзакции обновления кеша не требуется.
Следует также заметить, что в исключительном режиме доступа к базе данных кеш считанных страниц всегда остается актуальным и его обновление не производится.
Еще одной причиной для исключения страницы из кеша считанных страниц является попадание страницы в кеш модифицированных страниц.
Модификация данных и кеш модифицированных страниц
В процессе выполнения транзакции при внесении изменений в базу данных изменения никогда не записываются непосредственно в файл. Вместо этого они буферизуются в кеше модифицированных страниц. Страница, находящаяся в этом кеше, содержит все данные страницы, как модифицированные участки, так и оставшиеся неизменными с момента считывания. При этом ведется учет модифицированных участков, чтобы в момент выполнения физической записи в файл по возможности минимизировать объем записываемых данных.
Страница, попавшая в кеш модифицированных страниц, исключается из кеша считанных страниц.
При запросе на чтение данных из внутреннего файла соответствующая страница сначала ищется в кеше модифицированных страниц. Если не найдена, то производится поиск в кеше считанных страниц. И если не найдена там, то производится считывание страницы из файла с помещением в кеш считанных страниц.
Сброс кеша модифицированных страниц в файл производится только при выполнении фиксации (commit) транзакции. При фиксации транзакции все измененные страницы всех внутренних файлов собираются в общий массив, упорядоченный по номерам страниц в файле базы данных, и запись в файл базы данных производится от больших номеров страниц к меньшим. Это делается из следующих соображений:
Время жизни кеша модифицированных страниц ограничено временем выполнения транзакции. После завершения транзакции кеш полностью освобождается.
На размер кеша модифицированных страниц не накладывается никаких ограничений. Единственным ограничителем является размер свободной оперативной памяти.
Блокировки
Транзакционные блокировки
Этот вид блокировок уже упоминался выше. Транзакционные блокировки предназначены главным образом для обеспечения логической целостности и изоляции транзакций. Транзакционные блокировки бывают двух видов:
Read-блокировки не конфликтуют между собой, но конфликтуют с Write-блокировками. Write-блокировки конфликтуют с любыми блокировками: Read и Write. Единицей блокировки является таблица. Единица довольно крупная, особенно с учетом того, что в большинстве современных СУБД поддерживаются блокировки на уровне записи. Однако реализация блокировки на уровне записи потребовала бы большого числа файловых блокировок, что привело бы к существенному снижению производительности.
Транзакционные блокировки накладываются с ожиданием. По умолчанию время ожидания транзакционной блокировки равно 20 сек.
Блокировки фиксации состояния
Также имеется ряд блокировок фиксации состояния. Данный вид блокировок относится к системным блокировкам и предназначен для обеспечения согласованного доступа к файлу базы данных на физическом уровне. При использовании файловой базы данных крайне редко приходится сталкиваться с какими-либо внешними проявлениями, связанными с этим видом блокировок. В данной статье они упоминаются главным образом для полноты картины.
Поясним место этих блокировок на примере фиксации транзакции. Как было сказано выше, при фиксации результатов транзакции все изменения записываются в файл базы данных. Естественно, что пока процесс записи изменений не завершен, файл базы данных находится в рассогласованном состоянии. Соответственно, попытка чтения приведет к получению рассогласованных данных. Но записываемые данные относятся не ко всем таблицам, а только к измененным. Соответственно, нужно сделать так, чтобы никакие данные, имеющие отношение к модифицируемым таблицам, не считывались, пока запись изменений не завершена. Для обеспечения этого предусмотрена блокировка фиксации таблицы для записи и для чтения.
На время записи изменений, произведенных транзакцией, устанавливается фиксация для записи всех модифицированных транзакцией таблиц. А на время чтения данных, связанных с таблицей, устанавливается фиксация для чтения. Фиксация для записи конфликтует с фиксацией для чтения. Фиксации для чтения не конфликтуют между собой, но конфликтуют с фиксацией для записи. Соответственно, гарантируется, что, пока запись не завершена, никакие операции чтения не могут быть выполнены. А также, пока не завершено чтение, запись изменений не может быть начата.
Данный вид блокировок накладывается на очень непродолжительное время. Время ожидания захвата блокировки составляет 120 сек. Такое время ожидания выбрано из расчета, чтобы любая операция, прикрытая блокировкой фиксации состояния, успела завершиться. Исключительные ситуации с сообщениями «Не удалось зафиксировать таблицу для записи» или «Не удалось зафиксировать таблицу для чтения» крайне редки и возникают в основном в условиях сильной загрузки сети или компьютера, выполняющего функции файл-сервера.