Выбрать главу

Во время успешного подтверждения старая версия записи становится устаревшей записью.

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

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

Сведения о чистке и сборке мусора см. в разд. "Гигиена базы данных" главы 15. Руководство по чистке находится в главе 39.

В итоге при нормальных условиях:

* любая транзакция может читать любую строку, которая была подтверждена до старта этой транзакции;

* любая транзакция чтения/записи может запрашивать изменение или удаление строки;

* пересылка (post) обычно будет успешной, если никакая другая транзакция чтения/записи не пересылала и не подтверждала изменения для новой версии этой записи. Транзакциям READ COMMITTED разрешается пересылать изменения, перезаписывающие подтвержденные версии более новых транзакций;

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

Блокировка на уровне таблицы

Транзакция может быть сконфигурирована для блокирования всей таблицы. Существует два приемлемых способа сделать это в DSQL: установив уровень изоляции транзакции в SNAPSHOT TABLE STABILITY (известный также как согласованный режим, поддерживаемый повторяемым чтением, repeatable read) или с помощью резервирования таблицы. Следует подчеркнуть, что такие настройки нужны в случае, когда требуются приоритетные условия. Они не рекомендуются для ежедневного использования в Firebird.

Неприемлемый способ установления блокировки на уровне таблицы (Firebird 1.5 и выше) - применение пессимистической блокировки на уровне оператора, которая действует на всю таблицу. Оператор подобный следующему делает это:

SELECT * FROM ATABLE

FOR UPDATE WITH LOCK;

Строго говоря, это не является вопросом конфигурирования транзакции. Тем не менее конфигурирование транзакции, которая владеет наборами, найденными с этой явной пессимистической блокировкой, является важным. Вопросы пессимистической блокировки обсуждаются в главе 27.

Добавление

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

"Старение" и статистика транзакций

Когда клиентский процесс передает дескриптор транзакции, он получает уникальный внутренний идентификатор для транзакции и сохраняет его в инвентарной странице транзакции (Transaction Inventory Page, TIP).

Идентификатор и возраст транзакции

Идентификатор транзакции является 32-битовым целым, которое генерируется с единичным приращением. Новая или только восстановленная база данных начинает Серию идентификаторов с единицы. Возраст транзакции определяется ее идентификатором: самая старая имеет наименьший идентификатор.

Идентификаторы транзакций и ассоциированные с ними состояния данных сохраняются в инвентарных страницах транзакций. На заголовочной странице базы данных система учета поддерживает набор полей, содержащих идентификаторы транзакций, интересующих эту систему, а именно старейшую заинтересованную транзакцию (Oldest Interesting Transaction, OIT), старейшую активную транзакцию (Oldest Active Transaction, OAT) и число, используемое в следующей транзакции. "Мгновенный снимок" идентификатора транзакции также записывается каждый раз, когда увеличивается OAT - обычно тот же самый идентификатор, что и OAT, или близкий к нему.

Получение идентификатора транзакции

Firebird 1.5 и более поздние версии имеют контекстную переменную CURRENT? TRANSACTION, которая возвращает идентификатор данной транзакции. Она может использоваться в SQL в любом выражении. Например, для сохранения идентификатора транзакции в таблице протокола вы можете использовать следующее:

INSERT INTO BOOK_OF_LIFE

(TID, COMMENT, SIGNATURE) VALUES

(CURRENT_TRANSACTION,

'This has been a great day for transactions', CUERENT_USER) ;

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

Переполнение идентификатора транзакции

Как было сказано, идентификатор транзакции является 32-битовым целым. Если последовательность идентификаторов превысит лимит в 4 Гбайт и начнется новый отсчет, то могут произойти неприятные вещи. Когда завершится последняя транзакция, системная транзакция перестанет работать, и изменение метаданных будет невозможным. Сборка мусора прекратится. Транзакции пользователя не станут запускаться.

При 100 транзакциях в секунду это займет 1 год, 4 месяца, 11 дней, 2 часа и приблизительно 30 минут[90].

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

"Заинтересованные транзакции"

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

Активные, зависшие (limbo), отмененные и "умершие" транзакции все являются интересными. Транзакции, которые были подтверждены с использованием оператора COMMIT WITH RETAIN (мягкое подтверждение или CommitRetaining), остаются активными, пока не будут подтверждены жестко, и поэтому являются интересными. В некоторых случаях интересные транзакции могут стать "застрявшими" и станут тормозить работу.

Заброшенные, застрявшие транзакции станут источником серьезного ухудшения производительности. Застрявшие OIT (старейшие заинтересованные транзакции) приведут к росту количества инвентарных страниц транзакций. Сервер поддерживает рабочую таблицу транзакций в виде битовой маски, хранящейся в инвентарных страницах транзакций. Таблица копируется и записывается в базу данных при старте каждой транзакции[91]. Когда она непомерно раздуется от застрявших транзакций, она

будет использовать гораздо больше ресурсов памяти, и память станет фрагментированной от постоянного выделения ресурсов.

Старейшая заинтересованная транзакция

OIT является транзакцией с наименьшим номером в инвентарной странице транзакций, которая находится в неподтвержденном (rolled back) состоянии.

Старейшая активная транзакция

OAT- это транзакция с наименьшим номером в инвентарной странице транзакций, которая является активной. Транзакция является активной до тех пор, пока она не подтверждена жестко, не выполнен ее откат, и если она не является зависшей (limbo)[92].

Транзакции только для чтения
вернуться

90

Спасибо, Ann Harrison!

вернуться

91

В оригинале не совсем верно выбраны термины. Никакой "таблицы", конечно, нет. Есть битовый массив, который меняется при изменении состояния транзакций и который копируется в память (частично) при старте транзакций SNAPSHOT. Также TIP не может быть "раздут", поскольку битовый массив просто растет в размерах при старте каждой новой транзакции и строится заново при очередном восстановлении (restore). Размер TIP равен Next Transaction / 4 (в байтах). - Прим. науч. ред.

вернуться

92

Здесь и далее в тексте практически не упоминается важный параметр Oldest Snapshot Transaction (OS Г). - Прим, науч. ред.