Одним
из основных требований к развитым СУБД является надежность хранения баз данных.
Это требование предполагает, в частности, возможность восстановления
согласованного состояния базы данных после любого рода аппаратных и программных
сбоев. Очевидно, что для выполнения восстановлений необходима некоторая
дополнительная информация. В подавляющем большинстве современных реляционных
СУБД такая избыточная дополнительная информация поддерживается в виде журнала
изменений базы данных.
Общей целью журнализации
изменений баз данных является обеспечение возможности восстановления
согласованного состояния базы данных после любого сбоя. Поскольку основой
поддержания целостного состояния базы данных является механизм транзакций,
журнализация и восстановление тесно связаны с понятием транзакции. Общими
принципами восстановления являются следующие:
- результаты зафиксированных
транзакций должны быть сохранены в восстановленном состоянии базы данных;
- результаты незафиксированных
транзакций должны отсутствовать в восстановленном состоянии базы данных.
Это, собственно, и
означает, что восстанавливается последнее по времени согласованное состояние
базы данных.
Возможны следующие
ситуации, при которых требуется производить восстановление состояния базы
данных:
- Индивидуальный откат
транзакции. Тривиальной ситуацией отката транзакции является ее явное завершение
оператором ROLLBACK. Возможны также ситуации, когда откат транзакции
инициируется системой. Примерами могут быть возникновение исключительной
ситуации в прикладной программе (например, деление на ноль) или выбор транзакции
в качестве жертвы при обнаружении синхронизационного тупика. Для восстановления
согласованного состояния базы данных при индивидуальном откате транзакции нужно
устранить последствия операторов модификации базы данных, которые выполнялись в
этой транзакции.
- Восстановление после
внезапной потери содержимого оперативной памяти (мягкий сбой). Такая ситуация
может возникнуть при аварийном выключении электрического питания, при
возникновении неустранимого сбоя процессора (например, срабатывании контроля
оперативной памяти) и т.д. Ситуация характеризуется потерей той части базы
данных, которая к моменту сбоя содержалась в буферах оперативной памяти.
- Восстановление после поломки
основного внешнего носителя базы данных (жесткий сбой). Эта ситуация при
достаточно высокой надежности современных устройств внешней памяти может
возникать сравнительно редко, но тем не менее, СУБД должна быть в состоянии
восстановить базу данных даже и в этом случае. Основой восстановления является
архивная копия и журнал изменений базы данных.
Во всех трех случаях
основой восстановления является избыточное хранение данных. Эти избыточные
данные хранятся в журнале, содержащем последовательность записей об изменении
базы данных.
Возможны два основных
варианта ведения журнальной информации. В первом варианте для каждой транзакции
поддерживается отдельный локальный журнал изменений базы данных этой
транзакцией. Эти локальные журналы используются для индивидуальных откатов
транзакций и могут поддерживаться в оперативной (правильнее сказать, в
виртуальной) памяти. Кроме того, поддерживается общий журнал изменений базы
данных, используемый для восстановления состояния базы данных после мягких и
жестких сбоев.
Этот подход позволяет
быстро выполнять индивидуальные откаты транзакций, но приводит к дублированию
информации в локальных и общем журналах. Поэтому чаще используется второй
вариант – поддержание только общего журнала изменений базы данных, который
используется и при выполнении индивидуальных откатов. Далее мы рассматриваем
именно этот вариант.
10.1.
Журнализация и буферизация
Журнализация изменений
тесно связана не только с управлением транзакциями, но и с буферизацией страниц
базы данных в оперативной памяти. По причинам объективно существующей разницы в
скорости работы процессоров, оперативной памяти и устройств внешней памяти
буферизация страниц базы данных в оперативной памяти – единственный реальный
способ достижения удовлетворительной эффективности СУБД.
Если бы запись об
изменении базы данных, которая должна поступить в журнал при выполнении любой
операции модификации базы данных, реально немедленно записывалась бы во внешнюю
память, это привело бы к существенному замедлению работы системы. Поэтому записи
в журнал тоже буферизуются: при нормальной работе очередная страница
выталкивается во внешнюю память журнала только при полном заполнении записями.
Но реальная ситуация
является более сложной. Имеются два вида буферов – буфер журнала и буфер страниц
оперативной памяти, которые содержат связанную информацию. И те, и другие буфера
могут выталкиваться во внешнюю память. Проблема состоит в выработке некоторой
общей политики выталкивания, которая обеспечивала бы возможности восстановления
состояния базы данных после сбоев.
Проблема не возникает при
индивидуальных откатах транзакций, поскольку в этих случаях содержимое
оперативной памяти не утрачено и можно пользоваться содержимым как буфера
журнала, так и буферов страниц базы данных. Но если произошел мягкий сбой, и
содержимое буферов утрачено, для проведения восстановления базы данных
необходимо иметь некоторое согласованное состояние журнала и базы данных во
внешней памяти.
Основным принципом
согласованной политики выталкивания буфера журнала и буферов страниц базы данных
является то, что запись об изменении объекта базы данных должна попадать во
внешнюю память журнала раньше, чем измененный объект оказывается во внешней
памяти базы данных. Соответствующий протокол журнализации (и управления
буферизацией) называется Write Ahead Log (WAL) – "пиши сначала в журнал", и
состоит в том, что если требуется вытолкнуть во внешнюю память измененный объект
базы данных, то перед этим нужно гарантировать выталкивание во внешнюю память
журнала записи о его изменении.
Другими словами, если во
внешней памяти базы данных находится некоторый объект базы данных, по отношению
к которому выполнена операция модификации, то во внешней памяти журнала
обязательно находится запись, соответствующая этой операции. Обратное неверно,
т.е. если во внешней памяти журнале содержится запись о некоторой операции
изменения объекта базы данных, то сам измененный объект может отсутствовать во
внешней памяти базы данных.
Дополнительное условие на
выталкивание буферов накладывается тем требованием, что каждая успешно
завершившаяся транзакция должна быть реально зафиксирована во внешней памяти.
Какой бы сбой не произошел, система должна быть в состоянии восстановить
состояние базы данных, содержащее результаты всех зафиксированных к моменту сбоя
транзакций.
Простым решением было бы
выталкивание буфера журнала, за которым следует массовое выталкивание буферов
страниц базы данных, изменявшихся данной транзакцией. Довольно часто так и
делают, но это вызывает существенные накладные расходы при выполнении операции
фиксации транзакции.
Минимальным требованием,
гарантирующим возможность восстановления последнего согласованного состояния
базы данных, является выталкивание при фиксации транзакции во внешнюю память
журнала всех записей об изменении базы данных этой транзакцией. При этом
последней записью в журнал, производимой от имени данной транзакции, является
специальная запись о конце транзакции.
Далее представлено, как
можно выполнять операции восстановления базы данных в различных ситуациях, если
в системе поддерживается общий для всех транзакций журнал с общей буферизацией
записей, поддерживаемый в соответствии с протоколом WAL.
10.2.
Индивидуальный откат транзакций
Для того, чтобы можно было
выполнить по общему журналу индивидуальный откат транзакции, все записи в
журнале от данной транзакции связываются в обратный список. Началом списка для
незакончившихся транзакций является запись о последнем изменении базы данных,
произведенном данной транзакцией. Для закончившихся транзакций (индивидуальные
откаты которых уже невозможны) началом списка является запись о конце
транзакции, которая обязательно вытолкнута во внешнюю память журнала. Концом
списка всегда служит первая запись об изменении базы данных, произведенном
данной транзакцией. Обычно в каждой записи проставляется уникальный
идентификатор транзакции, чтобы можно было восстановить прямой список записей об
изменениях базы данных данной транзакцией.
Индивидуальный откат
транзакции выполняется следующим образом:
- Выбирается очередная запись
из списка данной транзакции.
- Выполняется противоположная
по смыслу операция: вместо операции INSERT выполняется соответствующая операция
DELETE, вместо операции DELETE выполняется INSERT, и вместо прямой операции
UPDATE обратная операция UPDATE, восстанавливающая предыдущее состояние объекта
базы данных.
- Любая из этих обратных
операций также журнализуется. Собственно для индивидуального отката это не
нужно, но при выполнении индивидуального отката транзакции может произойти
мягкий сбой, при восстановлении после которого потребуется откатить такую
транзакцию, для которой не полностью выполнен индивидуальный откат.
- При успешном завершении
отката в журнал заносится запись о конце транзакции. С точки зрения журнала
такая транзакция является зафиксированной.
10.3.
Восстановление после мягкого сбоя
К числу основных проблем
восстановление после мягкого сбоя относится то, что одна логическая операция
изменения базы данных может изменять несколько физических блоков базы данных,
например, страницу данных и несколько страниц индексов. Страницы базы данных
буферизуются в оперативной памяти и выталкиваются независимо. Несмотря на
применение протокола WAL, после мягкого сбоя набор страниц внешней памяти базы
данных может оказаться несогласованным, т.е. часть страниц внешней памяти
соответствует объекту до изменения, часть – после изменения. К такому состоянию
объекта не применимы операции логического уровня.
Состояние внешней памяти
базы данных называется физически согласованным, если наборы страниц всех
объектов согласованы, т.е. соответствуют состоянию объекта либо после его
изменения, либо до изменения.
Будем считать, что в
журнале отмечаются точки физической согласованности базы данных – моменты
времени, в которые во внешней памяти содержатся согласованные результаты
операций, завершившихся до соответствующего момента времени, и отсутствуют
результаты операций, которые не завершились, а буфер журнала вытолкнут во
внешнюю память. Назовем такие точки tpc (time of physical consistency).
Тогда к моменту мягкого
сбоя возможны следующие состояния транзакций, представленные на рисунке далее (
Рисунок
22).

Рисунок 22.
Состояния
транзакций
Предположим, что некоторым
способом удалось восстановить внешнюю память базы данных к состоянию на момент
времени tlpc. Тогда:
- Для транзакции T1 никаких
действий производить не требуется. Она закончилась до момента tlpc, и все ее
результаты отражены во внешней памяти базы данных.
- Для транзакции T2 нужно
повторно выполнить оставшуюся часть операций (redo). Действительно, во внешней
памяти полностью отсутствуют следы операций, которые выполнялись в транзакции T2
после момента tlpc. Следовательно, повторная прямая интерпретация операций T2
корректна и приведет к логически согласованному состоянию базы данных (поскольку
транзакция T2 успешно завершилась до момента мягкого сбоя, в журнале содержатся
записи обо всех изменениях, произведенных этой транзакцией).
- Для транзакции T3 нужно
выполнить в обратном направлении первую часть операций (undo). Действительно, во
внешней памяти базы данных полностью отсутствуют результаты операций T3, которые
были выполнены после момента tlpc. С другой стороны, во внешней памяти
гарантированно присутствуют результаты операций T3, которые были выполнены до
момента tlpc. Следовательно, обратная интерпретация операций T3 корректна и
приведет к согласованному состоянию базы данных (поскольку транзакция T3 не
завершилась к моменту мягкого сбоя, при восстановлении необходимо устранить все
последствия ее выполнения).
- Для транзакции T4, которая
успела начаться после момента tlpc и закончиться до момента мягкого сбоя, нужно
выполнить полную повторную прямую интерпретацию операций (redo).
- Наконец, для начавшейся после
момента tlpc и не успевшей завершиться к моменту мягкого сбоя транзакции T5
никаких действий предпринимать не требуется. Результаты операций этой транзакции
полностью отсутствуют во внешней памяти базы данных.
10.4.
Физическая согласованность базы данных
Каким же образом можно
обеспечить наличие точек физической согласованности базы данных, т.е. как
восстановить состояние базы данных в момент tpc? Для этого используются два
основных подхода: подход, основанный на использовании теневого механизма, и
подход, в котором применяется журнализация постраничных изменений базы данных.
При открытии файла таблица
отображения номеров его логических блоков в адреса физических блоков внешней
памяти считывается в оперативную память. При модификации любого блока файла во
внешней памяти выделяется новый блок. При этом текущая таблица отображения (в
оперативной памяти) изменяется, а теневая – сохраняется неизменной. Если во
время работы с открытым файлом происходит сбой, во внешней памяти автоматически
сохраняется состояние файла до его открытия. Для явного восстановления файла
достаточно повторно считать в оперативную память теневую таблицу отображения.
Общая идея теневого
механизма показана на следующем рисунке далее (Рисунок
23).

Рисунок 23
Идея
теневого механизма
В контексте базы данных
теневой механизм используется следующим образом. Периодически выполняются
операции установления точки физической согласованности базы данных (checkpoints
в System R). Для этого все логические операции завершаются, все буфера
оперативной памяти, содержимое которых не соответствует содержимому
соответствующих страниц внешней памяти, выталкиваются. Теневая таблица
отображения файлов базы данных заменяется на текущую (правильнее сказать,
текущая таблица отображения записывается на место теневой).
Восстановление к tlpc
происходит мгновенно: текущая таблица отображения заменяется на теневую (при
восстановлении просто считывается теневая таблица отображения). Все проблемы
восстановления решаются, но за счет слишком большого перерасхода внешней памяти.
В пределе может потребоваться вдвое больше внешней памяти, чем реально нужно для
хранения базы данных. Теневой механизм – это надежное, но слишком грубое
средство. Обеспечивается согласованное состояние внешней памяти в один общий для
всех объектов момент времени. На самом деле, достаточно иметь набор
согласованных наборов страниц, каждому из которых может соответствовать свой
набор времени.
Для достижения такого
более слабого требования наряду с логической журнализацией операций изменения
базы данных производится журнализация постраничных изменений. Первый этап
восстановления после мягкого сбоя состоит в постраничном откате незакончившихся
логических операций. Подобно тому, как это делается с логическими записями по
отношению к транзакциям, последней записью о постраничных изменениях от одной
логической операции является запись о конце операции. Для того, чтобы
распознать, нуждается ли страница внешней памяти базы данных в восстановлении,
при выталкивании любой страницы из буфера оперативной памяти в нее помещается
идентификатор последней записи о постраничном изменении этой страницы. Имеются и
другие технические нюансы.
В этом подходе имеются два
поднаправления. В первом поднаправлении поддерживается общий журнал логических и
страничных операций. Естественно, наличие двух видов записей, интерпретируемых
абсолютно по-разному, усложняет структуру журнала. Кроме того, записи о
постраничных изменениях, актуальность которых носит локальный характер,
существенно (и не очень осмысленно) увеличивают журнал.
Поэтому все более
популярным становится поддержание отдельного (короткого) журнала постраничных
изменений. Такая техника применяется, например, в известном продукте Informix
Online.
10.5.
Восстановление после жесткого сбоя
Понятно, что для
восстановления последнего согласованного состояния базы данных после жесткого
сбоя журнала изменений базы данных явно недостаточно. Основой восстановления в
этом случае являются журнал и архивная копия базы данных.
Восстановление начинается
с обратного копирования базы данных из архивной копии. Затем для всех
закончившихся транзакций выполняется redo, т.е. операции повторно выполняются в
прямом смысле.
Более точно, происходит
следующее:
- по журналу в прямом
направлении выполняются все операции;
- для транзакций, которые не
закончились к моменту сбоя, выполняется откат.
На самом деле, поскольку
жесткий сбой не сопровождается утратой буферов оперативной памяти, можно
восстановить базу данных до такого уровня, чтобы можно было продолжить даже
выполнение незакончившихся транзакций. Но обычно это не делается, потому что
восстановление после жесткого сбоя – это достаточно длительный процесс.
Хотя к ведению журнала
предъявляются особые требования по части надежности, в принципе возможна и его
утрата. Тогда единственным способом восстановления базы данных является возврат
к архивной копии. Конечно, в этом случае не удастся получить последнее
согласованное состояние базы данных, но это лучше, чем ничего.
Последний рассматриваемый
в данном разделе вопрос относится к производству архивных копий базы данных.
Самый простой способ – архивировать базу данных при переполнении журнала. В
журнале вводится так называемая "желтая зона", при достижении которой
образование новых транзакций временно блокируется. Когда все транзакции
закончатся, и база данных придет в согласованное состояние, можно производить ее
архивацию, после чего начинать заполнять журнал заново.
Можно выполнять архивацию
базы данных реже, чем переполняется журнал. При переполнении журнала и окончании
всех начатых транзакций можно архивировать сам журнал. Поскольку такой
архивированный журнал, по сути дела, требуется только для воссоздания архивной
копии базы данных, журнальная информация при архивации может быть существенно
сжата.
|