Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Электронные книги по ОС Linux -> Архитектура приложений с открытым исходным кодом

Архитектура приложений с открытым исходным кодом. Том 1. Глава 4. Berkeley DB

Оригинал: "Berkeley DB"
Авторы: Margo Seltzer and Keith Bostic
Дата публикации: 2012 г.
Перевод: Н.Ромоданов
Дата перевода: октябрь 2012 г.

4.9. Менеджер транзакций: Txn

Нашим последним модулем является менеджер транзакций, который связывает вместе отдельные компоненты и реализует транзакционные свойства ACID — атомарность (atomicity), целостность (consistency), изолированность (isolation) и долговечность (durability). Менеджер транзакций отвественен за начало и за завершение транзакций (транзакция либо выполняется, либо отменяется), за координацию с журнальным менеджером и с менеджером буферирования при создании контрольных точек транзакций и за общее управление процессом восстановления. Мы по порядку рассмотрим каждую из этих тем.

Джим Грей придумал сокращение ACID для описания ключевых свойств, предоставляемых транзакциями [Gra81]. Atomicity или атомарность означает, что все операции, выполняемые в рамках транзакции, осуществляются в базе данных как единое целое – либо они все выполняются в базе данных, либо они все не выполняются. Consistency или согласованность означает, что транзакция переводит базу данных из одного логически согласованного состояния в другое согласованное состояние. Например, если в приложении указывается, что все сотрудники должны быть назначены в отдел, который описан в базе данных, то это реализуется с помощью свойства согласованности (при правильно написанных транзакциях). Isolation или изолированность означает, что если рассматривать транзакцию, то она выполняется как бы последовательно, а не параллельно с какими-нибудь другими транзакциями. Наконец, durability или долговечность означает, что если транзакция выполнена, то она остается выполненной — никакой сбой не сможет отменить выполненную транзакцию.

Подсистема транзакций с помощью других систем осуществляет реализацию свойств ACID. В ней используются традиционные транзакционные операции begin (начало транзакции), commit (подтверждение выполнения транзакции) и abort (отмена выполнения транзакции), указывающие начальную и конечную точки транзакции. В ней также реализуется вызов вида prepare call, который помогает осуществлять двухфазное подтверждение выполнения транзакций — технологии реализации транзакционных свойств для распределенных транзакций, которые в данной главе не рассматриваются. Транзакционная операция begin создает идентификатор новой транзакции и возвращает в приложение дескриптор транзакции DB_TXN. Транзакционная операция commit создает журнальную запись commit, а затем инициирует запись журнала на диск (если в приложении не указано, что оно готово оказаться от свойства долговечности в обмен на более быструю обработку операции commit) , что гарантирует, что даже при наличии отказа транзакция будет выполнена. Транзакционная операция abort выполняет чтение в обратном направлении журнальных записей, принадлежащих указанной транзакции, отменяя каждую операцию, которая была сделана транзакцией и возвращает базу данных в состояние, предшествующее выполнению транзакции.

4.9.1. Обработка контрольных точек

На менеджер транзакций также возлагается задача создания контрольных точек. В литературе описан ряд различных методик создания контрольных точек [HR83]. В Berkeley DB используется вариант нечетких контрольных точек. По большому счету при создании контрольных точек необходимо записывать на диск состояние буферов в Mpool. Это потенциально дорогостоящая операция, и для того, чтобы избежать длительных отказов в обслуживании, важно, чтобы система, когда она это выполняет, продолжала обрабатывать новые транзакции. Перед тем, как создать контрольную точку, Berkeley DB сначала просматривает множество активных в данный момент транзакций и ищет наименьший номер LSN, записанный какой либо из этих транзакций. Этот номер LSN становится номером контрольной точки LSN. Затем менеджер транзакций просит Mpool сбросить на диск все буферы, в которых были сделаны изменения; запись этих буферов может, в свою очередь, вызвать выполнение операций записи на диск журнала. После того, как все буферы будут надежно сохранены на диске, менеджер транзакций затем записывает в журнал запись о контрольной точке, в которой указывается номер LSN. Эта запись указывает, что результаты всех операций, записанных в журнальных записях перед контрольной точкой с номером LSN, надежно сохранены на диске. Поэтому журнальные записи, находящиеся перед записью о контрольной точке с номером LSN, больше не требуются для восстановления. Это подразумевает следующее: во-первых, система может повторно использовать место в журнальных файлах, находящихся перед записью о контрольной точке LSN. Во-вторых, при восстановлении нужно обрабатывать только записи, находящиеся после контрольной точки LSN, поскольку обновления, описываемые в записях перед контрольной точкой LSN, уже отражены в состоянии на диске.

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

4.9.2. Восстановление

Последней частью транзакционной головоломки является процесс восстановления. Цель восстановления — перевести базу данных, хранящуюся на диске, из потенциально несогласованного состояния в согласованное состояние. В Berkeley DB используется довольно обычная двухпроходная схема, которая, в общих чертах, «связана с последней контрольной точкой с номером LSN, отменой всех транзакций, которые никогда не были подтверждены, и повторного выполнения транзакций, которые были подтверждены». Детали выглядят несколько сложнее.

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

Когда начинается процесс восстановления, менеджер транзакций для того, чтобы определить в журнале место последней записи о контрольной точке, берет из журнального менеджера значение cached_ckp_lsn. В этой записи есть номер контрольной точки LSN. Berkeley DB должна осуществлять восстановление от этой контрольной точки LSN, но для того, чтобы это сделать, она должна реконструировать отображение идентификаторов журнальных файлов, которое было в момент создания контрольной точки LSN; эта информация есть для контрольной точки, находящейся перед контрольной точкой LSN. Таким образом, Berkeley DB должна искать последнюю запись о контрольной точке, которая находится перед контрольной точкой с номером LSN. Чтобы облегчить этот процесс, в записях о контрольных точках есть не только номер контрольной точки LSN, но и номер LSN предыдущей контрольной точки. Процесс восстановление начинается с последней контрольной точки и происходит в обратном направлении по записям о контрольных точках до тех пор, пока не будет найдена запись о контрольной точке, находящаяся перед записью о контрольной точке с номером LSN. Переход от одной записи к другой происходит с использованием поля prev_lsn, которое есть в каждой записи о контрольной точке. Алгоритм следующий:

ckp_record = read (cached_ckp_lsn)
ckp_lsn = ckp_record.checkpoint_lsn
cur_lsn = ckp_record.my_lsn
while (cur_lsn > ckp_lsn) {
    ckp_record = read (ckp_record.prev_ckp)
    cur_lsn = ckp_record.my_lsn
}

Чтобы реконструировать отображения идентификаторов журнальных файлов, процесс восстановления выполняет последовательное чтение, которое начинается с контрольной точки, выбранной в предыдущем алгоритме, до конца журнала. Когда будет достигнут конец файла, восстановленные отображения будут точно соответствовать отображениям, которые были в момент остановки системы. Также во время этого прохода отслеживаются все встретившиеся записи с операцией подтверждения транзакций commit и записываются их идентификаторы транзакций. Любая транзакция, для которой есть журнальные записи, но идентификатор транзакций отсутствует в записи операции подтверждения commit, либо была отменена, либо никогда не завершалась и ее следует трактовать как отмененную. Когда процесс восстановления достигнет конца журнала, направление движения изменится на обратное, и журнал будет читаться в обратном направлении. Для того, чтобы определить нужно ли для встретившейся журнальной записи о транзакции делать отмену операции, из каждой такой записи будет взят идентификатор транзакции, который будет сверен со списком транзакций, выполнение которых было подтверждено, Если процесс восстановления определит, что этот идентификатор транзакций отсутствует в списке подтвержденных транзакций, он определит тип записи и вызовет для этой журнальной записи процедуру восстановления, указывая ей отменить описанную операцию. Если этот идентификатор транзакций есть в списке подтвержденных транзакций, то процесс восстановления будет игнорировать ее на обратном проходе. Этот обратный проход будет продолжаться до контрольно точки с номером LSN [1]. Наконец, процесс восстановления прочитает журнал в последний раз в прямом направлении, на этот раз повторно выполняя действия для каждой журнальной записи, принадлежащей к подтвержденным транзакциям. Когда этот финальный проход будет завершен, процесс восстановления создаст контрольную точку. В этой точке база данных будет полностью согласованной и будет готова к началу работы приложения.

Таким образом, процесс восстановления можно резюмировать следующим образом:

  1. Находим среди недавних контрольных точек контрольную точку, которая предшествует контрольной точке с номером LSN.
  2. Читаем журнал в прямом направлении для того, чтобы восстановить отображения идентификаторов журнальных файлов, и создаем список завершенных транзакций.
  3. Читаем обратно до контрольной точки с номером LSN, отменяя все операции для незавершенных транзакций.
  4. Читаем в прямом направлении, повторно выполняя все операции для подтвержденных транзакций.
  5. Создаем контрольную точку.

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

Четырнадцатый урок конструирования

Процесс восстановление базы данных является сложной темой, его трудно написать и еще труднее отладить, поскольку он не должен происходить часто. В своей лекции при получении премии Тьюринга, Эдсгер Дейкстра (Edsger Dijkstra) утверждал, что программирование является трудным по своей сути и нужно признать, что мы не в состоянии справиться с этой задачей. Наша цель, как архитекторов и программистов, заключается в использовании имеющихся в нашем распоряжении инструментов: проектирования, декомпозиции проблем, просмотров, тестирования, конвенции об именах и стилях и других хороших приемов, сводящих проблемы программирования к задачам, которые мы можем решить.

4.10. Заключение

В настоящее время проекту Berkeley DB более двадцати лет. Это, возможно, было первое транзакционное хранилище типа "ключ/значение" и он является прародителем движения NoSQL. Система Berkeley DB продолжает использоваться в качестве основной системы хранения в сотнях коммерческих продуктов и тысячах приложений с открытым кодом (включая движки SQL, XML и NoSQL) и установлена на миллионах компьютерах по всему миру. Уроки, которые мы получили в ходе ее развития и сопровождения, отражены в ее коде и обобщены в советах по конструированию, изложенных выше. Мы предлагаем их в надежде, что другие разработчики программного обеспечения и архитекторы посчитают их полезными.

Примечания

1. Обратите внимание, что нам нужен обратный проход только до контрольной точки с номером LSN, а не до контрольной точки, предшествующей ей.

Creative Commons

Перевод был сделан в соответствие с лицензией Creative Commons. С русским вариантом лицензии можно ознакомиться здесь.


Назад К оглавлению книги Вперед