Уровни изолированности транзакций

В связи со свойством сохранения целостности БД транзакции являются подходящими единицами изолированности пользователей. Действительно, если с каждым сеансом работы с базой данных ассоциируется транзакция, то каждый пользователь начинает работу с согласованным состоянием базы данных, т.е. с таким состоянием, в котором база данных могла бы находиться, даже если бы пользователь работал с ней в одиночку. Следовательно требуется, чтобы обновления, выполняемые некоторой транзакцией T1, не были доступны для любой другой транзакции Т2 до тех и только до тех пор, пока не будет завершено выполнение транзакции Т1. Завершение выполнения транзакции открывает доступ ко всем обновлениям, выполненным данной транзакцией.

Существует несколько уровней изолированности транзакций. По приоритету их можно расположить в следующем порядке: READ UNCOMMITTED (незавершенное считывание) < READ COMMITTED (завершенное считывание) < REPEATABLE READ (повторяемое считывание) < SERIALIZABLE (способность к упорядочению). Не все СУБД поддерживают все уросни изолированности транзакций. Например, PARADOX (IDE Borland Builder) поддерживает уровни DIRTY READ, READ COMMITED и REPEATABLE READ.

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

1. Потерянные изменения. Допустим, транзакция Т1 изменяет объект базы данных A. До завершения транзакции Т1 транзакция Т2 также изменяет объект A. Транзакция Т2 завершается оператором ROLLBACK (например, по причине нарушения ограничений целостности). Тогда при повторном чтении объекта А транзакция Т1 не видит изменений этого объекта, произведенных ранее. Такая ситуация называется ситуацией потерянных изменений. Естественно, она противоречит требованию изолированности пользователей. Чтобы избежать такой ситуации, требуется до завершения транзакции Т1 запретить любой другой транзакции изменять объект А. Отсутствие потерянных изменений является минимальным требованием к многопользовательской СУБД, поскольку в этом случае обеспечивается требование целостности БД при завершении любой транзакции. Метод устранения: до завершения Т1 никакая другая транзакция не должна изменять объект А;

2. Неаккуратное считывание или чтение «грязных данных». Допустим, что транзакция Т1 выполняет вставку новой строки, затем транзакция Т2 извлекает эту строку, после чего выполнение транзакции Т1 отменяется. В результате транзакция Т2 обнаружит, что данной строки больше не существует в том смысле, что она никогда не существовала (поскольку транзакция Т1 действительно не была выполнена); Можно рассмотреть еще один сценарий совместного выполнения транзакций Т1 и Т2. Транзакция Т1 изменяет объект базы данных A. Параллельно с этим транзакция Т2 читает объект A. Поскольку операция изменения еще не завершена, транзакция Т2 видит несогласованные "грязные" данные (в частности, операция транзакции Т2 может быть отвернута при проверке немедленно проверяемого ограничения целостности). Это тоже не соответствует требованию изолированности пользователей (каждый пользователь начинает свою транзакцию при согласованном состоянии базы данных и вправе ожидать увидеть согласованные данные). Чтобы избежать ситуации чтения "грязных" данных, до завершения транзакции Т1, изменившей объект A, никакая другая транзакция не должна иметь возможности читать объект A (минимальным требованием является блокировка чтения объекта A до завершения операции его изменения в транзакции Т1). Метод устранения: до завершения Т1 никакая другая транзакция не должна читать объект А;

3. Неповторяемое считывание. Допустим, транзакция Т1 читает объект базы данных A. До завершения транзакции Т1 транзакция Т2 изменяет объект A и успешно завершается оператором COMMIT. Транзакция Т1 повторно читает объект A и видит его измененное состояние. Чтобы избежать неповторяющихся чтений, до завершения транзакции Т1 никакая другая транзакция не должна иметь возможности изменять объект A. В большинстве систем это является максимальным требованием к синхронизации транзакций, хотя, отсутствие неповторяющихся чтений еще не гарантирует реальной изолированности пользователей. Метод устранения: до завершения Т1 никакая другая транзакция не должна иметь возможность изменять объект А;

4. Наличие фиктивных элементов. Допустим, что транзакция Т1 извлекает множество всех записей, которые удовлетворяют некоторому условию (например, записи всех поставщиков из Парижа). Допустим, что транзакция Т2 вставляет новую строку, которая удовлетворяет тому же условию и успешно завершается. Если транзакция Т1 вновь повторит ту же операцию извлечения, что и раньше, то ею будет обнаружена ранее отсутствовавшая – "фиктивная" строка. Конечно, такая ситуация противоречит идее изолированности транзакций и может возникнуть даже на третьем уровне изолированности транзакций. Чтобы избежать появления кортежей-фантомов, требуется более высокий "логический" уровень синхронизации транзакций. Идеи такой синхронизации (предикатные синхронизационные захваты) известны давно, но в большинстве систем не реализованы.

Возможность возникновения этих нарушений при различных уровнях изоляции транзакций приведена в таблице:

Уровень изоляции Неаккуратное считывание Неповторяемое считывание Фиктивные элементы
READ UNCOMMITTED Да Да Да
READ COMMITTED Нет Да Да
REPEATABLE READ Нет Нет Да
SERIALIZABLE Нет Нет Нет

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

Наши рекомендации