Операторы управления транзакциями. Руководство по Spring

Транзакцией называется последовательность операций над базой данных, рассматриваемых СУБД как единое целое. Если все операции успешно выполнены, то транзакция также считается успешно выполненной и СУБД фиксирует (COMMIT) все изменения данных, произведенные этой транзакцией (то есть заносит изменения во внешнюю память). Если же хотя бы одна операция транзакции заканчивается неудачей, то транзакция считается невыполненной и производится откат (ROLLBACK) - отмена всех изменений данных, произведенных в ходе выполнения транзакции, и возврат базы данных к состоянию до начала выполнения транзакции. Управление транзакциями необходимо для поддержания логической целостности базы данных. Поддержка механизма транзакций является обязательным условием даже однопользовательских, а тем более для многопользовательских СУБД. То свойство, что каждая транзакция начинается при целостном состоянии базы данных и оставляет это состояние целостным после своего завершения, делает очень удобным использование понятия транзакции как единицы активности пользователя по отношению к базе данных. При соответствующем управлении параллельно выполняющимися транзакциями со стороны СУБД каждый из пользователей может, в принципе, ощущать себя единственным пользователем СУБД.

С управлением транзакциями в многопользовательской СУБД связаны важные понятия сериализации транзакций и сериального плана выполнения смеси транзакций. Под сериализациями параллельно выполняющихся транзакций понимается такое планирование их работы, при котором суммарный результат смеси транзакций эквивалентен результату их некоторого последовательного выполнения. Сериальный план выполнения смеси транзакций - это такой план, который приводит к сериализации транзакций. Попятно, что если удается добиться действительно сериального выполнения смеси транзакций, то для каждого пользователя, по инициативе которого образована транзакция, присутствие других транзакций будет незаметно (если не считать некоторого замедления работы по сравнению с однопользовательским режимом).

Существует несколько базовых алгоритмов сериализации транзакций. В централизованных СУБД наиболее распространены алгоритмы, основанные на синхронизационных захватах объектов базы данных. При использовании любого алгоритма сериализации возможны конфликты между несколькими транзакциями по доступу к объектам базы данных, В этом случае для поддержания сериализации необходимо выполнить откат одной или нескольких транзакций. Это один из случаев, когда пользователь многопользовательской СУБД может реально (и достаточно неприятно) ощутить присутствие в системе транзакций других пользователей.

Журнализация

Одним из основных требований к СУБД является надежность хранения данных во внешней памяти. Под надежностью хранения понимается то, что СУБД должна быть в состоянии восстановить последнее согласованное состояние БД после любого аппаратного или программного сбоя. Аппаратные сбои обычно подразделяются на два вида:

    мягкие сбои связаны с внезапной остановкой работы компьютера. Обычно являются следствием внезапного выключения питания или "зависания" операционной системы (что особенно характерно для операционных систем Windows);

    жесткие сбои характеризуются потерей информации на носителях внешней памяти.

Программные сбои обычно возникают вследствие ошибок в программах. Причем эти ошибки могут быть как в самой СУБД, что может привести к аварийному завершению ее работы, так и в пользовательской программе. Первый случай можно рассматривать как разновидность мягкого аппаратного сбоя. Во втором случае незавершенной остается только одна транзакция.

В любом случае для восстановления информации в базе данных необходимо иметь некоторую дополнительную информацию. Таким образом, для поддержания надежности хранения данных требуется избыточность данных. Причем та часть информации, которая используется для восстановления, должна храниться особо надежно. Наиболее распространенным методом поддержания такой избыточной информации является ведение журнала изменений базы данных. Журнал представляет собой особую часть базы данных, недоступную пользователям СУБД и поддерживаемую с особой тщательностью (иногда используются две копии журнала, располагаемые на разных физических дисках), в которую поступают записи обо всех изменениях основной части базы данных. В разных СУБД изменения базы данных журнализируются на разных уровнях: иногда запись в журнале соответствует некоторой логической операции изменения базы данных, иногда - минимальной внутренней операции модификации страницы внешней памяти. Могут также использоваться одновременно оба подхода. Во всех случаях придерживаются стратегии «упреждающей» записи в журнал (так называемого протокола Write Ahead Log - WAL). Несколько утрированно можно сказать, что эта стратегия заключается в том, что запись об изменении любого объекта базы данных должна быть занесена в журнал до того, как будет выполнено и зафиксировано изменение этого объекта. Если в СУБД корректно соблюдается протокол WAL, то с помощью журнала можно решить все проблемы восстановле­ния базы данных после любого сбоя.

Самая простая ситуация восстановления - индивидуальный откат транзакции. Строго говоря, для этого не требуется общесистемный журнал изменений базы данных. Достаточно для каждой транзакции поддерживать локальный журнал операций модификации базы данных, выполненных в этой транзакции, и производить откат транзакции путем выполнения обратных операций, следуя" от конца локального журнала. В некоторых СУБД так и делают, но в большинстве систем локальные журналы не поддерживают, а индивидуальный откат транзакции выполняют по общесистемному журналу, для чего все записи, относящиеся к одной транзакции, связывают обратным списком (от конца к началу). При мягком сбое во внешней памяти основной части базы данных могут находиться объекты, модифицированные транзакциями, не закончившимися к моменту сбоя, и могут отсутствовать объекты, модифицированные транзакциями, которые к моменту сбоя успешно завершились (по причине использования буферов оперативной памяти, содержимое которых при мягком сбое пропадает). При соблюдении протокола WAL во внешней памяти журнала должны гарантированно находиться записи, относящиеся к операциям модификации обоих видов объектов. Целью процесса восстановления после мягкого сбоя является приведение внешней памяти основной части базы данных в такое состояние, которое возникло бы при фиксации во внешней памяти изменений всех завершившихся транзакций и которое не содержало бы никаких следов незаконченных транзакций. Для того чтобы этого добиться, сначала производят откат незавершенных транзакций, а потом повторно воспроизводят те операции завершенных транзакций, результаты которых не отображены во внешней памяти.

Для восстановления базы данных после жесткого сбоя используют журнал и архивную копию базы данных. Архивная копия - это полная копия базы данных к моменту начала заполнения журнала (хотя имеется много вариантов трактовки смысла архивной копии). Для нормального восстановления базы данных после жесткого сбоя, естественно, необходимо, чтобы журнал не пропал. Тогда восстановление базы данных состоит в том, что, исходя из архивной копии, по журналу воспроизводится работа всех транзакций, которые закончились к моменту сбоя. В принципе можно даже воспроизвести работу незавершенных транзакций и продолжить их работу после завершения восстановления. Однако в реальных системах это обычно не делается, поскольку процесс восстановления после жесткого сбоя является достаточно длительным.

Концепция транзакций - неотъемлемая часть любой клиент-серверной базы данных.

Под транзакцией понимается неделимая с точки зрения воздействия на БД последовательность операторов манипулирования данными (чтения, удаления, вставки, модификации), приводящая к одному из двух возможных результатов: либо последовательность выполняется целиком, если все операторы правильные, либо вся транзакция откатывается, если хотя бы один оператор не может быть успешно выполнен. Обработка транзакций гарантирует целостность информации в базе данных. Таким образом, транзакция переводит базу данных из одного целостного состояния в другое.

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

уменьшение баланса исходящего счета;

увеличение баланса принимающего счета.

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

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

BEGIN TRAN - объявление начала транзакции (в журнале транзакций фиксируются первоначальные значения изменяемых данных и момент начала транзакции).

COMMIT TRAN - фиксация транзакции (если в теле транзакции не было ошибок, то эта команда предписывает серверу зафиксировать все изменения, сделанные в транзакции, после чего в журнале транзакций помечается, что изменения зафиксированы и транзакция завершена).

ROLLBACK TRAN - откат транзакции (когда сервер встречает эту команду, происходит откат транзакции (отмена всех изменений), восстанавливается первоначальное состояние системы и в журнале транзакций отмечается, что транзакция была отменена).

Рассмотрим следующий пример.

Откройте новое окно запроса и выберите Sales в качестве активной базы данных

BEGIN TRANSACTION

Будет запущена транзакция. Все модификации данных в этом соединении не будут видны для других соединений.

Введите и выполните следующий запрос

VALUES ("Новый город")

Чтобы проверить, что модификация прошла успешно, введите и выполните следующий запрос

В таблице появилась новая запись, но эти изменения видны только в данном соединении

Откройте новое окно запроса, введите и выполните в нем предыдущий запрос. Запрос не вернет результатов, поскольку он ждет завершения транзакции, запущенной в другом окне.

Вернитесь в первое окно, введите и выполните следующий запрос

ROLLBACK TRANSACTION

Модификация данных отменена. Вернитесь во второе окно. Обратите внимание, что запрос выполнился и вернул данные. Добавленная строка отсутствует.

Операция оформления нового заказа предполагает добавление новых записей сразу в две таблицы: Order и OrdItem. Реализуем данную двойную операцию в виде единой транзакции:

INSERT (IdCust)

INSERT OrdItem(IdOrd,IdProd,Qty,Price)

VALUES (SCOPE_IDENTITY(),1,1,5)

Когда мы работает с базами данных (далее – БД), то обычно чаще всего нам необходимо выполнить одно из 4 действий: создать, прочитать, изменить либо удалить (для этого набора действий существует аббревиатура CRUD – Create Read Update Delete). Если мы хотим выполнить одно из таких действий нам необходимо выполнить транзакцию. Когда мы говорим о транзакциях в контексте БД, то мы имеем в виду последовательность действий с конечным количеством операций для достижения определённой цели, которая рассматривается как единое целое. Другими словами, если одна из операция в последовательности не выполнена, то вся последовательность считается не выполненной. Управление транзакциями является важной частью любой системой управления базой данных (далее – СУБД), оно обеспечивает целостность и однозначность данных.

Основные концепции транзакции описываются аббревиатурой ACID – Atomicity, Consistency, Isolation, Durability (Атомарность, Согласованность, Изолированность, Долговечность).

Атомарность

Атомарность гарантирует, что любая транзакция будет зафиксирована только целиком (полностью). Если одна из операций в последовательности не будет выполнена, то вся транзакция будет отменена. Тут вводится понятие “отката” (rollback). Т.е. внутри последовательности будут происходить определённые изменения, но по итогу все они будут отменены (“откачены”) и по итогу пользователь не увидит никаких изменений.

Согласованность

Это означает, что любая завершённая транзакция (транзакция, которая достигла завершения транзакции – end of transaction) фиксирует только допустимые результаты. Например, при переводе денег с одного счёта на другой, в случае, если деньги ушли с одного счёта, они должны прийти на другой (это и есть согласованность системы). Списание и зачисление – это две разные транзакции, поэтому первая транзакция пройдёт без ошибок, а второй просто не будет. Именно поэтому крайне важно учитывать это свойство и поддерживать баланс системы.

Изолированность

Каждая транзакция должна быть изолирована от других, т.е. её результат не должен зависеть от выполнения других параллельных транзакций. На практике, изолированность крайне труднодостижима вещь, поэтому здесь вводится понятие “уровни изолированности” (транзакция изолируется не полностью).

Долговечность

Эта концепция гарантирует, что если мы получили подтверждение о выполнении транзакции, то изменения, вызванные этой транзакцией не должны быть отменены из-за сбоя системы (например, отключение электропитания).

В реальной жизни любая качественная СУБД поддерживает все эти 4 концепции для каждой транзакции. Если рассмотреть транзакцию упрощённо, то транзакция (при работе с SQL) выглядит примерно так:

  • Пользователь начинает транзакцию, используя команду “начать транзакцию” (begin transatcion);
  • Системы выполняют операцию создания, изменения или удаления используя SQL-запрос;
  • В случае, если все операции успешны, выполняется операция “выполнить” (commit). Если есть ошибка – выполняется откат всех операций (rollback);

Для различных API управления транзакциями Spring поддерживает абстрактный слой. Spring добавляет возможность транзакций для POJO (Plain Old Java Object – простые старые java-объекты).

Виды управления транзакциями в Spring

поддерживает 2 вида управления транзакциями:

Способ управления транзакциями с помощью программирования. Этот способ более сложный для чтения и поддержки, чем декларативный, но даёт большую гибкость.

В случае декларативного управления транзакциями мы отделяем бизнес-логику от управления транзакциями. Мы используем либо аннотации, либо XML-файл для конфигурации управления транзакциями.

На практике, чаще всего используется декларативный метод управления транзакциями. Хоть этот метод и менее гибкий, чем программный, он может быть модульным (как и AOP). Стоит отметить, что декларативный метод реализован с помощью модуля АОП.

Spring является альтернативой EJB, который требует сервер для запуска приложения, в то время как управление транзакциями в Spring может быть реализовано без сервера приложения.

Абстракции транзакций в Spring

Главные абстракции транзакций в Spring определены в интерфейсе PlatformTransactionManager , который находится в пакете org.springframework.transaction.

В нём указаны 3 метода:

TransactionStatus getTransaction(TransactionDefinition definition);

Этот метод возвращает текущую активную транзакцию, либо создаёт новую в соответствии с определением.

void commit(TransactionStatus status);

Этот метод выполняет транзакцию в соответствии с её статусом.

void rollback(TransactionStatus status);

Этот метод выполняет откат транзакции.

Интерфейс TransactionDefinition включает в себя 5 методов

int getPropagationBehavior()

Возвращает метод распространения.

int getIsolationLevel()

Возвращает уровень изолирования.

String getName()

Возвращает имя транзакции.

int getTimeout()

Возвращает время в секундах, в течение которого транзакция должна быть завершена.

boolean isReadOnly()

Метод возвращает логическое значение (true или false) доступен ли файл исключительно для чтения.

И наш крайний ключевой интерфейс TransactionStatus , который обеспечивает простой способ контроля за статусом выполнения транзакции. В нём определены следующие методы:

boolean hasSavepoint()
Этот метод возвращает логическое значение, имеет ли данная транзакция точку сохранения.

boolean isCompleted()

Возвращает логическое значение завершена ли данная транзакция (успешно завершена, либо выполнен откат).

boolean isNewTransaction()

Возвращает логическое значение является текущая транзакция новой.

boolean isRollbackOnly()

Возвращает логическое значение, была ли эта транзакция отмечена, как rollback-only.

void setRollbackOnly()

Этот метод устанавливает параметр транзакции rollback-only.

В этой статье мы ознакомились с основами управления транзакциями в Spring Framework.

Управление транзакциями. Основные стратегии.

Для управления взаимодействием транзакций используются уровни изолированности. Изолированность – состояние работы, при котором пользователь не ощущает присутствия других лиц. Уровень изолированности определяет уровень, при котором в транзакции допускаются несогласованные данные, то есть степень изолированности одной транзакции от другой. Более высокий уровень изолированности повышает точность данных, но при этом может снижаться количество параллельно выполняемых транзакций. С другой стороны, более низкий уровень изолированности позволяет выполнять больше параллельных транзакций, но снижает точность данных.

Стандарт SQL-92 определяет следующие четыре уровня изоляции, установка которых предотвращает определенные конфликтные ситуации («+» – предотвращает, «–» – не предотвращает):

Уровень Запрет читать измененные данные Запрет изменять прочитанные данные Запрет добавления Примечание
read uncommitted (чтение незафиксированных данных) Низший уровень изоляции. Гарантирует только отсутствие потерянных обновлений.
read committed (чтение фиксированных данных) + Принятый по умолчанию уровень для Microsoft SQL Server. Отсутствует черновое, «грязное» чтение. Тем не менее в процессе работы одной транзакции другая может быть успешно завершена и сделанные ею изменения зафиксированы (не решена проблема неповторяемого чтения)
repeatable read (повторяемость чтения) + + Уровень, при котором чтение одной и той же строки или строк в транзакции дает одинаковый результат
Serializable (упорядочиваемость) + + + Самый высокий уровень изолированности; транзакции полностью изолируются друг от друга

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

Сериальный план выполнения смеси транзакций – это такой план, который приводит к сериализации транзакций. Если удается добиться действительно сериального выполнения смеси транзакций, то для каждого пользователя, по инициативе которого образована транзакция, присутствие других транзакций будет незаметно (если не считать некоторого замедления работы по сравнению с однопользовательским режимом).

Существует несколько базовых алгоритмов сериализации транзакций.

1) Последовательное исполнение : выполняется только одна транзакция, остальные ждут ее завершения. Приводит к большим задержкам времени ожидания. Для ускорения следует разрешить любую работу с различными данными и одновременное чтение одних и тех же данных;

2) Использование синхронизационных блокировок : транзакция при обращении к данным накладывает блокировку (захват). Обычно используется два типа блокировок: на чтение и на запись. Если транзакции требуются данные и они свободны, то выполняется работа с данными; если они заблокированы, проверяется возможность совместимости: при совместимости работаем с данными, при несовместимости – ожидаем их освобождения. Блокировки снимаются при завершении транзакции. Для поддержки захватов используется двухфазный протокол синхронизации (двухфазное блокирование).

1 фаза – транзакция захватывает данные по мере обращения к ним:

2 фаза – одновременное освобождение всех данных по завершению транзакции.

Основной недостаток: возможность взаимоблокировок (тупиков):

При наличии тупика (deadlock) ожидание будет бесконечным, поэтому необходимо нестандартное разрешение. Конфликты блокировок решаются следующими методами:

полуавтоматический вариант : при обнаружении конфликта посылается запрос пользователю. Пользователь принимает решение – продолжить ожидание или произвести откат транзакции;

автоматический вариант: при обнаружении конфликта после заданного времени ожидания выполняется автооткат транзакции, первой обнаружившей тупик;

анализ наличия тупика: автоматически определяется разрешимость конфликта, при этом используется граф ожидания, в котором указаны транзакции и обрабатываемые объекты. Если обнаруживается тупик, откатывается одна из транзакций.

Граф ожидания – инструмент, используемый при разработке СУБД и многопоточных систем и используемый, в частности, для определения ситуации взаимной блокировки. Представляет собой ориентированный двудольный граф, содержащий вершины двух типов:

─ вершины типа T, соответствующие транзакциям или выполняющимся потокам. Они образуют первую долю графа;

─ вершины типа R, соответствующие ресурсам и объектам, которые могут быть захвачены транзакциями. Они образуют вторую долю графа.

Дуги графа ожидания также имеют двоякий смысл:

─ дуги (T, R), идущие из вершины-транзакции T в вершину-ресурс R, обозначают, что данный ресурс уже захвачен транзакцией

─ дуги (R, T), идущие из вершины-ресурса R в вершину-транзакцию T обозначают, что транзакция T ожидает, пока ресурс R будет освобождён.

Простейшие свойства:

1. Ресурс, не имеющий ни одной входящей дуги, является свободным.

2. Если вершина-транзакция имеет некоторое ненулевое количество входящих дуг, то соответствующая транзакция находится в состоянии ожидания.

3. Если между двумя транзакциями T 1 ® T 2 существует путь, то транзакция T 1 должна быть выполнена (завершена) раньше, чем начнётся выполнение T 2 , поскольку последняя требует освобождения некоторых ресурсов, захваченных транзакцией T 1 .

Из последнего свойства очевидным образом следует, что ситуации взаимной блокировки соответствует цикл на графе ожидания.

Для обнаружения цикла используется редукция графа. При этом поочередно выполняются два шага:

─ удаление дуг от неожидающих транзакций;

─ удаление дуг от свободных данных к транзакциям.

Если не удается удалить некоторый набор дуг, то это - цикл. Для разрешения тупика откатывается одна из транзакций.

3) Временные метки – транзакция при инициализации получает метку – это время начала. При обращении к данным, транзакция помечает его своей меткой. При обнаружении конфликта, метки сравниваются. Более молодая транзакция откатывается

При высоком уровне изолированности возникают большие затраты на ожидание. Для повышения быстродействия могут использоваться дополнительные возможности управления многопользовательским доступом:

─ многоуровневое блокирование: для записи, ячейки, массива записей, или всей таблицы (обычно при мелкой блокировке – много меток, при высокой – сразу блокируется большой кусок);

─ многоверсионная организация. Применена в InterBase.. Сущность её состоит в том, что все изменения, проводимые над конкретными записями, производятся не над самой записью, а над ее версией. Версия записи - это копия записи, которая создается при попытке ее изменить. Если какой-то транзакции нужно работать с какой-либо записью, транзакция обращается к последней зафиксированной версии записи;

─ использование оптимистического буферирования. При этом данные храниться в буфере до тех пор, пока принудительно не выполняется запись в базу. Различают пессимистическое (запись в БД при записи в буфер) и оптимистическое буферирование (запись выполняется после завершения транзакции, но потом проверяется - были ли изменения в данных. Если изменения были, то пользователь получает сообщение об этом.


15. Защита от несанкционированного доступа

Безопасность данных (data security) - концепция защиты программ и данных от случайного либо умышленного изменения, уничтожения, разглашения, а также несанкционированного использования.

Безопасность базы данных заключается в защите базы данных от несанкционированного доступа со стороны пользователей. Без привлечения соответствующих мер безопасности интегрированные в БД данные становятся более уязвимыми, чем данные в файловой системе. Однако интеграция позволяет определить требуемую систему безопасности базы данных, а СУБД привести ее в действие.

Защита данных от несанкционированного доступа предполагает:

─ предотвращение несанкционированного доступа к базе данных (обеспечение парольного входа в систему: регистрация пользователей, назначение и изменение паролей);

─ обеспечение защиты конкретных данных: определение прав доступа групп пользователей и отдельных пользователей, определение допустимых операций над данными для отдельных пользователей, выбор/создание программно-технологических средств защиты данных, шифрование информации в целях защиты данных от несанкционированного использования;

─ фиксация попыток несанкционированного доступа к информации;

─ аудит действий пользователей в базе данных;

─ исследование возникающих случаев нарушения защиты данных и проведение мероприятий по их предотвращению.

Безопасность базы данных можно разделить на две части:

1) Безопасность системы, которая охватывает доступ к базе данных на системном уровне. Безопасность системы включает в себя:

─ проверка правильности комбинации имени и пароля пользователя;

─ контроль системных операций, которые пользователю разрешено выполнять.

2) Безопасность данных включает механизмы, которые управляют доступом к объектам базы данных. Безопасность данных определяет:

─ к каким объектам базы данных имеет доступ пользователь;

─ какие действия пользователь может выполнять с объектами базы данных (извлечение, вставка, обновление, удаление).

Защита может выполняться на разных уровнях:

1. Защита от непосредственного доступа к базе данных. Применяются следующие методы:

1) Использование закрытого (сложного для чтения, уникального) формата данных.

2) Шифрование данных. Могут шифроваться как файлы целиком, так и отдельные поля.

Методика введения полного шифрования – шифрование в конце, расшифровка в начале программы.

Важнейшими характеристиками алгоритмов шифрования являются криптостойкость, длина ключа и скорость шифрования.

В криптографии обычно рассматриваются два типа криптографических алгоритмов. Это классические криптографические алгоритмы, основанные на использовании секретных ключей, и новые криптографические алгоритмы с открытым ключом, основанные на использовании ключей двух типов: секретного (закрытого) и открытого, так называемые двухключевые алгоритмы.

3) Самый популярный метод защиты – перенос базы на защищенный носитель (защищённый сервер).

Транзакция - это последовательность операций над БД, рассматриваемых СУБД как единое целое. Либо транзакция успешно выполняется, и СУБД фиксирует (COMMIT) изменения БД, произведенные этой транзакцией, во внешней памяти, либо ни одно из этих изменений никак не отражается на состоянии БД. Понятие транзакции необходимо для поддержания логической целостности БД. Если вспомнить наш пример информационной системы с файлами СОТРУДНИКИ и ОТДЕЛЫ, то единственным способом не нарушить целостность БД при выполнении операции приема на работу нового сотрудника является объединение элементарных операций над файлами СОТРУДНИКИ и ОТДЕЛЫ в одну транзакцию. Таким образом, поддержание механизма транзакций является обязательным условием даже однопользовательских СУБД (если, конечно, такая система заслуживает названия СУБД). Но понятие транзакции гораздо более важно в многопользовательских СУБД.

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

С управлением транзакциями в многопользовательской СУБД связаны важные понятия сериализации транзакций и сериального плана выполнения смеси транзакций . Под сериализаций параллельно выполняющихся транзакций понимается такой порядок планирования их работы, при котором суммарный эффект смеси транзакций эквивалентен эффекту их некоторого последовательного выполнения. Сериальный план выполнения смеси транзакций - это такой план, который приводит к сериализации транзакций. Понятно, что если удается добиться действительно сериального выполнения смеси транзакций, то для каждого пользователя, по инициативе которого образована транзакция, присутствие других транзакций будет незаметно (если не считать некоторого замедления работы по сравнению с однопользовательским режимом).



Существует несколько базовых алгоритмов сериализации транзакций. В централизованных СУБД наиболее распространены алгоритмы, основанные на синхронизационных захватах объектов БД. При использовании любого алгоритма сериализации возможны ситуации конфликтов между двумя или более транзакциями по доступу к объектам БД. В этом случае для поддержания сериализации необходимо выполнить откат (ликвидировать все изменения, произведенные в БД) одной или более транзакций. Это один из случаев, когда пользователь многопользовательской СУБД может реально (и достаточно неприятно) ощутить присутствие в системе транзакций других пользователей.

Журнализация

Одним из основных требований к СУБД является надежность хранения данных во внешней памяти. Под надежностью хранения понимается то, что СУБД должна быть в состоянии восстановить последнее согласованное состояние БД после любого аппаратного или программного сбоя. Обычно рассматриваются два возможных вида аппаратных сбоев: так называемые мягкие сбои, которые можно трактовать как внезапную остановку работы компьютера (например, аварийное выключение питания), и жесткие сбои, характеризуемые потерей информации на носителях внешней памяти. Примерами программных сбоев могут быть: аварийное завершение работы СУБД (по причине ошибки в программе или в результате некоторого аппаратного сбоя) или аварийное завершение пользовательской программы, в результате чего некоторая транзакция остается незавершенной. Первую ситуацию можно рассматривать как особый вид мягкого аппаратного сбоя; при возникновении последней требуется ликвидировать последствия только одной транзакции.

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

Журнал - это особая часть БД, недоступная пользователям СУБД и поддерживаемая с особой тщательностью (иногда поддерживаются две копии журнала, располагаемые на разных физических дисках), в которую поступают записи обо всех изменениях основной части БД. В разных СУБД изменения БД журнализуются на разных уровнях: иногда запись в журнале соответствует некоторой логической операции изменения БД (например, операции удаления строки из таблицы реляционной БД), иногда - минимальной внутренней операции модификации страницы внешней памяти; в некоторых системах одновременно используются оба подхода.

Во всех случаях придерживаются стратегии "упреждающей" записи в журнал (так называемого протокола Write Ahead Log - WAL). Грубо говоря, эта стратегия заключается в том, что запись об изменении любого объекта БД должна попасть во внешнюю память журнала раньше, чем измененный объект попадет во внешнюю память основной части БД. Известно, что если в СУБД корректно соблюдается протокол WAL, то с помощью журнала можно решить все проблемы восстановления БД после любого сбоя.

Самая простая ситуация восстановления - индивидуальный откат транзакции. Строго говоря, для этого не требуется общесистемный журнал изменений БД. Достаточно для каждой транзакции поддерживать локальный журнал операций модификации БД, выполненных в этой транзакции, и производить откат транзакции путем выполнения обратных операций, следуя от конца локального журнала. В некоторых СУБД так и делают, но в большинстве систем локальные журналы не поддерживают, а индивидуальный откат транзакции выполняют по общесистемному журналу, для чего все записи от одной транзакции связывают обратным списком (от конца к началу).

При мягком сбое во внешней памяти основной части БД могут находиться объекты, модифицированные транзакциями, не закончившимися к моменту сбоя, и могут отсутствовать объекты, модифицированные транзакциями, которые к моменту сбоя успешно завершились (по причине использования буферов оперативной памяти, содержимое которых при мягком сбое пропадает). При соблюдении протокола WAL во внешней памяти журнала должны гарантированно находиться записи, относящиеся к операциям модификации обоих видов объектов. Целью процесса восстановления после мягкого сбоя является состояние внешней памяти основной части БД, которое возникло бы при фиксации во внешней памяти изменений всех завершившихся транзакций и которое не содержало бы никаких следов незаконченных транзакций. Для того, чтобы этого добиться, сначала производят откат незавершенных транзакций (undo), а потом повторно воспроизводят (redo) те операции завершенных транзакций, результаты которых не отображены во внешней памяти. Этот процесс содержит много тонкостей, связанных с общей организацией управления буферами и журналом. Более подробно мы рассмотрим это в соответствующей лекции.

Для восстановления БД после жесткого сбоя используют журнал и архивную копию БД. Грубо говоря, архивная копия - это полная копия БД к моменту начала заполнения журнала (имеется много вариантов более гибкой трактовки смысла архивной копии). Конечно, для нормального восстановления БД после жесткого сбоя необходимо, чтобы журнал не пропал. Как уже отмечалось, к сохранности журнала во внешней памяти в СУБД предъявляются особо повышенные требования. Тогда восстановление БД состоит в том, что исходя из архивной копии по журналу воспроизводится работа всех транзакций, которые закончились к моменту сбоя. В принципе, можно даже воспроизвести работу незавершенных транзакций и продолжить их работу после завершения восстановления. Однако в реальных системах это обычно не делается, поскольку процесс восстановления после жесткого сбоя является достаточно длительным.

Поддержка языков БД

Для работы с базами данных используются специальные языки, в целом называемые языками баз данных . В ранних СУБД поддерживалось несколько специализированных по своим функциям языков. Чаще всего выделялись два языка - язык определения схемы БД (SDL - Schema Definition Language) и язык манипулирования данными (DML - Data Manipulation Language). SDL служил главным образом для определения логической структуры БД, т.е. той структуры БД, какой она представляется пользователям. DML содержал набор операторов манипулирования данными, т.е. операторов, позволяющих заносить данные в БД, удалять, модифицировать или выбирать существующие данные. Мы рассмотрим более подробно языки ранних СУБД в следующей лекции.

В современных СУБД обычно поддерживается единый интегрированный язык, содержащий все необходимые средства для работы с БД, начиная от ее создания, и обеспечивающий базовый пользовательский интерфейс с базами данных. Стандартным языком наиболее распространенных в настоящее время реляционных СУБД является язык SQL (Structured Query Language). В нескольких лекциях этого курса язык SQL будет рассматриваться достаточно подробно, а пока мы перечислим основные функции реляционной СУБД, поддерживаемые на "языковом" уровне (т.е. функции, поддерживаемые при реализации интерфейса SQL).

Прежде всего, язык SQL сочетает средства SDL и DML, т.е. позволяет определять схему реляционной БД и манипулировать данными. При этом именование объектов БД (для реляционной БД - именование таблиц и их столбцов) поддерживается на языковом уровне в том смысле, что компилятор языка SQL производит преобразование имен объектов в их внутренние идентификаторы на основании специально поддерживаемых служебных таблиц-каталогов. Внутренняя часть СУБД (ядро) вообще не работает с именами таблиц и их столбцов.

Язык SQL содержит специальные средства определения ограничений целостности БД. Опять же, ограничения целостности хранятся в специальных таблицах-каталогах, и обеспечение контроля целостности БД производится на языковом уровне, т.е. при компиляции операторов модификации БД компилятор SQL на основании имеющихся в БД ограничений целостности генерирует соответствующий программный код.

Специальные операторы языка SQL позволяют определять так называемые представления БД, фактически являющиеся хранимыми в БД запросами (результатом любого запроса к реляционной БД является таблица) с именованными столбцами. Для пользователя представление является такой же таблицей, как любая базовая таблица, хранимая в БД, но с помощью представлений можно ограничить или наоборот расширить видимость БД для конкретного пользователя. Поддержание представлений производится также на языковом уровне.

Наконец, авторизация доступа к объектам БД производится также на основе специального набора операторов SQL. Идея состоит в том, что для выполнения операторов SQL разного вида пользователь должен обладать различными полномочиями. Пользователь, создавший таблицу БД, обладает полным набором полномочий для работы с этой таблицей. В число этих полномочий входит полномочие на передачу всех или части полномочий другим пользователям, включая полномочие на передачу полномочий. Полномочия пользователей описываются в специальных таблицах-каталогах, контроль полномочий поддерживается на языковом уровне.

Более точное описание возможных реализаций этих функций на основе языка SQL будет приведено в лекциях, посвященных языку SQL и его реализации.

Статьи по теме: