ObjESSty предоставляет реализацию долговременного объектного хранилища (oess_1::db), которое, строго говоря, нельзя называть БД или СУБД. oess_1::db решает только проблемы размещения данных во внешней памяти и поддержки кратковременных атомарных транзакций. Но не имеет возможностей поиска и индексации информации в хранилище и предоставляет органиченный набор инструментов для администрирования хранилища. Но за время эксплуатации oess_1::db определился термин "восстановочная БД", который вошел в обиход и, возможно, достаточно точно отражает характер как самого объектного хранилища, так и наиболее распространенных способов его использования.
oess_1::db появилась для решения вполне конкреной задачи: ряду приложений требовалось сохранять во внешней памяти информацию о начатых операциях (транзакциях). Нужно было это для того, чтобы после останова и перезапуска приложения можно было прочитать (поднять) сохраненную ранее информацию и продолжить выполнение с наиболее близкого к моменту останова состояния. Т.е., фактически, восстановить состояние приложения. Поэтому БД, в которую нужно было сохранять описания транзакций, получила название восстановочной БД.
Характер приложений для которых изначально планировалось применять oess_1::db накладывал ряд требований на восстановочную БД:
- БД должна была поддерживать сложные C++ объекты, атрибутами которых могли быть списки, множества, указатели на полиморфные объекты и т.д.;
- в одной БД должны были храниться объекты разных типов;
- сохраняемые в БД объекты с течением времени могли изменять свой размер, причем существенно;
- БД должна была быть небольшой, простой в использовании и, желательно, не требовать администрирования.
Еще одной важной характеристикой восстановочной БД являлось то, что в идеальном случае, информация в БД не должна была накапливаться. Т.е. объект в БД создавался при начале транзакции, изменялся по ходу выполнения транзакции и удалялся при завершении транзакции. Т.е. когда приложение простаивало, его восстановочная БД должна была быть пустой.
Использование серьезных реляционных СУБД было нежелательно по нескольким важным причинам:
- в некоторых случаях требовалось решение множества административных и политических вопросов для получения разрешения на использование РСУБД класса Oracle, MSSQL Server или даже MySQL;
- в некоторых случаях установка РСУБД была связана с техническими сложностями, вызванными органиченными ресурсами и особенностями ОС;
- РСУБД требуют некоторых усилий по администрированию, а этого, в большинстве случаев хотелось избежать.
Вполне подходящими решениями в этих случаях могли быть BerkeleyDB, SQLite и MetaKit. Но они не поддерживают работу с объектными данными. Поэтому и была реализована oess_1::db, которую можно называть восстановочной БД.
На физическом уровне база -- это один файл данных и конфигурационный файл. Файл данных разбит на страницы, размер страницы задается в конфигурации. Страницы объединяются в сегмены, сегменты объединяются в области. У сегментов, областей и самого файла есть заголовки, в которых указывается, где есть свободное место. Т.о. страницы в файле БД двух типов -- с данными и заголовками. Страницы с данными в свою очередь нарезаются на блоки (размер задается в конфигурации). При записи объекта его значение разбивается на блоки, а блоки провязываются в цепочку. Т.о. на одной странице данных могут оказаться блоки нескольких объектов, а сам объект может быть размазан на несколько страниц. Такая схема позволяет организовать "сборку мусора" -- повторно использовать блоки, которые принадлежали удаленным объектам. При работе с БД весь доступ к БД кэшируется, а размер кэша можно выставить в конфигурации.
Логическая структура БД очень простая. БД состоит из разделов (слайсов). У каждого слайса уникальное имя. Каждый слайс -- это последовательность объектов. Каждому объекту назначается идентификатор по которому объект извлекается из БД за конечное число шагов, не зависящее от количество объектов в хранилище. Идентификатор назначается объекту при создании и остается неизменным в течении всего времени жизни объекта (хотя сам объект может изменять свое расположение внутри файла данных). Идентификатор затем используется для выполнения всех остальных операций над объектом: извлечение из БД (load), обновление значения объекта в БД (update) и удаления объекта из БД (destroy). После удаления объекта его идентификатор освобождается и может быть присвоен новому объекту.
Имеется поддержка транзакций. При этом можно вести write-ahead-log либо для последней транзакции, либо сохранять все логи всех транзакций (как в Berkeley DB, тогда по логам можно восстановить все хранилище полностью). Транзакциями можно управлять явно: поддерживаются операции begin/commit/rollback. Если явное управление не используется, то транзакция автоматически начинается при выполнении модифицирующей операции и подтверждается после завершения операции. При необходимости write-ahead-log можно отключить. Если write-ahead-log не отключен, то любая операция изменения БД производится с предварительной фиксацией транзакции в специальном trace-файле. Поэтому, если при завершении транзакции происходит сбой, то затем можно восстановить БД (либо содержимое trace-файла игнорируется, либо повторно записываеся в файл данных).
Слайсы были предназначены для того, чтобы все множество объектов в БД можно было разделить на некоторые логические группы. Предполагалось, что в одном слайсе будут содержаться объекты, описывающие транзакции одного типа (но не обязательно, чтобы эти объекты были одного типа).
Сама БД, а так же слайсы в БД, могут быть созданы и уничтожены "на лету" из приложения. Для этого не нужно предпринимать никаких административных действий. Так же физическая структура БД позволяет повторно использовать освободившиеся после удаления объектов блоки и страницы. Это уменьшает влияние такого фактора, как фрагментация внутри файла данных. Т.е. восстановочная БД в oess_1::db полностью контролируется из приложения и не требует администрирования.
Текущая версия ObjESSty может использовать только один файл данных, поэтому максимальный объем БД ограничен 2Gb. Но, реально максимальный объем БД определяется не ограничениями на размер файла данных, а зависит от количества объектов, которые можно поместить в БД. А это, в свою очередь, зависит от выбранного размеров страницы и одного блока на странице. Например, при штатном размере страницы в 4096 байт и размере одного блока 256 байт, в БД может быть размещено около двух миллионов объектов, чей размер не превышает 256 байт.
Изначально oess_1::db использовалась для сохранения описания проводимых приложением транзакций. При этом явно выделяются две стадии работы приложения: старт с восстановлением из БД всех незавершенных транзакций и нормальный режим работы. Общая схема применения oess_1::db была следующей:
- для каждого типа транзакции создавался свой тип обработчика транзакции в виде C++ класса. Этому типу нужно было сохранять часть информации в восстановочной БД. Для этого либо сам тип объявлялся сериализуемым (с помощью oess_1::stdsn), либо определялись дополнительные сериализуемые типы, которые и сохранялись в БД;
- когда приложение определяло, что нужно начать транзакцию конкретного типа, создавался соответствующий объект-обработчик. Этот объект сохранял в восстановочной БД необходимое ему описание и запоминал назначенный описанию идентификатор;
- когда объекту-обработчику необходимо было изменить статус (состояние) транзакции, он обновлял описание транзакции в БД, используя идентификатор;
- когда транзакция завершалась, объект-обработчик уничтожал описание транзакции в БД с помощью того же идентификатора;
- если приложение рестартовало, то вначале их БД поднимались все описания транзакций и для каждого описания создавался свой объект-обработчик. При этом объекту обработчику сообщался уже известный идентификатор описания транзакции в БД.
Применялись так же и другие варианты этой схемы, например:
- описание транзакции в восстановочной БД создавал и удалял не объект-обработчик транзакции, менеджер транзакций. Обработчик сразу получал готовый объект-описатель (если он сам не было таковым) и идентификатор описателя в БД. Задачей обработчика являлось только обновлять описание по ходу выполнения транзакции. Если же во время транзакции описание в БД изменять не требовалось, то объект-обработчик мог быть вообще никак не связан с БД;
- не было отдельного обработчика на каждую транзакцию, а использовались менеджеры конкретных типов транзакций. Эти менеджеры создавали, обновляли и удаляли описания транзакций и сами сохраняли идентификаторы описаний в БД.
В любом случае использовалась схема: при старте приложения полное пересканирование всей БД, а по ходу работы приложения "точечное" воздействие на единичные объекты по уже известным идентификаторам.
Исходя из этого и была реализована функциональность oess_1::db:
- нет возможности индексации содержимого БД и поиска информации в БД;
- есть возможность последовательного перебора всех объектов в конкретном слайсе;
- все операции над объектами выполняются за конкретное число шагов, не зависящее линейно от количества объектов в БД.
- Заметки:
- Со временем все же обозначились ситуации, когда индексация объектов в БД была бы очень желательной. Но индексация не для организация сложного поиска, аналогичного SQL-оператору select. Скорее индексация в смысле BerkeleyDB, когда содержимое некоторого слайса хотелось бы видеть в виде map-а между ключами некоторого типа и объектами-значениями. Причем один и тот же слайс иногда хотелось бы одновременно map-ировать несколькими разными ключами. Пока выход из этих ситуаций сделан через вспомогательные классы oess_1::db::cln::slice_image_t, oess_1::db::cln::slice_index_t. Это промежуточное решение. Сейчас идет разработка способа эффективного индексирования содержимого слайса несколькими ключами и хранение этого индекса в самой oess_1::db.
При создании oess_1::db главной целью было сокращение времени на ее реализацию. Поэтому oess_1::db выполнена в виде встраиваемой БД (что так же устараняет необходимость администрирования БД). Но в архитектуру БД была заложена возможность создания со временем многопользовательской, клиент-серверной БД. Применительно к восстановочной БД это может выглядеть избыточным, но на самом деле подразумевается всего лишь возможность нескольких приложений работать с одной БД. Например, для балансировки нагрузки приложение разделено на несколько exe-файлов, каждый из которых выполняет собственный тип операций или все они выполняют один и тот же тип операций, но для разных клиентов/подключений. Но все вместе используют одну и ту же БД, например, работая с разными слайсами. Здесь так же прослеживается аналогия с BerkeleyDB, которая предоставляет нескольким процессам возможность работы с одной БД.
oess_1::db разбита на несколько слоев. Внешний слой, oess_1::db::cln, предназначен для предоставления "клиентам" возможности работы с БД. Главным классом клиентского слоя является класс oess_1::db::cln::db_t, который определяет интерфейс клиента к БД.
Следующим слоем является "коммуникационная" прослойка, oess_1::db::site. Эта прослойка определяет интерфейсы oess_1::db::site::localhost_t и oess_1::db::site::abstract_connector_t. Интерфейс oess_1::db::site::localhost_t предназначен для использования на "серверной" стороне. Т.е. сервер БД должен создать экземпляр oess_1::db::site::localhost_t (посредством oess_1::db::site::create_std_localhost()), описать доступные серверу БД и ожидать подключения клиентов. Интерфейс oess_1::db::site::abstract_connector_t предназначен для клиентов. Клиент должен создать экземпляр connector-а, реализующего этот интерфейс, подключиться connector-ом к серверу и, после удачного подключения, использовать connector для создания экземпляра oess_1::db::cln::db_t.
Пока существует реализация только одного connector-а: oess_1::db::site::impl::localhost_connector_t, которая предназначена для подключения к localhost. Т.е. для случаев, когда клиент и сервер работают в одном приложении.
Следующими слоями являются oess_1::db::impl::db_content, в котором реализуется понятие логической БД с поддержкой слайсов, и oess_1::db::storage, в котором реализуется физическое хранилище с поддержкой транзакций.
Когда БД описывается в экземпляре localhost, для БД указывается т.н. логическое имя -- это уникальный в рамках данного localhost псевдоним, по которому БД будет доступна клиентам. Когда клиент осуществляет подключение (oess_1::db::cln::db_t::attach) к БД, он указывает именно логическое имя. Это позволяет изменить расположение БД прозрачным для клиента образом.
Под физическим именем БД в ObjESSty понимается имя, из которого ObjESSty будет производить имена файлов БД. В текущей версии имена строятся путем добавления соответствующих расширений к исходному физическому имени. Так, основной файл данных имеет раширение .oess.data, конфигурационный файл имеет расширение .oess.cfg, trace-файлы имеют расширения .xxxxxxxx.trace (где xxxxxxxx -- это порядковый номер trace-файла).
ObjESSty поддерживает три режима для сохранения восстановочной информации для самой БД:
- режим oess_1::db::storage::trace_no указывает вообще не сохранять восстановочной информации при фиксации транзакций. Это самый быстрый режим работы БД. Но он не обеспечивает никакой дополнительной защиты логической целостности БД. Если сбой произойдет при фиксации транзакции, то БД может оказаться поврежденной. Когда БД работает в этом режиме, никаких trace-файлов для БД не создается;
- режим oess_1::db::storage::trace_last указывает использовать write-ahead-log только для последней транзакции. Т.е. при фиксации транзакции все измененные страницы хранилища сначала записываются в trace-файл и только затем переносятся в основной файл данных. Такой режим работы обеспечивает восстановление БД после сбоя, если сбой произошел во время фиксации транзакции. После успешной фиксации транзакции в основной файл содержимое trace-файла сбрасывается. В этом режиме для БД создается и используется единственный trace-файл с расширением .00000000.trace;
- режим oess_1::db::storage::trace_all указывает использовать write-ahead-log при фиксации всех транзакций и, более того, не удалять содержимое write-ahead-log после успешной фиксации транзакции. Т.о. информация о всех транзакциях в БД остается в trace-файлах. На размер trace-файла в конфигурации БД накладываются ограничения. Как только trace-файл достигает максимального размера, создается новый trace-файл с увеличенным на единицу порядковым номером. Этот режим сохранения восстановочной информации наиболее медленный, т.к. при проведении каждой транзакции ОС приходися выделять новое дисковое пространство под растущие trace-файлы.
Режим oess_1::db::storage::trace_all может использоваться для двух целей:
- для полного восстановления БД после жестких сбоев, когда разрушается целостность файловой системы. Но для этого нужна процедура сохранения резервных копий trace-файлов;
- для восстановления БД на удаленной машине. Для этого настраивается репликация trace-файлов, а на удаленной машине регулярно запускается процедура восстановления БД по trace-файлам.
В режиме oess_1::db::storage::trace_all trace-файлы используются только для дописывания в них новых транзакций. Это позволяет организовать репликацию trace-файлов такими инструментами, как rsync и unison.
Для восстановления БД по trace-файлам предназначен инструемент oess_trace_replay. Он реализует т.н. обратный просмотр trace-файлов: trace-файлы просматриваются в обратном порядке и в БД восстанавливаются только самые последние значения страниц БД. После восстановления БД может оказаться, что часть trace-файлов стала не актуальными -- т.е. те страницы БД, которые были в них зафиксированны, уже были полностью перезаписаны последующими транзакциями. oess_trace_replay может удалить эти файлы, что уменьшает расход дискового пространства для БД.
В текущей версии ObjESSty для работы с БД необходимо:
Этот способ работы самый трудоемкий, но он позволяет:
- управлять временем жизни БД, т.к. через oess_1::db::site::localhost_t можно создавать и уничтожать БД, а так же проверять существование БД;
- связывать один localhost с несколькими БД;
- связывать несколько oess_1::db::cln::db_t с одной БД через один localhost. Это может быть необходимо если несколько потоков (нитей) должны работать параллельно с одной БД.
Более простой способ заключается в использовании класса oess_1::db::cln::local_db_t -- он сам создает localhost, connector, db и выполняет операцию attach. Пока, на практике, этот способ используется чаще всего.
Т.к. oess_1::db не имеет средств индексации и поиска информации в БД, то oess_1::db врядли может конкурировать со встраиваемыми БД типа SQLite, MetaKit или BerkeleyDB. Соответственно, oess_1::db не имеет перспектив применения в качестве "обычной БД". Но, oess_1::db обладает следующими достоинствами:
Эти качества могут позволить использовать oess_1::db или некоторые из ее компонентов (oess_1::db::storage::chain_storage_t, oess_1::db::storage::stream_storage_t), например, для сохранения документов сложных форматов в CAD/CAM инструментах, компьютерных играх и др.
Документация по ObjESSty. Последние изменения: Fri Oct 13 18:35:37 2006. Создано системой
1.4.7