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:

Таблица 6.1 Параметры оператора SET TRANSACTION

Имя транзакции

Имя транзакции. Доступно только в ESQL.

Параметры транзакции

Необязательные параметры транзакции. Каждая опция может быть указана не более одного раза, а некоторые опции являются взаимоисключающими (например, READ ONLY и READ WRITE, WAIT и NO WAIT).

Кол-во секунд

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

Номер снимка транзакции

Номер снимка другой транзакции, данные которой должны быть общими с новой транзакцией.

Хендл БД

Хендл базы данных, к которой транзакция может получить доступ. Доступно только в ESQL.

Оператор 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 определяет уровень изоляции для запускаемой транзакции. Это наиболее значимый параметр транзакции, определяющий ее поведение по отношению к другим параллельно выполняющимся транзакциям.

В РЕД Базе Данных поддерживаются три уровня изоляции:

  • SNAPSHOT

  • SNAPSHOT TABLE STABILITY

  • READ 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 обработка конфликтов обновлений значительно изменяется. При обнаружении конфликта обновления выполняется следующее:

  1. Режим изолированности транзакции временно переключается в режим READ COMMITTED     NO RECORD_VERSION.

  2. Устанавливается блокировка записи на конфликтную запись.

  3. Сервер оценивает оставшиеся записи для удаления/обновления в курсоре, а также продолжает ставить на них блокировки.

  4. Когда больше нет записей для извлечения, сервер начинает отменять все действия, выполненные с момента начала выполнения оператора верхнего уровня, и сохраняет все установленные блокировки для каждой обновлённой/удалённой/заблокированной записи, все вставленные записи удаляются.

  5. Затем восстанавливается уровень изоляции транзакции 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.

Названия и совместимость четырех вариантов доступа для резервирования таблиц:

Таблица 6.2 Совместимости различных блокировок

SHARED READ

SHARED WRITE

PROTECTED READ

PROTECTED WRITE

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 секунд. По истечении этого срока Менеджер блокировок разрешит ситуацию взаимной блокировки, создав исключительную ситуацию («взаимная блокировка, изменение конфликтует с параллельным изменением») для одной из транзакций, включенных в конфликтную ситуацию.