Delphi-Help

Главная Статьи FireBird/Interbase Работа с транзакциями и их использование в FIBPlus. Часть 3

Работа с транзакциями и их использование в FIBPlus. Часть 3

Оцените материал
(0 голосов)


Работа с транзакциями и их использование в FIBPlus. Часть 3

Средства резервирования таблиц

Уровень SNAPSHOT TABLE STABILITY задает резервирование используемых в такой транзакции таблиц. Существуют дополнительные средства, позволяющие в транзакции задать резервирование отдельных таблиц или, наоборот, при уровне SNAPSHOT TABLE STABILITY разрешать изменения отдельных таблиц другими транзакциями. В SQL это фраза резервирования. В буфере параметров транзакции — это константы isc_tpb_lock_read, isc_tpb_lock_write, isc_tpb_exclusive, isc_tpb_shared, isc_tpb_protected. Рассмотрим возможности их использования на практике.

Резервирование таблиц на уровне изоляции READ COMMITTED

Запустите два экземпляра программы. В первой установите следующие параметры транзакции:

isc_tpb_write
isc_tpb_read_committed
isc_tpb_nowait
isc_tpb_rec_version
isc_tpb_lock_write=REFCOUNTRY
isc_tpb_protected

В языке SQL такому набору параметров соответствует следующий оператор:

SET TRANSACTION
READ COMMITTED READ WRITE
NO WAIT
RECORD_VERSION
RESERVING REFCOUNTRY FOR PROTECTED WRITE;

Внимание! Параметр isc_tpb_protected должен располагаться сразу после имени резервируемой таблицы.

Для второй программы задайте стандартный набор транзакции READ COMMITTED:

isc_tpb_write
isc_tpb_read_committed
isc_tpb_nowait
isc_tpb_rec_version

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

Теперь остановите транзакции в программах и закройте наборы данных. Запустите транзакцию в первой программе, но не открывайте набор данных. Во второй программе запустите транзакцию, откройте набор данных и попытайтесь выполнить изменения. Вы опять получите исключение. Мы видим, что резервирование таблиц выполняется не в момент открытия набора данных, как это происходит при уровне изоляции SNAPSHOT TABLE STABILITY, а в момент запуска транзакции.

Замените в первой транзакции параметр isc_tpb_protected на isc_tpb_exclusive. Результат будет таким же.

Если заменить в первой транзакции isc_tpb_lock_write=REFCOUNTRY на isc_tpb_lock_read=REFCOUNTRY, то получим точно такой же результат. Если же установить isc_tpb_shared, то фактически никакого резервирования при использовании уровня изоляции READ COMMITTED не будет.

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

Таким образом, средства резервирования для транзакции READ COMMITTED позволяют предотвратить изменение зарезервированной таблицы другой транзакцией с любым уровнем изоляции.

Резервирование таблиц на уровне изоляции SNAPSHOT

Установите для первой программы следующие характеристики транзакции:

isc_tpb_write
isc_tpb_concurrency
isc_tpb_nowait
isc_tpb_lock_read=REFCOUNTRY
isc_tpb_exclusive

Проведите те же эксперименты, что и в случае READ COMMITTED. Результаты будут в точности такими же.

Резервирование таблиц на уровне изоляции SNAPSHOT TABLE STABILITY

Уровень изоляции SNAPSHOT TABLE STABILITY не позволяет другим транзакциям изменять данные в используемых таблицах. Однако средства резервирования дают возможность разрешить такие изменения для отдельных таблиц. В первой программе задайте характеристики транзакции:

isc_tpb_write
isc_tpb_nowait
isc_tpb_lock_write=REFCOUNTRY
isc_tpb_shared
isc_tpb_consistency

Если вторая транзакция имеет уровень изоляции READ COMMITTED или SNAPSHOT, то ей предоставляется возможность совместного изменения данных указанной таблицы. Одновременное изменение данных одной и той же строки в этом случае, разумеется, также невозможно.

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

Напоминание. Параметры isc_tpb_shared, isc_tpb_exclusive и isc_tpb_protected должны следовать сразу же за параметром isc_tpb_lock_write (isc_tpb_lock_read). Имя любой таблицы не должно появляться более одного раза в конструкциях резервирования.

Использование разделенных транзакций

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

Создайте на базе существующего проекта новый проект Transaction2. Внесите изменения в программу. Добавьте еще один компонент транзакции с именем Transaction2. Ее мы будем использовать как транзакцию обновления. В компоненте базы данных и в обоих наборах данных задайте транзакцию для обновления Transaction2 (в Database это свойство DefaultUpdateTransaction, в наборах данных — UpdateTransaction). В наборах данных установите в True свойство AutoCommit и подсвойство poStartTransaction свойства Options. Этим мы создаем короткую транзакцию обновления. Она будет запускаться каждый раз, когда программа будет отправлять в базу данных изменения (операция Post) и тут же будет автоматически подтверждаться при отсутствии ошибок базы данных.

Для второй транзакции установите свойство TPBMode в значение tpbDefault. Установите следующие значения в окне TRParams:

isc_tpb_write
isc_tpb_read_committed
isc_tpb_nowait
isc_tpb_rec_version

Замечание. Этот слегка измененный вариант нашей исследовательской программы представлен в проекте Transaction2.

Запустите программу и задайте характеристики первой транзакции:

isc_tpb_read
isc_tpb_concurrency
isc_tpb_nowait

Это соответствует уровню изоляции SNAPSHOT. Запустите транзакцию, откройте набор данных и измените любую строку. Щелкните мышью по любой другой строке, чтобы выполнить операции Post и Commit для измененной записи. В строке появится старое, неизмененное значение столбца! Все правильно. Наша транзакция для чтения, Transaction1, не видит изменений, выполненных в той же программе транзакцией для обновления Transaction2. Даже если вы закроете набор данных и заново его откроете, ничего не изменится. Нужно остановить транзакцию, запустить ее заново и отрыть набор данных. Только тогда в программе мы увидим новое, измененное значение столбца. Транзакция для чтения не видит изменений другой транзакции, пусть даже они обе находятся в одной программе.

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

isc_tpb_read
isc_tpb_nowait
isc_tpb_consistency

Теперь наша транзакция для чтения имеет уровень изоляции SNAPSHOT TABLE STABILITY, что не позволяет другим транзакциям изменять используемые нашей транзакцией таблицы. Запустите транзакцию, переоткройте набор данных и попытайтесь изменить какую-нибудь строку. Вы тут же получите конфликт блокировки.

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

isc_tpb_read
isc_tpb_nowait
isc_tpb_read_committed
isc_tpb_rec_version

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

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

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

Преимущества использования компонентов FIBPlus

Мы потратили много сил и времени, чтобы получить неприятности в виде блокировок в программах, одновременно работающих с одной и той же базой данных. Теперь настала пора воспользоваться всеми преимуществами компонентов FIBPlus для создания «правильных» программ, уменьшающих вероятность блокировок.

Правильное использование разделенных транзакций

Запустите на выполнение проект Transaction2. Установите следующие характеристики транзакции чтения (Transaction1):

isc_tpb_read
isc_tpb_read_committed
isc_tpb_nowait
isc_tpb_rec_version

Это длинная транзакция только для чтения с уровнем изоляции READ COMMITTED. Одно из достоинств такой транзакции — минимальное использование ресурсов сервера.

Транзакция обновления (Transaction2) является короткой транзакцией. Она запускается при вызове метода Post, отправляющего изменения на сервер, и автоматически подтверждается (при отсутствии ошибок обновления) после завершения выполнения метода Post. Момент запуска и подтверждения транзакции можно уловить, написав обработчики событий у компонента транзакции Transaction2: AfterStart или BeforeStart для фиксации старта и AfterEnd (BeforeEnd) для завершения транзакции. В нашей программе при этих событиях выдаются соответствующие сообщения (вы можете убрать комментарии для реальной выдачи сообщений).

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

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

isc_tpb_write
isc_tpb_read_committed
isc_tpb_wait
isc_tpb_no_rec_version

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

Подобную ситуацию мы проверили на тех же одиннадцати компьютерах в сети. Конфликта мы не получили.

Хорошо это или плохо, зависит от конкретной задачи обработки данных.

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

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

Использование защищенного режима

Вернемся к первому проекту. В наборе данных DataSetCountry в свойстве Option установим в True значение подсвойства poProtectedEdit. Это позволяет задавать блокировку одной записи, для которой начинается операция изменения.

Запустим на выполнение два экземпляра программы. Установим в обоих экземплярах для транзакции уровень изоляции READ COMMITTED. Начнем изменять запись в одной программе, не подтверждая транзакцию. Затем начнем изменять ту же запись во второй программе.

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

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

Замечание. Следует отметить, что количество защищаемых таким образом записей не ограничено — как только вы приступаете к изменению новой записи в рамках одной транзакции, эта запись тут же становится защищенной. Не существует никаких средств снятия блокировок записей, кроме как подтвердив или отменив транзакцию.

Прочитано 7534 раз

Авторизация



Счетчики