Определим понятие согласованного состояния базы и на его основе введем понятие транзакции как набора действий, выполняющихся как единое целое и переводящих базу из одного согласованного состояния в другое.
Изучим свойства транзакций и языковые средства для их оформления.
Покажем как транзакции обеспечивают поддержку целостности данных, параллельную работу пользователей и восстановление данных при откатах транзакций и сбоях аппаратуры.
Дадим классификацию ограничений целостности.
Введем понятие ссылочного ограничения целостности и покажем как оно поддерживается с помощью триггера.
Опишем феномены — проблемы, возникающие при параллельной работе—и на их основе определим уровни изолированности пользователей.
Изучим проблемы блокирования ресурсов и возникновения тупиков. Бегло рассмотрим многоверсионные данные.
В заключительных разделах рассмотрим роль транзакций в организации откатов и восстановлении данных после сбоев.
Базы данных, на которых строятся информационные системы, должны обеспечить одновременный доступ к данным для многих, иногда сотен и тысяч, пользователей. Оказывается, при параллельной работе пользователей результаты вычислений могут зависеть от временных соотношений между действиями пользователей, от того, в какое время и в какой последовательности они выполняются. Как избежать этой зависимости? Как организовать чтение данных одним пользователем и одновременно их изменение другим? Как добиться, чтобы деньги, снятые с одного счета, и, из-за сбоя системы, не зачисленные на другой счет, не могли пропасть бесследно? Как восстановить данные при откатах и сбоях?
Все эти проблемы решаются за счет использования механизма транзакций, который должен работать в базах с любыми моделями данных.
Информационные системы, предназначенные для единственного пользователя, в настоящее время практического интереса не представляют. Но даже они нуждаются в транзакциях. Всегда существует наборы действий, такие что невыполнение некоторых из них приводит базу в противоречивое состояние. Кроме того, один пользователь может запустить несколько потоков вычислений, обращающихся, может быть, к одним данным. И тут ему потребуется механизм, обеспечивающий независимость работы этих потоков.
Транзакции обеспечивают:
В этой лекции будут рассмотрены транзакции и весь круг вопросов, связанных с перечисленными тремя аспектами их применения. Сложности для начинающих возникают из-за необходимости рассмотрения во времени нескольких процессов, может быть распределенных в некоторой сети.
Пусть при выполнении двух последовательно выполняющихся действий после успешного выполнения первого из них возник сбой (рисунок 6.1).
(рис 6.1) Что может произойти при сбое сервера
В приведенном примере это означает, что сумма, снятая с первого счета не попала на второй счет. Хуже того, сам факт исчезновения денег никак не зафиксирован. Если вас этот неприятный факт не расстроил, и вы считаете, что можно повторить все с начала, то тут сказывается одна нехорошая привычка, вырабатываемая ориентированностью наших студентов, фактически, на научные вычисления. Скажем, складывал я две матрицы, а они у меня не сложились. Ну, бог с ними — я их сложу еще раз. Но, если вы написали систему, которая отправила миллиард долларов, и он никуда не пришел, то, скорее всего, усовершенствовать ее будет уже другой человек, а вы будете искать новую работу. Вот, собственно, в этом разница в отношении к надежности вычислений в науке и бизнесе. Вообще говоря, бизнес-приложения должны работать всегда, независимо от того, исправен ли компьютер или нет, заболели ли вы или еще что-то случилось, ночь сейчас или день. Ведь есть организации, работающие круглые сутки. Хорошим тоном считается обеспечивать работоспособность в самом тяжелом режиме, который принято называть "24*7" (двадцать четыре на семь). Это означает 24 часа в сутки 7 дней в неделю. Это очень тяжелый режим, и без специальных мер он сам по себе не обеспечивается. Представьте, что в вашем домашнем компьютере нужно поменять или добавить жесткий диск. Как это сделать без останова системы?
(рис 6.2) Возможные ошибки
Для рассмотрения параллельной работы двух пользователей вспомним, что "сальдо" —это остаток на счете. Посчитать сальдо по группе счетов — это, попросту говоря, сложить их остатки. Обычный арифметический цикл, который известен всем школьникам. На рисунке 6.2 слева показана работа такого цикла. Сначала сальдо С по группе счетов равно 0, потом прибавляем к нему остаток первого счета, второго счета и т.д., счета тысячного. Как учили в школе? Написали цикл, проверили его на примере. Дальше работает всегда правильно. Но у нас то пользователь не один. Транзакция изображенная слева выполняет циклическое суммирование. Но после того, как счет 1 уже учтен, и до того, как учтен счет 1000, вторая транзакция, изображенная справа, снимает 200 руб. со счета 1, и зачисляет их (то есть прибавляет 200 руб.) на счет 1000. Теперь в подсчете сальдо по группе счетов появится ошибка в 200р. Почему? Потому что счет 1 мы обрабатывали в неизмененном состоянии, а счет 1000 в измененном. А что будет, если во времени вторая транзакция будет сдвигаться? Ну, очевидно, если она будет выполнена до первой (левой на рисунке) транзакции или после нее, то ничего страшного не случится, все будет посчитано правильно. А если транзакции начнут выполняться на одном промежутке времени то, с какого-то момента появится ошибка. Обратите внимание, довольно странная, неожиданная для начинающих ситуация — арифметический цикл, оказывается, может считать данные неправильно, если одновременно идет некоторый другой процесс, имеющий доступ к тем же данным. Транзактный механизм должен подобные казусы исключить.
В соответствии с общим принципом, на котором построен настоящий курс, мы все должны "проделать ручками", что в этом разделе я особенно вам советую, если, конечно, у вас уже нет опыта параллельных вычислений или работы с транзакциями. Параллельную работу двух пользователей моделируем включением двух терминалов Cache (рисунок 6.3). При этом транзакций мы пока не используем.
(рис 6.3) Ошибка при подсчете сальдо при параллельной работе без транзакций
Работа с тысячью счетов в примере может затянуться, поэтому счетов у нас всего три штучки (Сч1, Сч2, Сч3). На них 500 руб., 700 руб. и 800 руб. соответственно. Сальдо С по группе этих трех счетов в начале вычисления равно нулю. Переменная t — это разметочная переменная, отмечающая моменты времени 0, 1, 2, 3, 4. Она определяет порядок действий. Сначала вы должны выполнить ту строчку в левом терминале, где есть присваивание t = 0. После него выполняете первую строчку второго терминала там, где t присваивается значение 1, и т.д. Если вам трудно следить за текстом, повторите все сами. Как и ожидалось, сальдо вычисляется с ошибкой.
Определение. База находится в согласованном состоянии, если выполняются все записанные в ней ограничения целостности. Во время выполнения транзакции база может рассогласовываться.
Вернемся к рассмотренной ранее операции — переводу денег с одного счета на другой. Эта операция включает две атомарные (элементарные, неделимые) операции:
Если СУБД будет выполнять (или не выполнять) эти две операции только вместе, то база будет всегда находиться в согласованном состоянии и не будут возникать ситуации, при которых суммы, снятые с одного счета, ни на какой другой счет не поступают.
Определение. Объединение нескольких операций в неделимое целое называют транзакцией.
Возникает вопрос: Что делать, если часть данных уже изменена, а остальные измениться не могут? Ответ: Откатить выполненные изменения, то есть вернуться к ранее существовавшим значениям. Но как выполнить откат? Это мы выясним позже. Здесь отметим, что создание действий, аннулирующих изменения в базе, выполненные транзакцией, не приемлемо уже потому, что генерация таких инструкций и их запуск на исполнение потребовали бы слишком много времени.
При отказах информационной системы желательно сохранить сведения о попытке выполнения транзакции. Если при этом транзакция была выполнена частично, то уже выполненную часть операций необходимо откатить. После восстановления системы транзакция должна быть выполнена повторно.
Транзакция — это последовательность действий, выполняющаяся как единое целое и переводящая базу данных из одного согласованного состояния в другое согласованное состояние. Вы должны помнить, что ограничения целостности определенные в бизнесе, могут быть не прописаны явно. Так, в примере, приведенном на рисунке 6.1, ограничение целостности можно сформулировать так: деньги, снятые со счета, должны быть зачислены на другой счет. Может быть задействована некоторая цепочка счетов. Например, если вы отправляете деньги через свой банк клиенту другого банка, то деньги сначала поступают на ваш клиентский счет, с него — на корреспондентский счет вашего банка в другом банке, а уже оттуда на клиентский счет получателя. Всегда необходимо додумывать ситуацию до конца, тогда она будет прозрачна, понятна, и вы с ней справитесь. В транзакциях используют следующие инструкции:
Транзакции обладают набором свойств (рисунок 6.4), характеризуемым аббревиатурой АСИД (ACID):
(рис 6.4) Свойства транзакции
Замечание. Соответствующая английская аббревиатура ACID образована первыми буквами терминов Atomicity, Consistency, Isolation, Durability.
Свойства АСИД транзакций, за исключением атомарности, не всегда выполняются в полном объеме.
Будем рассматривать так называемые плоские транзакции, обладающие одним управляющим слоем. Именно этот тип широко распространен в современных СУБД.
Существует два способа запуска транзакции:
Транзакция завершается, успешно или не успешно, специальными командами или по одному из событий:
В языке COS начало транзакции — команда TSTART, успешное завершение — TCOMMIT, неуспешное — TROLLBACK.
В таблице 6.1 приведены команды управления данными для Cache.
| Команда SQL | Команда COS | Описание |
| %BEGTRANS | TSTART | Начало транзакции |
| COMMIT [WORK] | TCOMMIT | Успешное завершение транзакции |
| ROLLBACK [WORK] | TROLLBACK | Неуспешное завершение транзакции |
Замечание. В Oracle любые действия вкладываются в транзакции, в Cache транзакции используются по желанию пользователя. Пользователь Cache, работающий вне транзакции как бы "не замечает",
чужих транзакций.
Создадим и откомпилируем программу ^tr1 содержащую транзакцию:
SET ^tt(0)=0, ^tt(1)=1 ;значения до транзакции TSTART ;начало транзакции SET ^tt(0)= "Значение 1 из транзакции" READ x ;приостанавливает исполнение SET ^tt(1)= "Значение 2 из транзакции" TCOMMIT /* конец транзакции - успешное завершение */ WRITE "^tt(0)= ",^tt(0),!, "^tt(1)= ", ^tt(1) QUIT
Вы помните, что в Cache данные бестиповые, тип данных определяется по контексту. Например запись -x приведет к интерпретации x как числа. В программе ^tr1 назначаем глобалам ^tt(0) и ^tt(1) цифровые значения. А в транзакции присваиваем им текстовые значения. READ x используется для остановки исполнения программы за счет ожидания ввода данных человеком. В конце печатается текст ^tt(0) =.. ^tt(1) =.. и сами значения этих глобалов. Если транзакция откатывалась, то глобалам ^tt(0) и ^tt(1) возвращаются цифровые значения.
Запустим два терминала.
Просмотрим два варианта исполнения транзакции:
x (левый терминал) — см. рисунок ниже. Сначала в левом терминале исполняется программа ^tr1 , затем в правом читается значение глобала. Транзакция завершилась успешно, поэтому возвращены текстовые значения.
-см. рисунок ниже. Правый терминал читает ^tt(0) первый раз до выключения левого терминала, а второй раз — после его выключения. Вывод: выключение терминала откатывает транзакцию.
Переходим к первой задаче, которую решают транзакции — соблюдение ограничений целостности. Что нужно для проверки любого ограничения? Прежде всего, база должна быть активной, то есть иметь способность совершать что-то, сверх того, о чем ее попросили. Пусть задано ограничение первичный ключ. Когда мы приказываем ввести строку, СУБД сама обнаруживает, что такое ограничение имеется и запускает процедуру, проверяющую, не повторяются ли значения в ключевых столбцах. И, наверное, она не позволит ввести строку с повторяющимся значением ключа. Хорошо бы еще получить какое-то сообщение о возможном нарушении ограничения целостности.
Чаще всего источник активности — специальные процедуры, называемые триггерами. Но это не единственный вариант. Триггеры напоминают резидентные программы. Они начинают работать только при запуске некоторого процесса, называемого триггерным событием. В число таких событий всегда входят вставка, удаление и обновление строк (по-английски insert, update и delete). В современных базах данных триггеры могут реагировать и на другие события. Поскольку ограничения целостности определены для хранимых объектов базы, триггеры прикрепляются к этим объектам.
Рассмотрим ограничения целостности и реакции СУБД на их нарушения. Приведем примеры ограничений целостности, в том числе ссылочных. Изучим классификацию ограничений целостности по способам реализации, по времени проверки и по области действия. В частности, уточним, что такое триггеры.
Определение. База данных находится в согласованном (целостном) состоянии, если выполнены все заданные в ней ограничения целостности.
Для распределенных баз данных важна еще согласованность копий фрагментов данных в узлах. Меру различия между копиями данных в узлах сети (репликами) называют связностью данных. Распределенная база не может обеспечить одновременно высокую связность и высокую скорость работы. Чем выше связность, тем больше трафик по сети и тем выше загрузка серверов, реплицирующих данные.
Система управления базами данных обязана реагировать на любые попытки нарушения целостности.
Два основных типа реакции СУБД:
Классификация ограничений целостности проводится по следующим основаниям:
Различают
Декларативная поддержка ограничений целостности задается средствами языка определения данных (ЯОД, он же DDL — Data Definition Language).
Приведем пример для языка SQL, который мы еще не изучали. Но это нам не сильно помешает. Создаем таблицу, используя следующую инструкцию:
CREATE TABLE person (persid INTEGER PRIMARY KEY, persname CHAR(30) NOT NULL);
Если прочитать ее на русском языке, все станет понятно. Итак: "СОЗДАТЬ ТАБЛИЦУ с названием person, содержащую два столбца: persid типа ЦЕЛОЕ с ограничением ПЕРВИЧНЫЙ КЛЮЧ и persname типа СИМВОЛЬНОЕ с длиной 30 символов и ограничением НЕЛЬЗЯ НЕ ЗАПОЛНЯТЬ".
Процедурные ограничения целостности реализуются использованием триггеров и хранимых процедур.
Пример ограничения целостности, которое обычно реализуется процедурно: "Начальник отдела может изменять заработную плату только своим подчиненным и только в сторону уменьшения". Для того, чтобы реализовать это ограничение декларативно необходимо задать формулу, позволяющую найти строки для всех подчиненных и как-то указать, что будущие значения зарплат не больше уже существующих. Этого сделать нельзя, так как в базе нет этих будущих значений.
Деление на процедурные и декларативные ограничения целостности это не дихотомия, так как оба вида ограничений в действительности поддерживаются специальными процедурами. Просто в каждой СУБД определен список имен ограничений, которые можно записать декларативно. Остальные ограничения, для которых нет таких имен, реализуются явным заданием в процедурной части приложения.
Триггеры — это процедуры специального вида, прикрепленные к объекту базы, например, таблице, и срабатывающие при наступлении событий из некоторого набора событий. Наиболее известны триггеры, реализуемые в языке SQL.
В набор событий обычно входят вставка записи (инструкция INSERT в SQL), удаление записи (инструкция DELETE) и обновление записи (инструкция UPDATE).
Триггер может выполняться до отработки события (триггер BEFORE) и после его отработки (триггер AFTER).
Выделяют также триггеры уровня строки (записи), срабатывающие один раз на каждую выбранную строку, и триггеры уровня выражения, срабатывающие один раз при обработке набора записей (например, при входе в таблицу или выходе из нее). Итого 3 * 2 * 2 = 12 типов триггеров на один набор записей (таблицу).
Для проверки ограничения на возраст принимаемого на работу создают триггер на команду INSERT, срабатывающий до ее выполнения (нельзя допускать запись о неправильном приеме — значит выбираемый тип — BEFORE). В теле триггера вычисляют возраст, проверяют условие $$21\le возраст \le 45$$. Если условие не выполнено, запись о приеме аннулируется (рисунок 6.5).
(рис 6.5)
Рассмотрим пример ссылочного ограничения целостности.
Пусть задана схема, состоящая из двух таблиц dept (отделы) и person (сотрудники) (таблицы 6.2 и 6.3).
Столбцы таблицы dept (deptid, deptname, deptquan): deptid — идентификатор подразделения, deptname —имя подразделения, deptquan — количество сотрудников в подразделении.
Столбцы таблицы person(persid, persname,deptid): persid — идентификатор сотрудника, persname —имя сотрудника, deptid —идентификатор подразделения, в котором работает сотрудник.
| DEPTID | DEPTNAME | DEPTQUAN |
| 1 | Цех разлива | 3 |
| 2 | Транспортный цех | 2 |
| PERSID | PERSNAME | DEPTID |
| 1 | Иванов | 1 |
| 2 | Петров | 2 |
| 3 | Сидоров | 1 |
| 4 | Злобис | 2 |
| 5 | Вредис | 1 |
Имеющееся здесь ограничение ссылочной целостности определяется условием: поле deptquan должно содержать количество сотрудников, записанных в таблице person для каждого отдела.
Если уволим Иванова, недостаточно просто его вычеркнуть, потому что при этом изменится численность первого отдела. Ее нужно пересчитать, вычтя единицу из значения в столбце deptquan. А если переведем Иванова в транспортный цех, то необходимо не только изменить строку с Ивановым в таблице dept, но еще и прибавить к численности транспортного цеха единицу, вычтя единицу из численности цеха розлива. Иначе таблица "Отделы" окажется неправильно заполненной.
При наличии ссылочного ограничения, изменение в одной таблице обязательно влечет за собой изменение в другой таблице, причем изменение по вставке одно, по удалению другое, по изменению третье.
Например, прием нового сотрудника не может быть выполнен одной операцией. Необходимо вставить запись в таблицу person и одновременно увеличить значение поля deptquan на 1. Выполняем два шага, которые, конечно же, необходимо включить в транзакцию:
Вставить запись о сотруднике в таблицу person
INSERT INTO person VALUES (6, 'Петросян', 1).
(ВСТАВИТЬ в person строку (6, "Петросян", 1).)
Увеличить значение поля deptquan
UPDATE dept SET depquan=deptquan+l WHERE deptid=l.
(ОБНОВИТЬ dept, УСТАНОВИВ ЗНАЧЕНИЕ deptquan = deptquan+1 ДЛЯ СТРОКИ В КОТОРОЙ deptid= 1). Для увольнения и перемещения сотрудников необходимы свои транзакции.
Замечание. Пока мы не изучили инструкции SELECT, INSERT и UPDATE языка SQL, воспринимайте их просто как фразы, написанные на фрагменте естественного (как бы английского) языка, который понимает СУБД.
Пример 1. Возраст сотрудника не может быть меньше 21 и больше 45 лет (Декларативное ограничение типа check).
Пример 2. Атрибут "Табельный номер" уникален. (Декларативное ограничение—уникальный или первичный ключ).
Пример 3. Сотрудник обязан числиться в одном из отделов. (Декларативное ссылочное ограничение целостности).
Пример 4. Сумма накладной равняется сумме произведений цен товаров на количество товаров для всех товаров, входящих в накладную. (Определение вычислимого столбца.).
Замечание. Ограничение целостности может определять не только значения атрибутов, но и особенности удаления или обновления данных. Например, в схеме "Отдел" — "Сотрудник" удаление отдела может вызвать удаление его сотрудников или их перевод в другие отделы.
Задание ограничения целостности приходит в базу из бизнес-модели. Для проверки правильности этого перехода полезно определить, какие требования предлагает бизнесу это ограничение, то есть выполнить обратный переход. Например, задана уникальность табельного номера. Что это означает для бизнеса? Какой номер присваивается работнику при повторном приеме? Существуют ли сотрудники, не имеющие номера?
По времени проверки выделяют два вида ограничений:
COMMIT.Пример немедленно проверямого ограничения — первичный ключ. Пример ограничения с отложенной проверкой — ссылочные ограничения (таблицы 6.2 и 6.3).
Итоговая классификация ограничений целостности приведена в разделе 6.6.
Ограничения на допустимые связи могут определяться самими связываемыми сущностями. Но может понадобится учесть еще и другие сущности, как-то связанные с проверяемыми сущностями и не связанные напрямую проверяемой зависимостью.
В типичном случае имеется несколько взаимно исключающих связей. Например, сущность A может быть связана бинарной связью с B или C, но если существует связь A с B, то не может существовать связь A с C, и наоборот.
Возможно, наличие или отсутствие связи определяется состоянием экземпляров сущностей, которые могут входить в связь. Под состоянием здесь понимаются значения атрибутов сущностей, имеющих смысл "состояние".
Заметим, что реализация рассматриваемого класса ограничений достаточно сложна.
Одна из возможных связей — наследование. Реализация ограничений на наследование будет рассмотрена позже при изучении объектных моделей.
Любая система в бизнесе представляет множество действующих лиц — акторов — работающих одновременно и организованно. Актором может быть человек, программа или машина. Информационная система должна как-то отражать эту многоакторность. Конечно, в ней самой могут появиться параллельные процессы, не имеющие прямых аналогов в бизнесе.
В этом разделе будут рассмотрены проблемы, возникающие при параллельной работе транзакций. В соответствии с традицией будем называть эти проблемы феноменами. На основе феноменов зададим уровни изолированности транзакций. В определениях изолированности будет использован стандарт версии SQL-92 языка SQL, но знание SQL и, тем более, стандарта для освоения материала не требуется.
В заключительной части раздела рассмотрим блокировки ресурсов и эффекты, возникающие при блокировании.
При отсутствии блокировок или других средств, ограничивающих доступ к ресурсам, используемым транзакцией, некоторые изменения могут быть утеряны.
Пример (таблицы 6.4 и (6.5): На счет Acc с начальным сальдо 100, зачисляют первой транзакцией 20 руб., а второй 100 руб. Если первая транзакция записывает измененные данные в базу и завершается после того, как вторая транзакция их прочла, но до того, как она их записала, то изменения, внесенные первой транзакцией, теряются.
| Транзакция 1 | Время | Транзакция 2 |
| Начало Тр1 | ||
| Чтение X=Acc | to | |
| Начало Тр2 | ||
| ti | Чтение X=Acc | |
| t2 | Зачисление X=X+100 | |
| Зачисление X=X+20 | t3 | |
| Запись в базу Acc=X | t4 | |
| COMMIT | ||
| Конец Тр1 | ||
| t5 | Запись в базу Acc=X COMMIT | |
| Конец Тр2 |
USER>TStart USER>Set Х=^Асс, t=Q USER>Set Х=Х+20, t = 3 USER>Set ^Асс=Х, t = 4 USER>TCommit USER> USER>TStart |
USER>Set Х=^Асс, t=1 USER>Set X=X+100, t=2 USER>Set ^Acc=X, t = 5 U5ER>TComoit USER>Write ^Acc 2 00 USER>| |
Начальное значение ^Acc=100. TSTART — начало транзакции, TCOMMIT — ее успешное завершение. Переменная t, как и раньше, использована вместо комментария для задания отметки очередности исполнения команд, соответствующей моментам времени $$t = 0, t = 1, \dots$$.
Феномен возникает, когда читаются данные незавершенной транзакции, которые впоследствии откатываются. Начальное значение Acc в примере равно 100 (таблицы 6.6 и 6.7).
| Транзакция 1 | Время | Транзакция 2 |
| Начало Тр1 | ||
| Изменение Acc=Acc+20 | t1 | |
| Начало Тр2 | ||
| t2 | Чтение и изменение | |
| X=Acc+50 | ||
| ROLLBACK —возврат к Acc=100 | tз | |
| Конец Тр1 | ||
| t4 | Acc=X | |
| Конец Тр2. Значение Acc не 150, а 170 |
USER>TStart USER>Set ^Acc=^Acc+20, t=1 USER>TRollback USER>Sett=3 Rollback Write ^Acc 100 USER> |
USER>TStart USER>S X=^Acc+50, t=2 USER>Set ^Acc=X ,t=4 USER>TCommit USER>Set t=5 USER>Write ^Acc 170 |
Разберите сами приведенный ниже пример (таблицы 6.8 и 6.9).
| Транзакция 1 | Время | Транзакция 2 |
| Начало Тр2 | ||
| t1 | Чтение Acc (Acc=100) | |
| Начало Тр1 | ||
| Изменение Acc=300 | t2 | Проверка Acc<200 — Да |
| t3 | Чтение Acc (Acc=300) Проверка Acc<200 — Нет |
USER>TStart U5ER>Set t=2, ^Acc=300 USER> |
USER>TStart USER<Set t=l USER>If ^Acc<200 Write "Да"Да USER>Set t=3U SER>If ^Acc<200 Write "Да" USER>| |
Ниже мы изучим блокировки, то есть защиту от попыток совместного использования записей. За счет блокировок на уровне строк перечисленные выше три феномена могут быть исключены. Но останется еще одна проблема.
Пусть транзакция Тр1 выбирает группу строк из таблицы Т, удовлетворяющих условию У1. Затем транзакция Тр2 вставляет в Т еще одну строку, удовлетворяющую условию У1. При повторной выборке строк транзакцией Тр1, к полученному в предыдущей выборке набору добавляется еще одна строка, называемая фантомом, привидением.
Стандарт SQL-92, основываясь на перечисленных феноменах, определяет четыре уровня изолированности:
Данные по уровням изолированности сведены в таблицу 6.10. Знак "+" в ней означает наличие феномена, а знак "-" — его отсутствие.
| Уровень | Потерянные | "Грязное" | Неповторяющееся | Фантомы |
| изоляции | изменения | чтение | чтение | |
| Serializable | + | + | + | + |
| Repeatable read | + | + | + | - |
| Read commited | + | + | - | - |
| Read uncommited | + | - | - | - |
Так зачем нужны "плохие" виды транзакций, и особенно, Read uncommited?
Ответ простой. С одной стороны, чем выше уровень изоляции, тем меньше шансов на параллельную работу транзакций. С другой стороны, полная изоляция не всегда полезна.
Пример: Библиотекари регулярно работают с каталогом, открывая транзакции для сверки и изменения каталога. Эти работы могут длиться часами. Что Вы предпочитаете, ждать появления исправленного каталога, или просматривать его в любой момент времени, может быть, иногда получая неправильные данные?
Замечание. Правильнее было бы дать библиотекарям работать с копией каталога, а когда они закончат работу быстро обновить каталог, запретив остальным пользователям доступ на несколько минут или секунд.
Как обеспечить правильную работу транзакций, обращающихся к одним и тем же ресурсам?
Два основных способа:
По степени разделяемости блокируемых ресурсов различают:
Блокировки различают еще по размерам блокируемого ресурса: поле записи, запись, отношение, страница (блок базы), группа отношений, вся база. Забегая вперед, отметим, что в современных СУБД используется два способа хранения таблиц — строчный и столбцовый. Чаще данные хранятся строками. В этом случае минимальный объем блокировки — строка. В столбцовых базах —поле. Выполняется свойство иерархичности. Если объект верхнего уровня обладает блокировкой, то такой же блокировкой обладает его подобъект нижнего уровня.
Взаимодействие блокировок:
Рассмотрим доступ по чтению и записи. Возможный протокол доступа к данным по чтению и записи с блокировками определяется следующими правилами:
Если транзакция B начинается позже транзакции A, то успешность блокирования объекта базы транзакцией B определяется следующей матрицей совместимости блокировок (таблица 6.11):
| Транзакция A наложила блокировку | Транзакция B пытается наложить блокировку | |
| S-блокировку | X-блокировку | |
| S-блокировку | + | - (конфликт R-W) |
| X-блокировку | - (конфликт W-R) | - (конфликт W-W) |
| Транзакция-читатель мешает транзакции-писателю | ||
Сделаем важный вывод: при использовании разграничения доступа с помощью S- и X-блокировок транзакции-читатели могут мешать транзакциям-писателям. Транзакции-писатели всегда мешают другим транзакциям-писателям и транзакциям-читателям.
Как выполняются блокировки в транзакциях Cache ObjectScript? С помощью команды LOCK имеющей формат:
L[OCK] [+|-] список_имен_блокируемых_ресурсов
Пример: LOCK ^a(1) или, сокращенно, L ^а(1) блокирует узел ^а(1).
| Формат | Действие |
LOCK |
Отмена всех блокировок |
LOCK список_имен |
Снимает все блокировки, затем блокирует по |
| одному имени из списка | |
LOCK +имя |
Инкрементальная блокировка - добавление |
| блокировки к уже существующим блокировкам | |
LOCK -имя |
Удаляет блокировку |
Команда LOCK ^а(1), ^b(3) эквивалентна LOCK ^а(1) LOCK +^b(3). Использование времени ожидания (для инкрементальных блокировок рекомендуется всегда): LOCK ^а(1):5
Попытка блокировать прерывается, если прошло 5 секунд, а блокировка не завершилась. При этом устанавливается значение $TEST=0. Просмотреть текущие блокировки можно двумя способами:
Программой ^LOCKTAB, запускаемой из терминала. Пример:
USER>LOCK ^a(0) USER>ZN "%SYS" %SYS>DO ^LOCKTAB
С помощью портала управления системой - рисунок 6.6
(рис 6.6) Список блокировок в портале управления системой
Результат выполнения последней команды:
Node Name: WS111
LOCK table entries at 11:10PM 11/28/2012
1176464 bytes usable, 1178528 bytes available.
Entry Process X# S# Flg W# Item Locked
1) 1188 1 ^[ "^^c:\intersystems\trycache\mgr\"]%SYS("CSP","Daemon")
2) 3400 1 ^[ "^^c:\intersystems\trycache\mgr\"]TASKMGR
3) 2640 1 ^[ "^^c:\intersystems\trycache\mgr\user"]a
Command=>
Любая СУБД блокирует как минимум свой словарь, поэтому вместо одной установленной нами блокировки на ресурс ^а(0) мы видим еще и другие блокировки.
Справку по ^LOCKTAB можно получить командой HELP, сокращенно H. Выход из программы осуществляет команда Q[UIT].
Блокировки распространяются в дереве на всех потомков и предков.
Пример:
Создадим глобал S ^a(1)=1, ^a(1,1)=11, ^a(2)=2, ^a(2,1)=21
Команда L ^a(1) блокирует не только ^а(1), но и ^а,^а(1,1). Обратите внимание на то, что блокируются и локалы и глобалы. При этом LOCK не проверяет существование узлов. Поэтому блокировка создается и для несуществующих узлов.
Рекомендация: Для удобства тестирования желательно, чтобы количество инкрементальных блокировок узла совпадало с количеством инкрементальных деблокировок.
Пример: Если выполнена команда L +(^a,^b,^c), а затем L ^b, то останется заблокированным только ^b.
При использовании транзакций возникают побочные и очень неприятные явления. Это тупики. Возникают они, когда две или более транзакций пытаются блокировать монопольно одни и те же ресурсы. Не получая доступа к ресурсам, конфликтующие транзакции могут зависнуть на неопределенное время. В СУБД этого допускать нельзя. Нужно как-то разрешить конфликт. Например, через некоторое время после возникновения тупика конфликтующие транзакции откатывают, но затем их вновь запускают в случайно выбранные моменты времени, совсем как при разрешении конфликтов в сети Ethernet.
В транзакции могут исполняться следующие виды операций:
Ситуация тупика возникает при попытке двух транзакций блокировать одни и те же ресурсы:
(рис 6.7) Ситуация тупика
С момента времени T продолжение работы транзакций невозможно, если СУБД не применит какой-нибудь алгоритм разрешения конфликта.
Использование блокировок позволяет решать проблемы параллельного доступа. Пример использования блокировок для устранения чтения "грязных" данных — в таблице 6.13
| Транзакция 1 | Время | Транзакция 2 |
| S-блокировка Acc успешна | t1 | |
| Чтение Acc | t2 | |
| t3 | S-блокировка Acc успешна | |
| t4 | Чтение Acc | |
| Попытка записи Acc (X- | t5 | |
| блокировка отвергается) | ||
| Ожидание | t6 | Попытка записи Acc (X- |
| блокировка отвергается) | ||
| Ожидание | t7 | Ожидание |
X-блокировки отвергаются, так как уже установлены две S-блокировки. Обе транзакции ожидают завершения другой транзакции. Возникает тупик, зато феномен чтения "грязных" данных отсутствует. Если же S-блокировка транзакции 2 будет выполнена после успешной X-блокировки транзакции 1, то транзакция 2 будет ждать завершения транзакции 1.
Мы видели, что в СУБД с единственной версией данных транзакции-читатели могут мешать транзакциям-писателям (конфликты R-W).
В многоверсионных СУБД транзакциям-читателям предоставляются свои версии данных, получаемые откатом части схемы базы до последнего согласованного состояния. Такие транзакции не блокируют данных и потому не мешают транзакциям-писателям.
Например, многоверсионная СУБД Oracle создает и поддерживает номер изменения системы SCN (system change number). Каждая завершенная транзакция увеличивает его. Поэтому можно считать SCN уникальным идентификатором завершенной транзакции.
В заголовок блока данных записывается SCN завершившейся транзакции, которая изменяла блок последней.
При чтении результирующее множество запроса формируется следующим образом:
Возможны два варианта
Если не используется многоверсионность и не применяются блокировки, возможно получение несогласованных данных.
Требование атомарности транзакций означает, что откатившиеся транзакции или транзакции, не успевшие завершиться, не должны оставлять никаких следов своей работы в базе данных.
Требование долговечности означает, что данные, полученные зафиксированными транзакциями, должны сохраниться в базе, даже если в следующий за подтверждением момент произойдет сбой.
При современном уровне развития вычислительной техники единственный способ достичь приемлемой производительности это буферизация данных в оперативной памяти. Кажущийся естественным, способ немедленной записи данных завершенных транзакций на диск неприемлем, так как данные базы хранятся в файлах с адресным доступом, медленных по своей природе.
Выход из положения — использование дополнительных кольцевых последовательных буферов отката и файлов журналов, имеющих последовательный доступ. Такие файлы быстрее файлов данных. Избыточность, образующаяся при введении буферов отката и файлов журналов, позволяет хранить в базе и измененные данные, и их версии, существовавшие до внесения изменений. Рассмотрим работу буферных структур, изображенных на рисунке 6.8.
(рис 6.8) Буферные структуры СУБД
Кэш буферов базы — это раздел памяти (вспоминаем, что термин "память" означает "оперативная память" или "первичная память"), разделенный на блоки, по размеру совпадающие с блоками базы. Блок, который прочитали с диска и не успели изменить, называется чистым. И он же, но с внесенными изменениями, называется грязным. Заметим, что повышение быстродействия при использовании буферов базы происходит, только если вместе с необходимыми данными из диска извлекаются те данные, которые будут в ближайшее время использованы. Вспомните, что показатель HitRatio, определяемый как отношение числа обращений в буфер к общему числу обращений, очень близок к единице.
На самом деле чистые блоки пишутся одновременно и в кэш буферов базы, и в буфер журнала. Из-за высокого быстродействия первичной памяти это не слишком увеличивает время записи.
Кольцевой буфер отката состоит из таких же по размеру блоков. Из рисунка 6.8 видно, как взаимодействуют транзакции при квазипараллельной работе. Транзакция, например Т1, занимает чистыми блоками последовательно расположенные блоки буфера отката. Если в это время другая транзакция Т2 читает свой блок, то он будет размещен сразу за блоками, занятыми Т1, и далее блоки разных транзакций могут чередоваться. Если необходимо восстановить исходные данные, достаточно, используя инвертированные списки блоков, вытащить блоки откатываемой транзакции из кольцевого буфера. Движение по ссылкам занимает очень мало времени. Поэтому откат выполняется быстро.
Для того, чтобы сохранить изменения в базе, грязные блоки необходимо как можно скорее записать (как говорят, вытолкнуть) во вторичную память. Но при этом может резко упасть быстродействие. Поскольку последовательный файл журнала работает быстрее адресного файла данных, может оказаться, что запись в файл журнала выполнена, а запись в файл данных не успела завершиться, в этом случае файл данных будет восстановлен по файлу журнала.
Основные ситуации, в которых требуется восстановление данных:
ROLLBACK.Далее будет рассмотрена упрощенная система организации откатов и восстановлений после сбоев.
Необходимо удовлетворить два противоречивых требования. С одной стороны, из-за недостатка оперативной памяти и из-за возможности сбоев необходимо выталкивать "грязные" блоки как можно раньше, но это замедлит работу. Для ускорения работы необходимо записывать на диск как можно реже, но это увеличивает риск рассогласования базы.
Измененные объекты базы данных должны попадать в файл журнала раньше, чем грязные блоки кэша буферов базы попадут в файлы данных. Пиши сначала в журнальный файл! Write Ahead Log!
При этом может оказаться, что на диске имеется запись журнала, но еще нет записи блока базы. Если же записан блок данных, то журнальный блок тем более записан.
Периодически или при достижении кэшем буферов базы определенного состояния (например, количество страниц в списке грязных блоков превысило порог) возбуждается событие контрольной точки.
При этом выполняются следующие два действия:
При выполнении одной транзакции восстановление последнего согласованного состояния базы гарантируется, если в файл журнала вытолкнуты все записи об изменении базы данных этой транзакцией, включая запись о конце транзакции.
Завершенные по COMMIT транзакции нельзя откатить. Для выполнения отката незавершенной транзакции необходимо:
Пусть создана ситуация: принята последняя контрольная точка, и через некоторое время произошел мягкий сбой. Несколько возможных вариантов поведения системы:
Для создания и пополнения архивных файлов устанавливают режим архивирования (archivelog). Если погибла часть жесткого диска или весь диск, то восстановление возможно только по данным архивных файлов и журнала транзакций. Желательно регулярно копировать файлы журналов на отдельные носители.
Порядок действий по восстановлению базы:
Замечание. Комплекс работ по сохранению и восстановлению данных называется backuprecovery.
Хороший сервер это сложная система. В нем необходимо иметь источники питания и диски с горячей заменой. Источник бесперебойного питания рассчитывается так, чтобы он обеспечил работу во время запуска и выхода на режим дизель-генератора, обеспечивающего автономное питание практически на любое время. В серверном помещении должна быть создана эффективная система пожаротушения.
Должно быть организовано правильное архивирование данных. Журнал позволяет держать данные на сервере в течение небольшого времени, порядка часа. Необходимо наладить систему архивирования. Архив должен быть достаточно оперативным, т.е. если вы архивируете данные раз в месяц, то не исключено, что система погибнет перед очередным архивированием, и вы будете вынуждены вспоминать данные, которые вводились за последний месяц. Рекомендуется делать ежедневные архивы (администраторская "неделька"). Архивные носители желательно помещать в территориально удаленные хранилища.
В системах с правильной архивацией можно откатить базу на все время существования и восстановить ее практически при любых катастрофах. Теряются данные максимум за один последний день.
(рис 6.9) Свойства транзакции
(рис 6.10) Синтаксис транзакции
(рис 6.11) Ограничения целостности
(рис 6.12) Уровни изоляции пользователей
(рис 6.13) Согласованность
(рис 6.14) Блокировки Определим понятие согласованного состояния базы и на его основе введем понятие транзакции как набора действий, выполняющихся как единое целое и переводящих базу из одного согласованного состояния в другое.
Изучим свойства транзакций и языковые средства для их оформления.
Покажем как транзакции обеспечивают поддержку целостности данных, параллельную работу пользователей и восстановление данных при откатах транзакций и сбоях аппаратуры.
Дадим классификацию ограничений целостности.
Введем понятие ссылочного ограничения целостности и покажем как оно поддерживается с помощью триггера.
Опишем феномены — проблемы, возникающие при параллельной работе—и на их основе определим уровни изолированности пользователей.
Изучим проблемы блокирования ресурсов и возникновения тупиков. Бегло рассмотрим многоверсионные данные.
В заключительных разделах рассмотрим роль транзакций в организации откатов и восстановлении данных после сбоев.
Базы данных, на которых строятся информационные системы, должны обеспечить одновременный доступ к данным для многих, иногда сотен и тысяч, пользователей. Оказывается, при параллельной работе пользователей результаты вычислений могут зависеть от временных соотношений между действиями пользователей, от того, в какое время и в какой последовательности они выполняются. Как избежать этой зависимости? Как организовать чтение данных одним пользователем и одновременно их изменение другим? Как добиться, чтобы деньги, снятые с одного счета, и, из-за сбоя системы, не зачисленные на другой счет, не могли пропасть бесследно? Как восстановить данные при откатах и сбоях?
Все эти проблемы решаются за счет использования механизма транзакций, который должен работать в базах с любыми моделями данных.
Информационные системы, предназначенные для единственного пользователя, в настоящее время практического интереса не представляют. Но даже они нуждаются в транзакциях. Всегда существует наборы действий, такие что невыполнение некоторых из них приводит базу в противоречивое состояние. Кроме того, один пользователь может запустить несколько потоков вычислений, обращающихся, может быть, к одним данным. И тут ему потребуется механизм, обеспечивающий независимость работы этих потоков.
Транзакции обеспечивают:
В этой лекции будут рассмотрены транзакции и весь круг вопросов, связанных с перечисленными тремя аспектами их применения. Сложности для начинающих возникают из-за необходимости рассмотрения во времени нескольких процессов, может быть распределенных в некоторой сети.
Пусть при выполнении двух последовательно выполняющихся действий после успешного выполнения первого из них возник сбой (рисунок 6.1).
(рис 6.1) Что может произойти при сбое сервера
В приведенном примере это означает, что сумма, снятая с первого счета не попала на второй счет. Хуже того, сам факт исчезновения денег никак не зафиксирован. Если вас этот неприятный факт не расстроил, и вы считаете, что можно повторить все с начала, то тут сказывается одна нехорошая привычка, вырабатываемая ориентированностью наших студентов, фактически, на научные вычисления. Скажем, складывал я две матрицы, а они у меня не сложились. Ну, бог с ними — я их сложу еще раз. Но, если вы написали систему, которая отправила миллиард долларов, и он никуда не пришел, то, скорее всего, усовершенствовать ее будет уже другой человек, а вы будете искать новую работу. Вот, собственно, в этом разница в отношении к надежности вычислений в науке и бизнесе. Вообще говоря, бизнес-приложения должны работать всегда, независимо от того, исправен ли компьютер или нет, заболели ли вы или еще что-то случилось, ночь сейчас или день. Ведь есть организации, работающие круглые сутки. Хорошим тоном считается обеспечивать работоспособность в самом тяжелом режиме, который принято называть "24*7" (двадцать четыре на семь). Это означает 24 часа в сутки 7 дней в неделю. Это очень тяжелый режим, и без специальных мер он сам по себе не обеспечивается. Представьте, что в вашем домашнем компьютере нужно поменять или добавить жесткий диск. Как это сделать без останова системы?
(рис 6.2) Возможные ошибки
Для рассмотрения параллельной работы двух пользователей вспомним, что "сальдо" —это остаток на счете. Посчитать сальдо по группе счетов — это, попросту говоря, сложить их остатки. Обычный арифметический цикл, который известен всем школьникам. На рисунке 6.2 слева показана работа такого цикла. Сначала сальдо С по группе счетов равно 0, потом прибавляем к нему остаток первого счета, второго счета и т.д., счета тысячного. Как учили в школе? Написали цикл, проверили его на примере. Дальше работает всегда правильно. Но у нас то пользователь не один. Транзакция изображенная слева выполняет циклическое суммирование. Но после того, как счет 1 уже учтен, и до того, как учтен счет 1000, вторая транзакция, изображенная справа, снимает 200 руб. со счета 1, и зачисляет их (то есть прибавляет 200 руб.) на счет 1000. Теперь в подсчете сальдо по группе счетов появится ошибка в 200р. Почему? Потому что счет 1 мы обрабатывали в неизмененном состоянии, а счет 1000 в измененном. А что будет, если во времени вторая транзакция будет сдвигаться? Ну, очевидно, если она будет выполнена до первой (левой на рисунке) транзакции или после нее, то ничего страшного не случится, все будет посчитано правильно. А если транзакции начнут выполняться на одном промежутке времени то, с какого-то момента появится ошибка. Обратите внимание, довольно странная, неожиданная для начинающих ситуация — арифметический цикл, оказывается, может считать данные неправильно, если одновременно идет некоторый другой процесс, имеющий доступ к тем же данным. Транзактный механизм должен подобные казусы исключить.
В соответствии с общим принципом, на котором построен настоящий курс, мы все должны "проделать ручками", что в этом разделе я особенно вам советую, если, конечно, у вас уже нет опыта параллельных вычислений или работы с транзакциями. Параллельную работу двух пользователей моделируем включением двух терминалов Cache (рисунок 6.3). При этом транзакций мы пока не используем.
(рис 6.3) Ошибка при подсчете сальдо при параллельной работе без транзакций
Работа с тысячью счетов в примере может затянуться, поэтому счетов у нас всего три штучки (Сч1, Сч2, Сч3). На них 500 руб., 700 руб. и 800 руб. соответственно. Сальдо С по группе этих трех счетов в начале вычисления равно нулю. Переменная t — это разметочная переменная, отмечающая моменты времени 0, 1, 2, 3, 4. Она определяет порядок действий. Сначала вы должны выполнить ту строчку в левом терминале, где есть присваивание t = 0. После него выполняете первую строчку второго терминала там, где t присваивается значение 1, и т.д. Если вам трудно следить за текстом, повторите все сами. Как и ожидалось, сальдо вычисляется с ошибкой.
Определение. База находится в согласованном состоянии, если выполняются все записанные в ней ограничения целостности. Во время выполнения транзакции база может рассогласовываться.
Вернемся к рассмотренной ранее операции — переводу денег с одного счета на другой. Эта операция включает две атомарные (элементарные, неделимые) операции:
Если СУБД будет выполнять (или не выполнять) эти две операции только вместе, то база будет всегда находиться в согласованном состоянии и не будут возникать ситуации, при которых суммы, снятые с одного счета, ни на какой другой счет не поступают.
Определение. Объединение нескольких операций в неделимое целое называют транзакцией.
Возникает вопрос: Что делать, если часть данных уже изменена, а остальные измениться не могут? Ответ: Откатить выполненные изменения, то есть вернуться к ранее существовавшим значениям. Но как выполнить откат? Это мы выясним позже. Здесь отметим, что создание действий, аннулирующих изменения в базе, выполненные транзакцией, не приемлемо уже потому, что генерация таких инструкций и их запуск на исполнение потребовали бы слишком много времени.
При отказах информационной системы желательно сохранить сведения о попытке выполнения транзакции. Если при этом транзакция была выполнена частично, то уже выполненную часть операций необходимо откатить. После восстановления системы транзакция должна быть выполнена повторно.
Транзакция — это последовательность действий, выполняющаяся как единое целое и переводящая базу данных из одного согласованного состояния в другое согласованное состояние. Вы должны помнить, что ограничения целостности определенные в бизнесе, могут быть не прописаны явно. Так, в примере, приведенном на рисунке 6.1, ограничение целостности можно сформулировать так: деньги, снятые со счета, должны быть зачислены на другой счет. Может быть задействована некоторая цепочка счетов. Например, если вы отправляете деньги через свой банк клиенту другого банка, то деньги сначала поступают на ваш клиентский счет, с него — на корреспондентский счет вашего банка в другом банке, а уже оттуда на клиентский счет получателя. Всегда необходимо додумывать ситуацию до конца, тогда она будет прозрачна, понятна, и вы с ней справитесь. В транзакциях используют следующие инструкции:
Транзакции обладают набором свойств (рисунок 6.4), характеризуемым аббревиатурой АСИД (ACID):
(рис 6.4) Свойства транзакции
Замечание. Соответствующая английская аббревиатура ACID образована первыми буквами терминов Atomicity, Consistency, Isolation, Durability.
Свойства АСИД транзакций, за исключением атомарности, не всегда выполняются в полном объеме.
Будем рассматривать так называемые плоские транзакции, обладающие одним управляющим слоем. Именно этот тип широко распространен в современных СУБД.
Существует два способа запуска транзакции:
Транзакция завершается, успешно или не успешно, специальными командами или по одному из событий:
В языке COS начало транзакции — команда TSTART, успешное завершение — TCOMMIT, неуспешное — TROLLBACK.
В таблице 6.1 приведены команды управления данными для Cache.
| Команда SQL | Команда COS | Описание |
| %BEGTRANS | TSTART | Начало транзакции |
| COMMIT [WORK] | TCOMMIT | Успешное завершение транзакции |
| ROLLBACK [WORK] | TROLLBACK | Неуспешное завершение транзакции |
Замечание. В Oracle любые действия вкладываются в транзакции, в Cache транзакции используются по желанию пользователя. Пользователь Cache, работающий вне транзакции как бы "не замечает",
чужих транзакций.
Создадим и откомпилируем программу ^tr1 содержащую транзакцию:
SET ^tt(0)=0, ^tt(1)=1 ;значения до транзакции TSTART ;начало транзакции SET ^tt(0)= "Значение 1 из транзакции" READ x ;приостанавливает исполнение SET ^tt(1)= "Значение 2 из транзакции" TCOMMIT /* конец транзакции - успешное завершение */ WRITE "^tt(0)= ",^tt(0),!, "^tt(1)= ", ^tt(1) QUIT
Вы помните, что в Cache данные бестиповые, тип данных определяется по контексту. Например запись -x приведет к интерпретации x как числа. В программе ^tr1 назначаем глобалам ^tt(0) и ^tt(1) цифровые значения. А в транзакции присваиваем им текстовые значения. READ x используется для остановки исполнения программы за счет ожидания ввода данных человеком. В конце печатается текст ^tt(0) =.. ^tt(1) =.. и сами значения этих глобалов. Если транзакция откатывалась, то глобалам ^tt(0) и ^tt(1) возвращаются цифровые значения.
Запустим два терминала.
Просмотрим два варианта исполнения транзакции:
x (левый терминал) — см. рисунок ниже. Сначала в левом терминале исполняется программа ^tr1 , затем в правом читается значение глобала. Транзакция завершилась успешно, поэтому возвращены текстовые значения.
-см. рисунок ниже. Правый терминал читает ^tt(0) первый раз до выключения левого терминала, а второй раз — после его выключения. Вывод: выключение терминала откатывает транзакцию.
Переходим к первой задаче, которую решают транзакции — соблюдение ограничений целостности. Что нужно для проверки любого ограничения? Прежде всего, база должна быть активной, то есть иметь способность совершать что-то, сверх того, о чем ее попросили. Пусть задано ограничение первичный ключ. Когда мы приказываем ввести строку, СУБД сама обнаруживает, что такое ограничение имеется и запускает процедуру, проверяющую, не повторяются ли значения в ключевых столбцах. И, наверное, она не позволит ввести строку с повторяющимся значением ключа. Хорошо бы еще получить какое-то сообщение о возможном нарушении ограничения целостности.
Чаще всего источник активности — специальные процедуры, называемые триггерами. Но это не единственный вариант. Триггеры напоминают резидентные программы. Они начинают работать только при запуске некоторого процесса, называемого триггерным событием. В число таких событий всегда входят вставка, удаление и обновление строк (по-английски insert, update и delete). В современных базах данных триггеры могут реагировать и на другие события. Поскольку ограничения целостности определены для хранимых объектов базы, триггеры прикрепляются к этим объектам.
Рассмотрим ограничения целостности и реакции СУБД на их нарушения. Приведем примеры ограничений целостности, в том числе ссылочных. Изучим классификацию ограничений целостности по способам реализации, по времени проверки и по области действия. В частности, уточним, что такое триггеры.
Определение. База данных находится в согласованном (целостном) состоянии, если выполнены все заданные в ней ограничения целостности.
Для распределенных баз данных важна еще согласованность копий фрагментов данных в узлах. Меру различия между копиями данных в узлах сети (репликами) называют связностью данных. Распределенная база не может обеспечить одновременно высокую связность и высокую скорость работы. Чем выше связность, тем больше трафик по сети и тем выше загрузка серверов, реплицирующих данные.
Система управления базами данных обязана реагировать на любые попытки нарушения целостности.
Два основных типа реакции СУБД:
Классификация ограничений целостности проводится по следующим основаниям:
Различают
Декларативная поддержка ограничений целостности задается средствами языка определения данных (ЯОД, он же DDL — Data Definition Language).
Приведем пример для языка SQL, который мы еще не изучали. Но это нам не сильно помешает. Создаем таблицу, используя следующую инструкцию:
CREATE TABLE person (persid INTEGER PRIMARY KEY, persname CHAR(30) NOT NULL);
Если прочитать ее на русском языке, все станет понятно. Итак: "СОЗДАТЬ ТАБЛИЦУ с названием person, содержащую два столбца: persid типа ЦЕЛОЕ с ограничением ПЕРВИЧНЫЙ КЛЮЧ и persname типа СИМВОЛЬНОЕ с длиной 30 символов и ограничением НЕЛЬЗЯ НЕ ЗАПОЛНЯТЬ".
Процедурные ограничения целостности реализуются использованием триггеров и хранимых процедур.
Пример ограничения целостности, которое обычно реализуется процедурно: "Начальник отдела может изменять заработную плату только своим подчиненным и только в сторону уменьшения". Для того, чтобы реализовать это ограничение декларативно необходимо задать формулу, позволяющую найти строки для всех подчиненных и как-то указать, что будущие значения зарплат не больше уже существующих. Этого сделать нельзя, так как в базе нет этих будущих значений.
Деление на процедурные и декларативные ограничения целостности это не дихотомия, так как оба вида ограничений в действительности поддерживаются специальными процедурами. Просто в каждой СУБД определен список имен ограничений, которые можно записать декларативно. Остальные ограничения, для которых нет таких имен, реализуются явным заданием в процедурной части приложения.
Триггеры — это процедуры специального вида, прикрепленные к объекту базы, например, таблице, и срабатывающие при наступлении событий из некоторого набора событий. Наиболее известны триггеры, реализуемые в языке SQL.
В набор событий обычно входят вставка записи (инструкция INSERT в SQL), удаление записи (инструкция DELETE) и обновление записи (инструкция UPDATE).
Триггер может выполняться до отработки события (триггер BEFORE) и после его отработки (триггер AFTER).
Выделяют также триггеры уровня строки (записи), срабатывающие один раз на каждую выбранную строку, и триггеры уровня выражения, срабатывающие один раз при обработке набора записей (например, при входе в таблицу или выходе из нее). Итого 3 * 2 * 2 = 12 типов триггеров на один набор записей (таблицу).
Для проверки ограничения на возраст принимаемого на работу создают триггер на команду INSERT, срабатывающий до ее выполнения (нельзя допускать запись о неправильном приеме — значит выбираемый тип — BEFORE). В теле триггера вычисляют возраст, проверяют условие $$21\le возраст \le 45$$. Если условие не выполнено, запись о приеме аннулируется (рисунок 6.5).
(рис 6.5)
Рассмотрим пример ссылочного ограничения целостности.
Пусть задана схема, состоящая из двух таблиц dept (отделы) и person (сотрудники) (таблицы 6.2 и 6.3).
Столбцы таблицы dept (deptid, deptname, deptquan): deptid — идентификатор подразделения, deptname —имя подразделения, deptquan — количество сотрудников в подразделении.
Столбцы таблицы person(persid, persname,deptid): persid — идентификатор сотрудника, persname —имя сотрудника, deptid —идентификатор подразделения, в котором работает сотрудник.
| DEPTID | DEPTNAME | DEPTQUAN |
| 1 | Цех разлива | 3 |
| 2 | Транспортный цех | 2 |
| PERSID | PERSNAME | DEPTID |
| 1 | Иванов | 1 |
| 2 | Петров | 2 |
| 3 | Сидоров | 1 |
| 4 | Злобис | 2 |
| 5 | Вредис | 1 |
Имеющееся здесь ограничение ссылочной целостности определяется условием: поле deptquan должно содержать количество сотрудников, записанных в таблице person для каждого отдела.
Если уволим Иванова, недостаточно просто его вычеркнуть, потому что при этом изменится численность первого отдела. Ее нужно пересчитать, вычтя единицу из значения в столбце deptquan. А если переведем Иванова в транспортный цех, то необходимо не только изменить строку с Ивановым в таблице dept, но еще и прибавить к численности транспортного цеха единицу, вычтя единицу из численности цеха розлива. Иначе таблица "Отделы" окажется неправильно заполненной.
При наличии ссылочного ограничения, изменение в одной таблице обязательно влечет за собой изменение в другой таблице, причем изменение по вставке одно, по удалению другое, по изменению третье.
Например, прием нового сотрудника не может быть выполнен одной операцией. Необходимо вставить запись в таблицу person и одновременно увеличить значение поля deptquan на 1. Выполняем два шага, которые, конечно же, необходимо включить в транзакцию:
Вставить запись о сотруднике в таблицу person
INSERT INTO person VALUES (6, 'Петросян', 1).
(ВСТАВИТЬ в person строку (6, "Петросян", 1).)
Увеличить значение поля deptquan
UPDATE dept SET depquan=deptquan+l WHERE deptid=l.
(ОБНОВИТЬ dept, УСТАНОВИВ ЗНАЧЕНИЕ deptquan = deptquan+1 ДЛЯ СТРОКИ В КОТОРОЙ deptid= 1). Для увольнения и перемещения сотрудников необходимы свои транзакции.
Замечание. Пока мы не изучили инструкции SELECT, INSERT и UPDATE языка SQL, воспринимайте их просто как фразы, написанные на фрагменте естественного (как бы английского) языка, который понимает СУБД.
Пример 1. Возраст сотрудника не может быть меньше 21 и больше 45 лет (Декларативное ограничение типа check).
Пример 2. Атрибут "Табельный номер" уникален. (Декларативное ограничение—уникальный или первичный ключ).
Пример 3. Сотрудник обязан числиться в одном из отделов. (Декларативное ссылочное ограничение целостности).
Пример 4. Сумма накладной равняется сумме произведений цен товаров на количество товаров для всех товаров, входящих в накладную. (Определение вычислимого столбца.).
Замечание. Ограничение целостности может определять не только значения атрибутов, но и особенности удаления или обновления данных. Например, в схеме "Отдел" — "Сотрудник" удаление отдела может вызвать удаление его сотрудников или их перевод в другие отделы.
Задание ограничения целостности приходит в базу из бизнес-модели. Для проверки правильности этого перехода полезно определить, какие требования предлагает бизнесу это ограничение, то есть выполнить обратный переход. Например, задана уникальность табельного номера. Что это означает для бизнеса? Какой номер присваивается работнику при повторном приеме? Существуют ли сотрудники, не имеющие номера?
По времени проверки выделяют два вида ограничений:
COMMIT.Пример немедленно проверямого ограничения — первичный ключ. Пример ограничения с отложенной проверкой — ссылочные ограничения (таблицы 6.2 и 6.3).
Итоговая классификация ограничений целостности приведена в разделе 6.6.
Ограничения на допустимые связи могут определяться самими связываемыми сущностями. Но может понадобится учесть еще и другие сущности, как-то связанные с проверяемыми сущностями и не связанные напрямую проверяемой зависимостью.
В типичном случае имеется несколько взаимно исключающих связей. Например, сущность A может быть связана бинарной связью с B или C, но если существует связь A с B, то не может существовать связь A с C, и наоборот.
Возможно, наличие или отсутствие связи определяется состоянием экземпляров сущностей, которые могут входить в связь. Под состоянием здесь понимаются значения атрибутов сущностей, имеющих смысл "состояние".
Заметим, что реализация рассматриваемого класса ограничений достаточно сложна.
Одна из возможных связей — наследование. Реализация ограничений на наследование будет рассмотрена позже при изучении объектных моделей.
Любая система в бизнесе представляет множество действующих лиц — акторов — работающих одновременно и организованно. Актором может быть человек, программа или машина. Информационная система должна как-то отражать эту многоакторность. Конечно, в ней самой могут появиться параллельные процессы, не имеющие прямых аналогов в бизнесе.
В этом разделе будут рассмотрены проблемы, возникающие при параллельной работе транзакций. В соответствии с традицией будем называть эти проблемы феноменами. На основе феноменов зададим уровни изолированности транзакций. В определениях изолированности будет использован стандарт версии SQL-92 языка SQL, но знание SQL и, тем более, стандарта для освоения материала не требуется.
В заключительной части раздела рассмотрим блокировки ресурсов и эффекты, возникающие при блокировании.
При отсутствии блокировок или других средств, ограничивающих доступ к ресурсам, используемым транзакцией, некоторые изменения могут быть утеряны.
Пример (таблицы 6.4 и (6.5): На счет Acc с начальным сальдо 100, зачисляют первой транзакцией 20 руб., а второй 100 руб. Если первая транзакция записывает измененные данные в базу и завершается после того, как вторая транзакция их прочла, но до того, как она их записала, то изменения, внесенные первой транзакцией, теряются.
| Транзакция 1 | Время | Транзакция 2 |
| Начало Тр1 | ||
| Чтение X=Acc | to | |
| Начало Тр2 | ||
| ti | Чтение X=Acc | |
| t2 | Зачисление X=X+100 | |
| Зачисление X=X+20 | t3 | |
| Запись в базу Acc=X | t4 | |
| COMMIT | ||
| Конец Тр1 | ||
| t5 | Запись в базу Acc=X COMMIT | |
| Конец Тр2 |
USER>TStart USER>Set Х=^Асс, t=Q USER>Set Х=Х+20, t = 3 USER>Set ^Асс=Х, t = 4 USER>TCommit USER> USER>TStart |
USER>Set Х=^Асс, t=1 USER>Set X=X+100, t=2 USER>Set ^Acc=X, t = 5 U5ER>TComoit USER>Write ^Acc 2 00 USER>| |
Начальное значение ^Acc=100. TSTART — начало транзакции, TCOMMIT — ее успешное завершение. Переменная t, как и раньше, использована вместо комментария для задания отметки очередности исполнения команд, соответствующей моментам времени $$t = 0, t = 1, \dots$$.
Феномен возникает, когда читаются данные незавершенной транзакции, которые впоследствии откатываются. Начальное значение Acc в примере равно 100 (таблицы 6.6 и 6.7).
| Транзакция 1 | Время | Транзакция 2 |
| Начало Тр1 | ||
| Изменение Acc=Acc+20 | t1 | |
| Начало Тр2 | ||
| t2 | Чтение и изменение | |
| X=Acc+50 | ||
| ROLLBACK —возврат к Acc=100 | tз | |
| Конец Тр1 | ||
| t4 | Acc=X | |
| Конец Тр2. Значение Acc не 150, а 170 |
USER>TStart USER>Set ^Acc=^Acc+20, t=1 USER>TRollback USER>Sett=3 Rollback Write ^Acc 100 USER> |
USER>TStart USER>S X=^Acc+50, t=2 USER>Set ^Acc=X ,t=4 USER>TCommit USER>Set t=5 USER>Write ^Acc 170 |
Разберите сами приведенный ниже пример (таблицы 6.8 и 6.9).
| Транзакция 1 | Время | Транзакция 2 |
| Начало Тр2 | ||
| t1 | Чтение Acc (Acc=100) | |
| Начало Тр1 | ||
| Изменение Acc=300 | t2 | Проверка Acc<200 — Да |
| t3 | Чтение Acc (Acc=300) Проверка Acc<200 — Нет |
USER>TStart U5ER>Set t=2, ^Acc=300 USER> |
USER>TStart USER<Set t=l USER>If ^Acc<200 Write "Да"Да USER>Set t=3U SER>If ^Acc<200 Write "Да" USER>| |
Ниже мы изучим блокировки, то есть защиту от попыток совместного использования записей. За счет блокировок на уровне строк перечисленные выше три феномена могут быть исключены. Но останется еще одна проблема.
Пусть транзакция Тр1 выбирает группу строк из таблицы Т, удовлетворяющих условию У1. Затем транзакция Тр2 вставляет в Т еще одну строку, удовлетворяющую условию У1. При повторной выборке строк транзакцией Тр1, к полученному в предыдущей выборке набору добавляется еще одна строка, называемая фантомом, привидением.
Стандарт SQL-92, основываясь на перечисленных феноменах, определяет четыре уровня изолированности:
Данные по уровням изолированности сведены в таблицу 6.10. Знак "+" в ней означает наличие феномена, а знак "-" — его отсутствие.
| Уровень | Потерянные | "Грязное" | Неповторяющееся | Фантомы |
| изоляции | изменения | чтение | чтение | |
| Serializable | + | + | + | + |
| Repeatable read | + | + | + | - |
| Read commited | + | + | - | - |
| Read uncommited | + | - | - | - |
Так зачем нужны "плохие" виды транзакций, и особенно, Read uncommited?
Ответ простой. С одной стороны, чем выше уровень изоляции, тем меньше шансов на параллельную работу транзакций. С другой стороны, полная изоляция не всегда полезна.
Пример: Библиотекари регулярно работают с каталогом, открывая транзакции для сверки и изменения каталога. Эти работы могут длиться часами. Что Вы предпочитаете, ждать появления исправленного каталога, или просматривать его в любой момент времени, может быть, иногда получая неправильные данные?
Замечание. Правильнее было бы дать библиотекарям работать с копией каталога, а когда они закончат работу быстро обновить каталог, запретив остальным пользователям доступ на несколько минут или секунд.
Как обеспечить правильную работу транзакций, обращающихся к одним и тем же ресурсам?
Два основных способа:
По степени разделяемости блокируемых ресурсов различают:
Блокировки различают еще по размерам блокируемого ресурса: поле записи, запись, отношение, страница (блок базы), группа отношений, вся база. Забегая вперед, отметим, что в современных СУБД используется два способа хранения таблиц — строчный и столбцовый. Чаще данные хранятся строками. В этом случае минимальный объем блокировки — строка. В столбцовых базах —поле. Выполняется свойство иерархичности. Если объект верхнего уровня обладает блокировкой, то такой же блокировкой обладает его подобъект нижнего уровня.
Взаимодействие блокировок:
Рассмотрим доступ по чтению и записи. Возможный протокол доступа к данным по чтению и записи с блокировками определяется следующими правилами:
Если транзакция B начинается позже транзакции A, то успешность блокирования объекта базы транзакцией B определяется следующей матрицей совместимости блокировок (таблица 6.11):
| Транзакция A наложила блокировку | Транзакция B пытается наложить блокировку | |
| S-блокировку | X-блокировку | |
| S-блокировку | + | - (конфликт R-W) |
| X-блокировку | - (конфликт W-R) | - (конфликт W-W) |
| Транзакция-читатель мешает транзакции-писателю | ||
Сделаем важный вывод: при использовании разграничения доступа с помощью S- и X-блокировок транзакции-читатели могут мешать транзакциям-писателям. Транзакции-писатели всегда мешают другим транзакциям-писателям и транзакциям-читателям.
Как выполняются блокировки в транзакциях Cache ObjectScript? С помощью команды LOCK имеющей формат:
L[OCK] [+|-] список_имен_блокируемых_ресурсов
Пример: LOCK ^a(1) или, сокращенно, L ^а(1) блокирует узел ^а(1).
| Формат | Действие |
LOCK |
Отмена всех блокировок |
LOCK список_имен |
Снимает все блокировки, затем блокирует по |
| одному имени из списка | |
LOCK +имя |
Инкрементальная блокировка - добавление |
| блокировки к уже существующим блокировкам | |
LOCK -имя |
Удаляет блокировку |
Команда LOCK ^а(1), ^b(3) эквивалентна LOCK ^а(1) LOCK +^b(3). Использование времени ожидания (для инкрементальных блокировок рекомендуется всегда): LOCK ^а(1):5
Попытка блокировать прерывается, если прошло 5 секунд, а блокировка не завершилась. При этом устанавливается значение $TEST=0. Просмотреть текущие блокировки можно двумя способами:
Программой ^LOCKTAB, запускаемой из терминала. Пример:
USER>LOCK ^a(0) USER>ZN "%SYS" %SYS>DO ^LOCKTAB
С помощью портала управления системой - рисунок 6.6
(рис 6.6) Список блокировок в портале управления системой
Результат выполнения последней команды:
Node Name: WS111
LOCK table entries at 11:10PM 11/28/2012
1176464 bytes usable, 1178528 bytes available.
Entry Process X# S# Flg W# Item Locked
1) 1188 1 ^[ "^^c:\intersystems\trycache\mgr\"]%SYS("CSP","Daemon")
2) 3400 1 ^[ "^^c:\intersystems\trycache\mgr\"]TASKMGR
3) 2640 1 ^[ "^^c:\intersystems\trycache\mgr\user"]a
Command=>
Любая СУБД блокирует как минимум свой словарь, поэтому вместо одной установленной нами блокировки на ресурс ^а(0) мы видим еще и другие блокировки.
Справку по ^LOCKTAB можно получить командой HELP, сокращенно H. Выход из программы осуществляет команда Q[UIT].
Блокировки распространяются в дереве на всех потомков и предков.
Пример:
Создадим глобал S ^a(1)=1, ^a(1,1)=11, ^a(2)=2, ^a(2,1)=21
Команда L ^a(1) блокирует не только ^а(1), но и ^а,^а(1,1). Обратите внимание на то, что блокируются и локалы и глобалы. При этом LOCK не проверяет существование узлов. Поэтому блокировка создается и для несуществующих узлов.
Рекомендация: Для удобства тестирования желательно, чтобы количество инкрементальных блокировок узла совпадало с количеством инкрементальных деблокировок.
Пример: Если выполнена команда L +(^a,^b,^c), а затем L ^b, то останется заблокированным только ^b.
При использовании транзакций возникают побочные и очень неприятные явления. Это тупики. Возникают они, когда две или более транзакций пытаются блокировать монопольно одни и те же ресурсы. Не получая доступа к ресурсам, конфликтующие транзакции могут зависнуть на неопределенное время. В СУБД этого допускать нельзя. Нужно как-то разрешить конфликт. Например, через некоторое время после возникновения тупика конфликтующие транзакции откатывают, но затем их вновь запускают в случайно выбранные моменты времени, совсем как при разрешении конфликтов в сети Ethernet.
В транзакции могут исполняться следующие виды операций:
Ситуация тупика возникает при попытке двух транзакций блокировать одни и те же ресурсы:
(рис 6.7) Ситуация тупика
С момента времени T продолжение работы транзакций невозможно, если СУБД не применит какой-нибудь алгоритм разрешения конфликта.
Использование блокировок позволяет решать проблемы параллельного доступа. Пример использования блокировок для устранения чтения "грязных" данных — в таблице 6.13
| Транзакция 1 | Время | Транзакция 2 |
| S-блокировка Acc успешна | t1 | |
| Чтение Acc | t2 | |
| t3 | S-блокировка Acc успешна | |
| t4 | Чтение Acc | |
| Попытка записи Acc (X- | t5 | |
| блокировка отвергается) | ||
| Ожидание | t6 | Попытка записи Acc (X- |
| блокировка отвергается) | ||
| Ожидание | t7 | Ожидание |
X-блокировки отвергаются, так как уже установлены две S-блокировки. Обе транзакции ожидают завершения другой транзакции. Возникает тупик, зато феномен чтения "грязных" данных отсутствует. Если же S-блокировка транзакции 2 будет выполнена после успешной X-блокировки транзакции 1, то транзакция 2 будет ждать завершения транзакции 1.
Мы видели, что в СУБД с единственной версией данных транзакции-читатели могут мешать транзакциям-писателям (конфликты R-W).
В многоверсионных СУБД транзакциям-читателям предоставляются свои версии данных, получаемые откатом части схемы базы до последнего согласованного состояния. Такие транзакции не блокируют данных и потому не мешают транзакциям-писателям.
Например, многоверсионная СУБД Oracle создает и поддерживает номер изменения системы SCN (system change number). Каждая завершенная транзакция увеличивает его. Поэтому можно считать SCN уникальным идентификатором завершенной транзакции.
В заголовок блока данных записывается SCN завершившейся транзакции, которая изменяла блок последней.
При чтении результирующее множество запроса формируется следующим образом:
Возможны два варианта
Если не используется многоверсионность и не применяются блокировки, возможно получение несогласованных данных.
Требование атомарности транзакций означает, что откатившиеся транзакции или транзакции, не успевшие завершиться, не должны оставлять никаких следов своей работы в базе данных.
Требование долговечности означает, что данные, полученные зафиксированными транзакциями, должны сохраниться в базе, даже если в следующий за подтверждением момент произойдет сбой.
При современном уровне развития вычислительной техники единственный способ достичь приемлемой производительности это буферизация данных в оперативной памяти. Кажущийся естественным, способ немедленной записи данных завершенных транзакций на диск неприемлем, так как данные базы хранятся в файлах с адресным доступом, медленных по своей природе.
Выход из положения — использование дополнительных кольцевых последовательных буферов отката и файлов журналов, имеющих последовательный доступ. Такие файлы быстрее файлов данных. Избыточность, образующаяся при введении буферов отката и файлов журналов, позволяет хранить в базе и измененные данные, и их версии, существовавшие до внесения изменений. Рассмотрим работу буферных структур, изображенных на рисунке 6.8.
(рис 6.8) Буферные структуры СУБД
Кэш буферов базы — это раздел памяти (вспоминаем, что термин "память" означает "оперативная память" или "первичная память"), разделенный на блоки, по размеру совпадающие с блоками базы. Блок, который прочитали с диска и не успели изменить, называется чистым. И он же, но с внесенными изменениями, называется грязным. Заметим, что повышение быстродействия при использовании буферов базы происходит, только если вместе с необходимыми данными из диска извлекаются те данные, которые будут в ближайшее время использованы. Вспомните, что показатель HitRatio, определяемый как отношение числа обращений в буфер к общему числу обращений, очень близок к единице.
На самом деле чистые блоки пишутся одновременно и в кэш буферов базы, и в буфер журнала. Из-за высокого быстродействия первичной памяти это не слишком увеличивает время записи.
Кольцевой буфер отката состоит из таких же по размеру блоков. Из рисунка 6.8 видно, как взаимодействуют транзакции при квазипараллельной работе. Транзакция, например Т1, занимает чистыми блоками последовательно расположенные блоки буфера отката. Если в это время другая транзакция Т2 читает свой блок, то он будет размещен сразу за блоками, занятыми Т1, и далее блоки разных транзакций могут чередоваться. Если необходимо восстановить исходные данные, достаточно, используя инвертированные списки блоков, вытащить блоки откатываемой транзакции из кольцевого буфера. Движение по ссылкам занимает очень мало времени. Поэтому откат выполняется быстро.
Для того, чтобы сохранить изменения в базе, грязные блоки необходимо как можно скорее записать (как говорят, вытолкнуть) во вторичную память. Но при этом может резко упасть быстродействие. Поскольку последовательный файл журнала работает быстрее адресного файла данных, может оказаться, что запись в файл журнала выполнена, а запись в файл данных не успела завершиться, в этом случае файл данных будет восстановлен по файлу журнала.
Основные ситуации, в которых требуется восстановление данных:
ROLLBACK.Далее будет рассмотрена упрощенная система организации откатов и восстановлений после сбоев.
Необходимо удовлетворить два противоречивых требования. С одной стороны, из-за недостатка оперативной памяти и из-за возможности сбоев необходимо выталкивать "грязные" блоки как можно раньше, но это замедлит работу. Для ускорения работы необходимо записывать на диск как можно реже, но это увеличивает риск рассогласования базы.
Измененные объекты базы данных должны попадать в файл журнала раньше, чем грязные блоки кэша буферов базы попадут в файлы данных. Пиши сначала в журнальный файл! Write Ahead Log!
При этом может оказаться, что на диске имеется запись журнала, но еще нет записи блока базы. Если же записан блок данных, то журнальный блок тем более записан.
Периодически или при достижении кэшем буферов базы определенного состояния (например, количество страниц в списке грязных блоков превысило порог) возбуждается событие контрольной точки.
При этом выполняются следующие два действия:
При выполнении одной транзакции восстановление последнего согласованного состояния базы гарантируется, если в файл журнала вытолкнуты все записи об изменении базы данных этой транзакцией, включая запись о конце транзакции.
Завершенные по COMMIT транзакции нельзя откатить. Для выполнения отката незавершенной транзакции необходимо:
Пусть создана ситуация: принята последняя контрольная точка, и через некоторое время произошел мягкий сбой. Несколько возможных вариантов поведения системы:
Для создания и пополнения архивных файлов устанавливают режим архивирования (archivelog). Если погибла часть жесткого диска или весь диск, то восстановление возможно только по данным архивных файлов и журнала транзакций. Желательно регулярно копировать файлы журналов на отдельные носители.
Порядок действий по восстановлению базы:
Замечание. Комплекс работ по сохранению и восстановлению данных называется backuprecovery.
Хороший сервер это сложная система. В нем необходимо иметь источники питания и диски с горячей заменой. Источник бесперебойного питания рассчитывается так, чтобы он обеспечил работу во время запуска и выхода на режим дизель-генератора, обеспечивающего автономное питание практически на любое время. В серверном помещении должна быть создана эффективная система пожаротушения.
Должно быть организовано правильное архивирование данных. Журнал позволяет держать данные на сервере в течение небольшого времени, порядка часа. Необходимо наладить систему архивирования. Архив должен быть достаточно оперативным, т.е. если вы архивируете данные раз в месяц, то не исключено, что система погибнет перед очередным архивированием, и вы будете вынуждены вспоминать данные, которые вводились за последний месяц. Рекомендуется делать ежедневные архивы (администраторская "неделька"). Архивные носители желательно помещать в территориально удаленные хранилища.
В системах с правильной архивацией можно откатить базу на все время существования и восстановить ее практически при любых катастрофах. Теряются данные максимум за один последний день.
(рис 6.9) Свойства транзакции
(рис 6.10) Синтаксис транзакции
(рис 6.11) Ограничения целостности
(рис 6.12) Уровни изоляции пользователей
(рис 6.13) Согласованность
(рис 6.14) Блокировки Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.