InterBase: тормозология и глюконавтика
Страница 28. Транзакционные хитрости


 

Транзакционные хитрости

Как известно, в Delphi, если не определять явную транзакцию, то все заботы возьмут на себя компоненты и InterBase. Это на словах. На деле происходит следующее (подробности работы с несколькими Database и Session опускаю):
  • Открытие датасетов делается в рамках текущей транзакции. Если её ещё нет, то она запускается.
  • Попытка что-либо удалить или запостить в датасете приводит к операции commit.
  • Попытка выполнить ExecSQL для запроса или ExecProc для хранимой процедуры, а так же любой Refresh опять же приводит к commit.

В InterBase API существует две разновидности операции: просто commit и commit retaining. Обычный commit приводит к тому, что все запросы, открытые в рамках транзакции закрываются. BDE, чтобы не смущать приложения, сразу же начинает новые транзакции, переоткрывает запросы, а затем во многих случаях вычитывает содержимое запроса до конца. В свете того, что было сказано выше о прокрутке, понятно, что это приведёт к повторному прочитыванию данных от начала запроса до текущей позиции. Но в данном случае BDE идёт дальше - прочитывает всё содержимое запроса.

 

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

Операция commit retaining, напротив, сохраняет все открытые запросы, и даже не портит позицию текущей записи. С одной стороны, это хорошо, так как не приводит к маниакальному чтению. С другой - BDE не так часто этим пользуется, по крайней мере, с настройками по умолчанию. Кроме того, при такой фиксации сохраняется часть блокировок на данных. Так что рано или поздно, если не делать полный commit, пользователи начнут натыкаться на непонятные сообщения об ошибках.

Ещё одно замечание: явные вызовы TDatabase.Commit дают всегда полный commit, независимо от прочих настроек, о чём непосредственно ниже.

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

  1. Ограничить в параметрах драйвера или алиаса максимальное количество записей с помощью параметра MaxRows. На самом деле это не решает проблему. Лишние записи всё равно вычитываются. Просто любое обращение будет обрублено после заданного количества записей. В частности, если у вас имеется большая таблица, которую вы позволяете пользователю листать, то в процессе листания он рано или поздно наткнётся на такое ограничение.
  2. Консультанты из Borland предлагают снабдить приложение кнопкой "показать следующие записи". Короче говоря, издеваются.

  3. В параметрах алиаса или драйвера поставить Flags=4096. Вообще-то "официальная" документация тщательно скрывает смысл битов этого числа и настоятельно рекомендует оставлять их все нулями. Тем не менее, насколько я могу судить, бит 12 (дающий 4096) заставляет драйвер использовать commit retaining ведзе, где возможно. Это радикальным образом соратит маниакальные обращения, но приведёт к тому, что пользователи начнут натыкаться на ссобщения о блокировках при попытке редактировать одну и ту же запись. Причём для снятия блокировок придётся делать полный commit, который при данных настройках возможен только "руками". Так пишет Борланд. На самом деле я не обнаружил каких-либо оснований для такого предупреждения, так что рекомендую всем работать в режими 4096.
  4. В параметрах поставить Flags=4608. Это - комбинация битов 12 и 9 (4096 + 512). В целом режим похож на предыдущий, но уровень изоляции транзакций принудительно устанавливается на read committed. Это снижает число блокировок, но чтобы гарантированно увидеть все свежие изменения в БД, программа должна сделать полный commit.
 
« Предыдущая статья