Управление транзакциями

Множество разработчиков выбирают ColdFusion для создания веб приложений, потому что он прост в использовании, а CFML снижет сложность написания кода. Это позволяет разработчикам уделять больше времени возможностям их приложений. Первое, что узнает ColdFusion разработчик, это то, что ColdFusion упрощает способ подключения к базе данных и получения из нее данных. Это осуществляется с помощью тега <cfquery>.

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

 

Что такое транзакции?

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

Уровень изоляцииОписание
Read Uncommitted Никакие блокировки не устанавливаются, и данные считываются, несмотря на монопольную блокировку. Допускается чтение несогласованных данных, модифицируемых другой транзакцией («dirty read»).
Read Committed На каждую считываемую строку устанавливается разделяемая блокировка. После чтения строки блокировка снимается, если только за чтением строки не следует ее обновление. По-умолчанию, большинство RDBMS используют для своих транзакций этот уровень изоляции.
Repeatable Read На каждую считываемую строку устанавливается разделяемая блокировка, которая сохраняется (или даже заменяется монопольной блокировкой) до конца транзакции. Во время такой изоляции другая транзакция может добавить в таблицу(ы) новые ряды с данными. Поскольку данный тип изоляции использует высокий уровень блокировки, то его использование лучше избегать.
Serializable Самый надежный тип изоляции, но может вызвать перегрузку ресурсов базы данных. Устанавливается и сохраняется до конца транзакции блокировка диапазона ключевых значений. Доступ к таблицам базы данных осуществляется в один поток. Поскольку данный тип изоляции использует высокий уровень блокировки, то его использование лучше избегать.

В дополнение к уровням изоляции транзакции могут осуществлять еще три действия:

  1. Begin – начать транзакцию
  2. Commit – передать или завершить команды
  3. Roll back – отменить все действия
Поскольку каждая транзакция должна начинаться, то действие begin присутствует всегда. Если транзакция выполняет roll back, то отменяются все действия, выполненные до этой команды. Произойдет так, как если бы вообще ничего не происходило. Если транзакция выполняет commit, то все SQL команды передаются или завершаются. После действия commit мы не можем отменить ранее выполненные команды.

 

Почему транзакции так важны?

Транзакции важны по двум причинам. Во-первых, транзакции позволяют приложениям исполнять несколько SQL выражений как одну логическую единицу. Приложению может понадобиться, чтобы результаты выражения SELECT не затронуты конкурентными транзакциями. Ранее уже был приведен пример, в котором приложением добавляет в базу данных новый ряд и считывает последнее значение id только что добавленных данных. Поместив выражения SELECT и INSERT внутри одной транзакции, мы даем команду базе данных, чтобы доступ к текущим ресурсам был заблокирован. Отсутствие блокировки данных таблицы может привести к неправильному считыванию id.

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

 

Кому следует использовать транзакции?

Каждый разработчик, который осуществляет манипуляции с базой данных, должен использовать транзакции. Несомненно, создавая запросы к Oracle, MS SQL Server или к любой другой RDBMS, разработчику следует знать все возможности данной базы данных. В особых случаях большую часть SQL запросов следует держать в хранимых процедурах.

Хранимые процедуры дают программисту базы данных полный контроль над транзакциями. Конечно, большинство ColdFusion программистов не пока еще не имеют того опыта, требуемого для создания хранимых процедур. Кроме того, многие разработчики используют Microsoft Access. Microsoft Access не поддерживает обработку транзакций и хранимых процедур через какой-либо интерфейс для ColdFusion разработчиков. Но не стоит бояться, можно легко справиться с обработкой транзакций прямо на ColdFusion страницах!

 

Как использовать транзакции в ColdFusion?

Любое количество тегов <cfquery> можно сгруппировать в теге <cftransaction>. Тег <cftransaction> содержит два необязательных атрибута:

Каждый атрибут и его значение напрямую относится к описанным выше механизмам транзакций. Чтобы просто объединить два запроса в одну логическую единицу, поместите их внутри одного тега <cftransaction>. Приведенный ниже пример использует источник данных cfsnippets, являющийся одним из примерных приложений в ColdFusion. Чтобы добавить нового сотрудника в таблицу Employees и получить уникальный id в пределах одной транзакции, используем следующий код:
Если один из тегов <cfquery> вызовет ошибку, то все операции в других тегах <cfquery> в пределах того же тега <cftransaction> будут отменены (roll back). Чтобы протестировать это, скопируйте приведенный код в файл и измените имя таблицы с Employees на Employees2 во втором запросе в выражении FROM. Запустите этот файл. В результате будет ошибка, т.к. таблицы с именем Employees2 не существует. Затем посмотрим в базу данных cfsnippets и увидим, что первый запрос не добавил никаких данных.

Чтобы отменить или прервать транзакцию в зависимости от того, соответствует ли транзакция бизнес правилам, добавим в транзакцию еще теги <cftransaction>. Следующий пример добавляет запись в таблицу Orders, считывает значения колонок CardType и Order_ID только что добавленной записи и отменяет транзакцию, если значение CardType не верно.

Классическим примером использования отмены и подтверждения может быть система онлайн банкинга, которая позволяет переводить деньги с одного счета на другой. Очевидным бизнес правилом здесь является то, что пользователь не может взять денег со счета больше, чем есть. Примерный код может быть таким:

 

Советы, недостатки в транзакциях

Перед тем как все теги <cfquery> помещать внутри тегов <cftransaction>, следует учесть некоторые моменты. Как и со всеми тегами, каждый раз, когда ColdFusion исполняет тег <cftransaction>, сервер выполняет некоторую задачу. Иными словами, CFML теги являются инструкциями и каждый тег предполагает некоторую нагрузку. Поэтому не стоит повсюду использовать тег <cftransaction> - это замедлит исполнение страницы. Лучшей практикой будет использование транзакций, по умолчанию используемых базой данных, и только в особых случаях, когда следует управлять конкурентными запросами, можно использовать <cftransaction>.

Лучшим решением является хранение всей логики транзакций внутри хранимых процедур. В этом смысле, то, что можно делать средствами базы данных, следует переложить на базу данных, а не на ColdFusion. Хранение логики транзакций и SQL в одном месте позволяет легче управлять всем этим.

Если вы используете Microsoft Access, то вам всегда придется использовать только тег <cftransaction>, т.к. Access не поддерживает хранимые процедуры.

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

Если поместить в один тег <cftransaction> несколько запросов, использующих разные источники данных, то они не будут являться частью одной логической транзакции. В этом случае следует операции с каждым источником данных проводить как отдельные транзакции и контролировать правильное последовательное исполнение запросов.


Источник: Controlling Database Transactions in ColdFusion MX

 


Hosted by uCoz