6. Транзакции
Все операции в РЕД Базе Данных выполняются в рамках транзакции.
Транзакция - логическая единица изолированной работы группы последовательных операций над базой данных.
Изменения остаются обратимыми до тех пор, пока клиентское приложение не выдаст серверу инструкцию COMMIT.
В РЕД Базе Данных есть небольшое количество SQL-операторов для управления транзакциями:
SET TRANSACTION- определение параметров транзакции и её старт;COMMIT- завершение транзакции и фиксация изменений;ROLLBACK- отмена изменений, выполненных в рамках транзакции, или выполняет откат к точке сохранения;SAVEPOINT- определение точки сохранения для частичного отката изменений;RELEASE SAVEPOINT- удаление точки сохранения.
6.1. SET TRANSACTION
Оператор SET TRANSACTION определяет параметры транзакции и запускает её.
Синтаксис оператора:
Листинг 6.1 Синтаксис оператора запуска транзакции SET TRANSACTION
SET TRANSACTION
[NAME <имя транзакции>]
[<параметры транзакции> ...]
<параметры транзакции> ::=
READ {ONLY | WRITE}
| [WAIT [LOCK TIMEOUT <кол-во секунд>] | NO WAIT]
| [ISOLATION LEVEL] <уровень изоляции>
| NO AUTO UNDO
| AUTO COMMIT
| [AUTO RELEASE TEMP BLOBID]
| IGNORE LIMBO
| RESERVING <список таблиц для резервирования>
| USING <хендл БД> [,<хендл БД>]
<уровень изоляции> ::=
SNAPSHOT [AT NUMBER <номер снимка транзакции>]
| SNAPSHOT TABLE [STABILITY]
| READ COMMITTED [[NO] RECORD_VERSION | READ CONSISTENCY]
<список таблиц для резервирования> ::= <определение таблицы> [, <определение таблицы> ...]
<определение таблицы> ::= <имя таблицы> [, <имя таблицы> ...]
[FOR [SHARED | PROTECTED] {READ | WRITE}]
Параметры оператора SET TRANSACTION:
Имя транзакции |
Имя транзакции. Доступно только в |
|---|---|
Параметры транзакции |
Необязательные параметры транзакции. Каждая опция может быть указана не более одного раза, а некоторые опции являются взаимоисключающими (например, |
Кол-во секунд |
Время в секундах, в течение которого оператор будет ждать в случае возникновения конфликта. Должно быть равно 0 или больше. |
Номер снимка транзакции |
Номер снимка другой транзакции, данные которой должны быть общими с новой транзакцией. |
Хендл БД |
Хендл базы данных, к которой транзакция может получить доступ. Доступно только в |
Оператор SET TRANSACTION задаёт параметры транзакции и запускает её.
Старт транзакции осуществляется только клиентскими приложениями, но не сервером
(за исключением автономных транзакций и некоторых фоновых системных потоков/процессов, например, sweep).
Клиентское приложение может запускать любое количество параллельно выполняющихся транзакций. Одно соединение может иметь несколько одновременных активных транзакций. Фактически есть ограничение на общее количество выполняемых транзакций во всех клиентских приложениях, работающих с одной конкретной базой данных с момента последнего восстановления базы данных из резервной копии или с момента создания базы данных. Это ограничение составляет \(2^{48} - 1\), то есть 281474976710656.
Все предложения в операторе SET TRANSACTION являются необязательными.
Если предложения не указаны, то предполагается старт транзакции
со значениями всех характеристик по умолчанию
(режим доступа, режим разрешения блокировок и уровень изоляции).
По умолчанию транзакция запускается со следующими характеристиками:
SET TRANSACTION
READ WRITE
WAIT
ISOLATION LEVEL SNAPSHOT;
Предупреждение
Драйверы баз данных или компоненты доступа могут использовать другие значения по умолчанию для транзакций, запускаемых через их API.
Сервер последовательно присваивает транзакциям целые номера.
Каждый раз, когда клиент запускает какую-либо транзакцию, определенную явно или по умолчанию,
сервер отправляет клиенту идентификатор транзакции.
Этот номер можно узнать с помощью контекстной переменной CURRENT_TRANSACTION.
Некоторые драйверы баз данных требуют, чтобы транзакция настраивалась
и запускалась с помощью методов API.
В этом случае использование SET TRANSACTION либо не поддерживается,
либо может привести к неоднозначному поведению.
Имя транзакции
Необязательное предложение NAME задаёт имя транзакции.
Предложение NAME доступно только в Embedded SQL.
Если предложение NAME не указано,
то оператор SET TRANSACTION применяется к транзакции по умолчанию.
За счёт именованных транзакций позволяется одновременный запуск
нескольких активных транзакций в одном приложении.
При этом должна быть объявлена и инициализирована одноименная переменная базового языка.
В DSQL, это ограничение предотвращает динамическую спецификацию имён транзакций.
Параметры транзакции
Основными характеристиками транзакции являются:
Режим доступа к данным (
READ WRITE, READ ONLY),Режим разрешения блокировок (
WAIT, NO WAIT) с возможным дополнительным уточнением (LOCK TIMEOUT),Уровень изоляции (
READ COMMITTED,SNAPSHOT,SNAPSHOT TABLE STABILITY),средства резервирования или освобождения таблиц (предложение
RESERVING).
Режим доступа
Для транзакций существует два режима доступа к данным базы данных: READ WRITE и READ ONLY.
При режиме доступа READ WRITE операции в контексте данной транзакции
могут быть как операциями чтения, так и операциями изменения данных.
Это режим по умолчанию.
В режиме READ ONLY в контексте данной транзакции могут выполняться
только операции выборки данных SELECT.
Любая попытка изменения данных в контексте такой транзакции приведет к исключениям базы данных.
Однако это не относится к глобальным временным таблицам (GTT),
которые разрешено изменять в READ ONLY транзакциях.
Этот режим доступа совместно с некоторыми другими характеристиками
базы данных должен использоваться при работе
с базами данных только для чтения, находящимися на носителях, допускающих только чтение данных,
например, на компакт-дисках. Подробности см. в документе «Руководство администратора».
Режим разрешения блокировок
Когда несколько клиентских процессов работают с одной базой данных, могут возникать блокировки, когда один процесс вносит незафиксированные изменения в строку таблицы или удаляет строку, а другой процесс пытается обновить или удалить ту же строку. Такие блокировки называются конфликтами обновлений. Блокировки могут возникать и в других ситуациях, когда используется несколько уровней изоляции транзакций.
Есть два режима разрешения блокировок: WAIT и NO WAIT.
В режиме WAIT (режим по умолчанию), если конфликт возникает между двумя параллельными процессами,
выполняющими одновременное обновление данных в одной и той же базе данных,
транзакция WAIT будет ждать, пока другая транзакция не завершит работу - зафиксирует (COMMIT)
или откатится (ROLLBACK).
Клиентское приложение с транзакцией WAIT будет переведено в режим ожидания, пока конфликт не разрешится.
Если для транзакции WAIT указано LOCK TIMEOUT,
ожидание будет продолжаться только в течение указанного в этом пункте количества секунд.
Если по истечении указанного интервала блокировка не будет разрешена,
клиенту будет возвращено сообщение «Lock time-out on wait transaction».
В зависимости от уровня изоляции транзакции поведение при разрешении блокировки может незначительно отличаться.
В режиме NO WAIT при возникновении конфликта транзакция немедленно выбрасывает исключение из базы данных.
LOCK TIMEOUT - это отдельная опция, но может использоваться только для транзакций WAIT.
Указание LOCK TIMEOUT дл NO WAIT приведет к ошибке
«invalid parameter in transaction parameter block -Option isc_tpb_lock_timeout is not valid if isc_tpb_nowait was used previously in TPB».
Уровень изоляции
Уровень изоляции транзакций определяет уровень, при котором в транзакции допускаются несогласованные данные, то есть степень изолированности одной транзакции от другой. Изменения, внесённые некоторым оператором, будут видны всем последующим операторам, запущенным в рамках этой же транзакции, независимо от её уровня изоляции. Изменения, сделанные в рамках другой транзакции, остаются невидимыми для текущей транзакции до тех пор, пока они не подтверждены. Уровень изоляции определяет, как транзакция будет взаимодействовать с другой транзакцией, которая хочет подтвердить изменения.
Параметр ISOLATION LEVEL определяет уровень изоляции для запускаемой транзакции.
Это наиболее значимый параметр транзакции,
определяющий ее поведение по отношению к другим параллельно выполняющимся транзакциям.
В РЕД Базе Данных поддерживаются три уровня изоляции:
SNAPSHOTSNAPSHOT TABLE STABILITYREAD COMMITTEDс тремя уточнениями (READ CONSISTENCY,NO RECORD_VERSIONиRECORD_VERSION)
Уровень изоляции SNAPSHOT
С этим уровнем изоляции транзакция запускается по умолчанию.
Уровень изоляции SNAPSHOT позволяет транзакции видеть только те изменения,
которые были зафиксированы до ее запуска.
Любые зафиксированные изменения, сделанные параллельными транзакциями,
не будут видны транзакции SNAPSHOT, пока она активна. Изменения станут видны новой транзакции,
когда текущая транзакция будет зафиксирована или откачена,
но не в том случае, если это будет только откат к точке сохранения.
Изменения, сделанные автономными транзакциями, не видны в контексте транзакции SNAPSHOT, которая их запустила.
Предложение AT NUMBER
Транзакцию с уровнем изолированности SNAPSHOT можно запустить на основе другой транзакции,
если известен номер её снимка.
В этом случае эта новая транзакция может видеть те же самые данные,
что и транзакция на основе которой она запущена.
Предложение AT NUMBER позволяет создавать параллельные процессы (в разных подключениях),
считывающие согласованные данные из базы данных.
Например, процесс резервного копирования может создавать несколько потоков,
параллельно считывающих данные из базы данных.
Или веб-служба работать с распределенными вспомогательными службами, выполняя некоторую обработку.
Для этого используется следующий синтаксис:
SET TRANSACTION SNAPSHOT AT NUMBER <номер снимка транзакции>
<номер снимка транзакции> можно получить используя:
select RDB$GET_CONTEXT('SYSTEM', 'SNAPSHOT_NUMBER') from rdb$database;
Для обмена стабильным снимком между транзакциями другая транзакция
также должна иметь уровень изоляции SNAPSHOT.
При выполнении READ COMMITTED номер моментального снимка переместится вперед.
Уровень изоляции SNAPSHOT TABLE STABILITY
Уровень изоляции SNAPSHOT TABLE STABILITY (или SNAPSHOT TABLE) является самым строгим.
Как и в SNAPSHOT, транзакция SNAPSHOT TABLE STABILITY видит только те изменения,
которые были зафиксированы до начала текущей транзакции.
После запуска SNAPSHOT TABLE STABILITY никакие другие транзакции
не могут вносить изменения в любую таблицу в базе данных, в которой есть изменения,
ожидающие этой транзакции. Другие транзакции могут читать другие данные,
но любая попытка вставки, обновления или удаления параллельным процессом
приведет к возникновению конфликтных исключений.
Чтобы разрешить другим транзакциям изменять данные в некоторых таблицах,
можно использовать предложение RESERVING.
Если любая другая транзакция ожидает незафиксированного изменения в любой (не SHARED) таблице,
перечисленной в предложении RESERVING,
попытка запустить транзакцию SNAPSHOT TABLE STABILITY приведет к неопределенному ожиданию
(по умолчанию или явному WAIT) или исключению (NO WAIT или по истечении LOCK TIMEOUT).
Для таблиц, указанных в предложении RESERVING, в параллельных
транзакциях в зависимости от их уровня изоляции допустимы при различных
способах резервирования следующие варианты поведения:
SHARED READ— позволяет всем параллельным транзакциям независимо от их уровня изоляции не только читать, но и выполнять любые изменения в резервируемых таблицах (если параллельная транзакция имеет режим доступаREAD WRITE);SHARED WRITE— для всех параллельных транзакций с уровнем доступаREAD WRITEи с уровнями изоляцииSNAPSHOTиREAD COMMITTEDпозволяет читать данные из таблиц и писать данные в указанные таблицы, для транзакций с уровнем изоляцииSNAPSHOT TABLE STABILITYзапрещает не только запись, но также и чтение данных из указанных таблиц;PROTECTED READ— допускает только лишь чтение данных из резервируемых таблиц для параллельных транзакций с любым уровнем изоляции;PROTECTED WRITE— для параллельных транзакций с уровнями изоляцииSNAPSHOTиREAD COMMITTEDзапрещает запись в указанные таблицы, для транзакций с уровнем изоляцииSNAPSHOT TABLE STABILITYзапрещает также и чтение данных из резервируемых таблиц.
Уровень изоляции READ COMMITTED
Уровень изоляции READ COMMITTED позволяет текущей незафиксированной транзакции сразу же увидеть
все изменения данных,
которые были зафиксированы другими транзакциями с момента ее запуска.
Нефиксированные изменения не видны транзакции READ COMMITTED.
Чтобы получить обновленный набор строк в интересующей таблице,
необходимо снова выполнить оператор SELECT,
находясь все еще в незафиксированной транзакции READ COMMITTED.
Опции READ COMMITTED
Для транзакций READ COMMITTED может быть задан один из трех уточняющих параметров,
в зависимости от желаемого типа разрешения конфликтов:
READ CONSISTENCY, RECORD_VERSION или NO RECORD_VERSION.
Если в firebird.conf или в databases.conf параметр ReadConsistency установлен в 1,
указанные опции фактически игнорируются и ведут себя как READ CONSISTENCY.
В противном случае эти опции являются взаимоисключающими.
Опции READ COMMITTED:
NO RECORD_VERSION(по умолчанию, еслиReadConsistency = 0) - это механизм двухфазной блокировки: он делает транзакцию неспособной записывать в любую строку, в которой ожидается обновление от другой транзакции.Если указано
NO WAIT, то транзакция немедленно выдаст ошибку о конфликте блокировок.Если указано
WAIT, она будет ждать, пока другая транзакция не будет либо зафиксирована, либо отменена. Если другая транзакция откатывается, или если она зафиксирована и ее идентификатор транзакции старше идентификатора текущей транзакции, то изменение текущей транзакции разрешается. Если другая транзакция была зафиксирована и ее идентификатор был новее, чем у текущей транзакции, возвращается ошибка конфликта блокировок.
Если указано значение
RECORD_VERSION, транзакция считывает последнюю зафиксированную версию строки, независимо от других ожидающих версий строки. Режим разрешения блокировки (WAITилиNO WAIT) никак не влияет на поведение транзакции при ее старте.Если указано
READ CONSISTENCY(илиReadConsistency = 1), при выполнении оператора создается снимок базы данных, чтобы обеспечить последовательное чтение на уровне операторов транзакций, зафиксированных на момент начала выполнения.
Два других варианта могут привести к непоследовательным чтениям на уровне оператора,
поскольку они могут считывать некоторые, но не все изменения параллельной транзакции,
если эта транзакция фиксируется во время выполнения оператора.
Например, оператор SELECT COUNT(*) может прочитать некоторые,
но не все вставленные записи другой транзакции,
если фиксация этой транзакции произойдет во время чтения записей.
Снимок уровня оператора создается при выполнении оператора верхнего уровня, вложенные операторы (триггеры, хранимые процедуры и функции, операторы динамики и т. д.) используют снимок уровня оператора, созданный для оператора верхнего уровня.
Обработка конфликтов обновлений
Когда оператор выполняется в транзакции с режимом изоляции READ COMMITTED READ CONSISTENCY он видит неизменное
состояние базы данных (подобно транзакции SNAPSHOT). Поэтому не имеет смысла
ждать подтверждения параллельной транзакции в надежде перечитать новую версию
подтверждённой записи. При чтении поведение похоже на транзакцию READ COMMITTED RECORD_VERSION — оператор
не ждёт завершения активной транзакции и обходит цепочку
версий, в которой ищет версию записи, видимую на текущем моментальном снимке.
Для уровня изоляции READ COMMITTED READ CONSISTENCY обработка конфликтов
обновлений значительно изменяется. При обнаружении конфликта обновления
выполняется следующее:
Режим изолированности транзакции временно переключается в режим
READ COMMITTED NO RECORD_VERSION.Устанавливается блокировка записи на конфликтную запись.
Сервер оценивает оставшиеся записи для удаления/обновления в курсоре, а также продолжает ставить на них блокировки.
Когда больше нет записей для извлечения, сервер начинает отменять все действия, выполненные с момента начала выполнения оператора верхнего уровня, и сохраняет все установленные блокировки для каждой обновлённой/удалённой/заблокированной записи, все вставленные записи удаляются.
Затем восстанавливается уровень изоляции транзакции
READ COMMITTED READ CONSISTENCY, создается новый снимок уровня оператора и перезапускается выполнение оператора верхнего уровня.
Такой алгоритм гарантирует, что после перезапуска уже обновленные записи останутся заблокированными, они будут видны на новом снимке и могут быть обновлены снова без дальнейших конфликтов. Кроме того, из-за режима согласованности чтения набор измененных записей остается согласованным.
Замечания:
Приведенный выше алгоритм перезапуска применяется к операторам
UPDATE,DELETE,SELECT WITH LOCKиMERGE, с предложениемRETURNINGи без него, выполняемым непосредственно из пользовательского приложения или в составе некоторого объектаPSQL(хранимая процедура, функция, триггер,EXECUTE BLOCKи т. д.).Если оператор
UPDATE/DELETEрасположен на каком-либо явном курсоре (WHERE CURRENT OF), то пропускается шаг выше, то есть не извлекаются и не устанавливаются блокировки записи для оставшихся записей курсора.Если оператор верхнего уровня
SELECT(илиEXECUTE BLOCKвозвращающий набор данных) и конфликт обновления происходит после того, как одна или несколько записей были возвращены приложению, то ошибка конфликта обновления сообщается как обычно и перезапуск не инициируется.Перезапуск не инициируется для операторов в автономных блоках (
IN AUTONOMOUS TRANSACTION DO …).После 10 попыток прерывается алгоритм перезапуска, снимаются все блокировки записи, восстанавливается режим изоляции транзакции как
READ COMMITTED READ CONSISTENCYи сообщается о конфликте обновления.Любая необработанная ошибка на шаге выше останавливает алгоритм перезапуска и сервер продолжает работать в обычном режиме, например, ошибка может быть перехвачена и обработана
PSQL-блоком WHENили возвращена пользователю, если она не была обработана.Триггеры
UPDATE/DELETEсработают многократно для одной и той же записи, если выполнение оператора было перезапущено и запись обновлена/удалена снова.По историческим причинам
isc_update_conflictсообщается как вторичный код ошибки с основным кодом ошибкиisc_deadlock.
Опция NO AUTO UNDO
При использовании опции NO AUTO UNDO оператор ROLLBACK только помечает транзакцию
как отменённую без
удаления созданных в этой транзакции версий, которые будут удалены позднее в соответствии
с выбранной политикой сборки мусора (см. параметр GCPolicy в firebird.conf).
Эта опция может быть полезна при выполнении транзакции, в рамках которой производится много отдельных операторов, изменяющих данные, и при этом есть уверенность, что эта транзакция будет завершаться успешно, а не откатываться.
Для транзакций, в рамках которых не выполняется никаких изменений, опция NO AUTO UNDO игнорируется.
Опция AUTO COMMIT
Указание AUTO COMMIT включает режим автокоммита для транзакции.
В режиме автоматической фиксации сервер будет внутренне
выполнять эквивалент COMMIT RETAIN после каждого выполнения оператора.
Это не совсем удобный режим автокоммита; контекст транзакции сохраняется до тех пор,
пока транзакция не будет завершена фиксацией или откатом.
Другими словами, при использовании SNAPSHOT или SNAPSHOT TABLE STABILITY
эта автокоммитация не изменит видимость записей (эффекты транзакций, которые были зафиксированы после запуска этой транзакции, не будут видны).
Для READ COMMITTED действуют те же предупреждения, что и для COMMIT RETAIN:
длительное использование одной транзакции в режиме автокоммита
может препятствовать сборке мусора и снижать производительность.
Опция IGNORE LIMBO
При указании опции IGNORE LIMBO игнорируются записи, создаваемые "потерянными" (т.е. не завершёнными)
транзакциями (limbo transaction). Транзакции считается "потерянной", если не завершён второй этап
двухфазного подтверждения (two-phase commit).
Опция AUTO RELEASE TEMP BLOBID
При использовании опции AUTO RELEASE TEMP BLOBID транзакция автоматически
освобождает временный идентификатор пользовательского BLOB сразу после его материализации.
Это полезно при массовых вставках записей с пользовательскими BLOB,
так как при этом устраняются затраты памяти на хранение временных идентификаторов.
Опцию AUTO RELEASE TEMP BLOBID следует использовать с осторожностью и только в том случае,
если нет необходимости обращаться к материализованному BLOB через временный идентификатор.
РЕД База Данных автоматически применяет эту опцию при выполнении восстановления базы данных.
Предложение RESERVING
Предложение RESERVING в операторе SET TRANSACTION резервирует таблицы, указанные в списке таблиц.
Резервирование таблицы не позволяет другим транзакциям вносить в нее изменения или даже, при включении определенных параметров, считывать из нее данные во время выполнения этой транзакции.
Предложение RESERVING также может использоваться для указания списка таблиц,
которые могут быть изменены другими транзакциями,
даже если транзакция запущена с уровнем изоляции SNAPSHOT TABLE STABILITY.
Синтаксис предложения резервирования представлен в листинге .
Листинг 6.2 Синтаксис предложения резервирования
RESERVING <предложение резервирования>
<предложение резервирования> ::= <имя таблицы> [, <имя таблицы> ...]
[FOR [SHARED | PROTECTED] {READ | WRITE}]
[, <предложение резервирования>] ...
Если одно из ключевых слов SHARED или PROTECTED опущено, предполагается SHARED.
Если опущен весь пункт FOR, предполагается FOR SHARED READ.
Названия и совместимость четырех вариантов доступа для резервирования таблиц:
|
|
|
|
|
|---|---|---|---|---|
SHARED READ |
да |
да |
да |
да |
SHARED WRITE |
да |
да |
нет |
нет |
PROTECTED READ |
да |
нет |
да |
нет |
PROTECTED WRITE |
да |
нет |
нет |
нет |
Комбинации этих флагов пункта RESERVING для параллельного доступа зависят
от уровней изоляции параллельных транзакций:
SNAPSHOTПараллельные транзакции
SNAPSHOTсSHARED READне влияют на доступ друг друга.Параллельные транзакции
SNAPSHOTиREAD COMMITTEDсSHARED WRITEне влияют на доступ друг друга, но блокируют транзакции с изоляциейSNAPSHOT TABLE STABILITYот чтения из или записи в указанную таблицу.Параллельные транзакции с любым уровнем изоляции и
PROTECTED READмогут только читать данные из зарезервированных таблиц. Любая попытка записи в них вызовет исключение.При использовании
PROTECTED WRITEодновременные транзакции с изоляциейSNAPSHOTиREAD COMMITTEDне могут записывать в указанные таблицы. Транзакции с изоляциейSNAPSHOT TABLE STABILITYвообще не могут читать из зарезервированных таблиц или писать в них.SNAPSHOT TABLE STABILITYВсе параллельные транзакции с
SHARED READ, независимо от их уровней изоляции, могут читать из зарезервированных таблиц или записывать в них (если они находятся в режимеREAD WRITE).Параллельные транзакции с уровнями изоляции
SNAPSHOTиREAD COMMITTEDиSHARED WRITEмогут читать данные из указанных таблиц и записывать их (если находятся в режимеREAD WRITE), но одновременный доступ к этим таблицам со стороны транзакций сSNAPSHOT TABLE STABILITYблокируется, пока эти транзакции активны.Параллельные транзакции с любым уровнем изоляции и
PROTECTED READмогут читать только из зарезервированных таблиц.С
PROTECTED WRITEодновременные транзакцииSNAPSHOTиREAD COMMITTEDмогут читать из зарезервированных таблиц, но не записывать в них. Доступ транзакций с уровнем изоляцииSNAPSHOT TABLE STABILITYблокируется.READ COMMITTEDПри
SHARED READвсе параллельные транзакции с любым уровнем изоляции могут как читать из зарезервированных таблиц, так и записывать в них (если они находятся в режимеREAD WRITE).SHARED WRITEпозволяет всем транзакциям с изоляциейSNAPSHOTиREAD COMMITTEDчитать из указанных таблиц и записывать в них (если в режимеREAD WRITE) и блокирует доступ транзакций с изоляциейSNAPSHOT TABLE STABILITY.При
PROTECTED READпараллельные транзакции с любым уровнем изоляции могут читать только из зарезервированных таблиц.С
PROTECTED WRITEпараллельные транзакции в изоляцииSNAPSHOTиREAD COMMITTEDмогут читать из указанных таблиц, но не записывать в них. Доступ от транзакций в изоляцииSNAPSHOT TABLE STABILITYблокируется.
6.2. Подтверждение транзакции
Чтобы подтвердить текущую транзакцию используется оператор COMMIT (см. листинг ).
Листинг 6.3 Синтаксис оператора подтверждения транзакции COMMIT
COMMIT [WORK] [TRANSACTION <имя транзакции>]
[RELEASE] [RETAIN [SNAPSHOT]];
При выполнении этого оператора подтверждаются все изменения в данных, выполненные в контексте данной
транзакции (добавления, изменения, удаления). Новые версии записей становятся доступными для других
процессов. Если не указано предложение RETAIN, то при этом освобождаются все ресурсы сервера,
связанные с выполнением данной транзакции. Если в процессе подтверждения транзакции
возникли ошибки в базе данных, то транзакция не подтверждается. Пользовательская программа должна
обработать ошибочную ситуацию и заново подтвердить транзакцию или выполнить ее откат.
Необязательное ключевое слово WORK может быть использовано лишь для
совместимости с другими системами управления реляционными базами данных.
Необязательное предложение TRANSACTION задаёт имя транзакции. Предложение
TRANSACTION доступно только в Embedded SQL. Если предложение TRANSACTION не
указано, то оператор COMMIT применяется к транзакции по умолчанию.
Ключевое слово RELEASE доступно только в Embedded SQL. Оно позволяет отключиться ото
всех баз данных после завершения текущей транзакции. RELEASE поддерживается только для
обратной совместимости со старыми версиями серверов базы данных.
Если используется предложение RETAIN [SNAPSHOT], то выполняется так
называемое мягкое (soft) подтверждение. Выполненные действия в
контексте данной транзакции фиксируются в базе данных, а сама
транзакция продолжает оставаться активной. В этом случае нет
необходимости опять стартовать транзакцию и заново выдавать оператор
SELECT для получения данных из таблицы. Если уровень изоляции такой
транзакции SNAPSHOT или SNAPSHOT TABLE STABILITY, то после мягкого
подтверждения транзакция продолжает видеть то состояние базы данных,
которое было при первоначальном запуске транзакции, то есть клиентская
программа не видит новых подтвержденных результатов изменения данных
других процессов. Кроме того, мягкое подтверждение не освобождает
ресурсов сервера.
Примечание
Для транзакций, которые выполняют только чтение данных из базы данных,
рекомендуется также использовать оператор COMMIT, а не ROLLBACK,
поскольку этот вариант требует меньшего количества ресурсов сервера и
улучшает производительность всех последующих транзакций.
6.3. Откат (отмена) транзакции
Для отмены всех изменений, выполненных в контексте текущей транзакции, или для отката на созданную
ранее в контексте транзакции контрольную точку используется оператор ROLLBACK.
Листинг 6.4 Синтаксис оператора отката транзакции ROLLBACK
ROLLBACK [WORK] [TRANSACTION <имя транзакции>]
[RETAIN [SNAPSHOT] | TO SAVEPOINT <имя точки сохранения>] [RELEASE];
При выполнении оператора отменяются все изменения данных базы данных (добавление, изменение, удаление),
выполненные под управлением этой транзакции. Оператор ROLLBACK никогда не вызывает ошибок.
Если не указано предложение RETAIN, то при его выполнении освобождаются все ресурсы сервера,
связанные с выполнением данной транзакции.
Необязательное ключевое слово WORK может быть использовано лишь для совместимости с
другими системами управления реляционными базами данных.
Необязательное предложение TRANSACTION задаёт имя транзакции. Предложение доступно только в Embedded SQL.
Если предложение TRANSACTION не указано, то оператор ROLLBACK применяется к транзакции по умолчанию.
Необязательное предложение TO SAVEPOINT задает имя точки сохранения, на
которую происходит откат. Подробнее о вложенных транзакциях и точках
сохранения см. в следующем разделе.
Ключевое слово RETAIN указывает, что все действия по изменению данных в контексте этой транзакции,
отменяются, при этом контекст транзакции сохраняется. Выделенные ресурсы для транзакции не освобождаются. Для
уровней изоляции SNAPSHOT и SNAPSHOT TABLE STABILITY состояние базы данных остается в том виде,
которое база данных имела при первоначальном старте такой транзакции, однако в случае уровня изоляции
READ COMMITTED база данных будет иметь вид, соответствующий новому состоянию на момент выполнения
оператора ROLLBACK RETAIN. В случае отмены транзакции с сохранением ее контекста нет необходимости заново
выполнять оператор SELECT для получения данных из таблицы.
Ключевое слово RELEASE доступно только в Embedded SQL. Оно позволяет отключиться ото
всех баз данных после завершения текущей транзакции. RELEASE поддерживается только для
обратной совместимости со старыми версиями серверов базы данных.
6.4. Использование вложенных транзакций
Вложенные транзакции (nested transactions) позволяют в процессе выполнения действий с базой данных в
контексте одной длинной транзакции создавать некоторые контрольные точки, или точки сохранения, к которым
можно вернуться, не отменяя действий всей транзакции. В этом случае состояние базы данных станет соответствовать
тому состоянию, которое база данных имела на момент создания этой точки сохранения (здесь учитываются только те
изменения, которые были выполнены в контексте данной транзакции). В процессе активности транзакции можно создавать
произвольное количество точек сохранения. Они упорядочиваются в хронологическом порядке — по мере их создания.
Для создания точки сохранения используется оператор SAVEPOINT. Его синтаксис представлен в
листинге .
Листинг 6.5 Синтаксис оператора создания точки сохранения SAVEPOINT
SAVEPOINT <имя точки сохранения>;
Имя точки сохранения — обычный идентификатор базы данных, который может содержать до 63 символов.
Имена точек сохранения, созданных в контексте одной транзакции, должны
отличаться. Если же в операторе SAVEPOINT создается точка сохранения с
именем, уже присутствующем в списке созданных точек сохранения в
процессе активности данной транзакции, то существующая точка
сохранения будет удалена, и создаётся новая с тем же именем.
Любую созданную в транзакции точку сохранения можно удалить, выполнив
оператор RELEASE SAVEPOINT. Синтаксис оператора представлен в листинге .
Листинг 6.6 Синтаксис оператора удаления точки сохранения RELEASE SAVEPOINT
RELEASE SAVEPOINT <имя точки сохранения> [ONLY];
Оператор удаляет указанную по ее имени точку сохранения из списка. Если
не указано ключевое слово ONLY, то удаляются и все последующие точки
сохранения. Ключевое слово ONLY задает удаление только одной указанной
точки сохранения без какого-либо влияния на другие последующие.
Если точка сохранения с таким именем отсутствует, то не выдается никакого сообщения об ошибке. Операция удаления точки сохранения «молчаливо» не выполняется.
Для отката транзакции на одну из созданных ранее в контексте этой
транзакции точек сохранения используется оператор ROLLBACK TO SAVEPOINT.
Его синтаксис в этом случае выглядит следующим образом:
Листинг 6.7 Синтаксис оператора отката транзакции на точку сохранения ROLLBACK TO SAVEPOINT
ROLLBACK TO SAVEPOINT <имя точки сохранения>
[WORK];
Ключевое слово WORK используется только лишь для совместимости с другими
реляционными системами управления базами данных и со стандартом SQL-92.
Оператор отменяет только те изменения, которые были сделаны в базе данных в контексте данной транзакции после создания указанной точки сохранения.
Оператор ROLLBACK TO SAVEPOINT выполняет следующие операции:
Все изменения в базе данных, выполненные в рамках транзакции начиная с созданной точки сохранения, отменяются. Пользовательские переменные, заданные с помощью функции
RDB$SET_CONTEXT()остаются неизменными.Все точки сохранения, создаваемые после названной, уничтожаются. Все более ранние точки сохранения, как сама точка сохранения, остаются. Это означает, что можно откатываться к той же точке сохранения несколько раз.
Все явные и неявные блокированные записи, начиная с точки сохранения, освобождаются. Другие транзакции, запросившие ранее доступ к строкам, заблокированным после точки сохранения, должны продолжать ожидать, пока транзакция не фиксируется или откатывается. Другие транзакции, которые ещё не запрашивали доступ к этим строкам, могут запросить и сразу же получить доступ к разблокированным строкам.
Транзакция продолжает оставаться активной, как если бы было задано ключевое слово RETAIN.
Если точка сохранения с таким именем отсутствует, то не выдается никакого сообщения. Операция отката просто не выполняется.
6.5. Внутренние точки сохранения
По умолчанию сервер использует автоматическую системную точку сохранения уровня транзакции для выполнения
её отката. При выполнении оператора ROLLBACK, все изменения, выполненные в транзакции, откатываются до
системной точки сохранения и после этого транзакция подтверждается.
Когда объем изменений, выполняемых под системной точкой сохранения уровня транзакции, становится большим (затрагивается порядка 50000 записей) сервер освобождает системную точку сохранения и, при необходимости отката транзакции, использует механизм TIP.
6.6. Точки сохранения и PSQL
Использование операторов управления транзакциями в PSQL не разрешается, так как это нарушит атомарность оператора, вызывающего процедуру. Но РЕД База Данных поддерживает вызов и обработку исключений в PSQL, так, чтобы действия, выполняемые в хранимых процедурах и триггерах, могли быть выборочно отменены без полного отката всех действий в них. Внутренне автоматические точки сохранения используется для:
отмены всех действий внутри блока
BEGIN ... END, где происходит исключение;отмены всех действий, выполняемых в хранимой процедуре/триггере (или, в случае селективной хранимой процедуры, всех действий, выполненных с момента последнего оператора
SUSPEND), если они завершаются преждевременно из-за непредусмотренной ошибки или исключения.
Каждый блок обработки исключений PSQL также ограничен автоматическими точками сохранения сервера.
Сам по себе блок BEGIN ... END не создаёт автоматическую точку сохранения. Она создаётся только в блоках,
которых присутствует блок WHEN для обработки исключений или ошибок.
6.7. Вариант взаимной блокировки
Существует вероятность того, что две конкурирующие транзакции создадут ситуацию взаимной блокировки, или
как ее еще называют «смертельная блокировка» (dead lock). Такая взаимная блокировка произойдет, если
первая транзакция ожидает завершения второй транзакции (подтверждения или отмены), а вторая
транзакция ожидает в том или ином виде завершения первой транзакции. Для появления взаимной блокировки
обе конкурирующие транзакции должны иметь режим разрешения блокировки WAIT. Их уровни изоляции могут быть
SNAPSHOT или READ COMMITTED. Взаимная блокировка возможна и в случае уровня изоляции транзакции
SNAPSHOT TABLE STABILITY, если предложение резервирования при старте этой транзакции дает возможность другим
транзакциям изменять данные отдельных таблиц базы данных.
Например, первая транзакция изменила в некоторой таблице данные первой строки и не подтвердила изменения. Вторая транзакция изменила данные второй строки той же таблицы и также не подтвердила эти изменения. После этого первая транзакция пытается изменить вторую строку. Поскольку в другой транзакции выполнены неподтвержденные изменения этой строки, первая транзакция переходит в режим ожидания. Далее вторая транзакция, являясь активной и дееспособной, пытается изменить первую строку таблицы, неподтвержденные изменения которой выполнила первая транзакция. Вторая транзакция тут же перейдет в режим ожидания. Обе транзакции будут ожидать соответствующих действий друг от друга, в этом случае создается взаимная блокировка.
В РЕД База Данных существует Менеджер блокировок (Lock Manager), который
отслеживает и обрабатывает подобные ситуации взаимных блокировок.
Менеджер блокировок вызывается с определенной периодичностью, которая
задается в файле конфигурации системы firebird.conf параметром
DeadlockTimeout. Значением по умолчанию является интервал в 10 секунд.
По истечении этого срока Менеджер блокировок разрешит ситуацию взаимной
блокировки, создав исключительную ситуацию («взаимная блокировка,
изменение конфликтует с параллельным изменением») для одной из
транзакций, включенных в конфликтную ситуацию.