Но сделать базу данных доступной веб приложению не так просто, как использовать тег <cfquery>. Множество уникальных пользователей могут просматривать веб приложение одновременно. Когда пользователи веб приложения создают конкурентные запросы к базе данных, то существует вероятность, что некоторые пользователи получат неправильные данные. Такое может произойти только если приложение не контролирует запросы в контексте текущей транзакции пользователя. В действительности, даже единственный пользователь приложения может создать или получить неправильные данные, если его взаимодействие с базой данных не исполняется в защищенном контейнере транзакции базы данных. Классический пример получения неправильных данных: приложение добавляет в базу данных новую запись, а затем считывает уникальный id (счетчик) только что добавленной записи, но вместо того, чтобы получить правильный id, приложение получает уникальный id добавленной записи в конкурентной транзакции. В этой статье мы рассмотрим:
Что такое транзакции?
Каждый раз, когда мы выполняем команды к базе данных, они исполняются в контексте транзакции. Каждая транзакция имеет ассоциированный с ней уровень изоляции. Уровень изоляции определяет уровень блокирования транзакции во время ее исполнения, а также определяет степень взаимодействия с другими конкурентными блокированными транзакциями. Существует четыре типа уровней изоляции:
Уровень изоляции | Описание |
Read Uncommitted | Никакие блокировки не устанавливаются, и данные считываются, несмотря на монопольную блокировку. Допускается чтение несогласованных данных, модифицируемых другой транзакцией («dirty read»). |
Read Committed | На каждую считываемую строку устанавливается разделяемая блокировка. После чтения строки блокировка снимается, если только за чтением строки не следует ее обновление. По-умолчанию, большинство RDBMS используют для своих транзакций этот уровень изоляции. |
Repeatable Read | На каждую считываемую строку устанавливается разделяемая блокировка, которая сохраняется (или даже заменяется монопольной блокировкой) до конца транзакции. Во время такой изоляции другая транзакция может добавить в таблицу(ы) новые ряды с данными. Поскольку данный тип изоляции использует высокий уровень блокировки, то его использование лучше избегать. |
Serializable | Самый надежный тип изоляции, но может вызвать перегрузку ресурсов базы данных. Устанавливается и сохраняется до конца транзакции блокировка диапазона ключевых значений. Доступ к таблицам базы данных осуществляется в один поток. Поскольку данный тип изоляции использует высокий уровень блокировки, то его использование лучше избегать. |
В дополнение к уровням изоляции транзакции могут осуществлять еще три действия:
Почему транзакции так важны?
Транзакции важны по двум причинам. Во-первых, транзакции позволяют приложениям исполнять несколько 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>. Следующий пример добавляет запись в таблицу Orders, считывает значения колонок CardType и Order_ID только что добавленной записи и отменяет транзакцию, если значение CardType не верно.
Классическим примером использования отмены и подтверждения может быть система онлайн банкинга, которая позволяет переводить деньги с одного счета на другой. Очевидным бизнес правилом здесь является то, что пользователь не может взять денег со счета больше, чем есть. Примерный код может быть таким:Советы, недостатки в транзакциях
Перед тем как все теги <cfquery> помещать внутри тегов <cftransaction>, следует учесть некоторые моменты. Как и со всеми тегами, каждый раз, когда ColdFusion исполняет тег <cftransaction>, сервер выполняет некоторую задачу. Иными словами, CFML теги являются инструкциями и каждый тег предполагает некоторую нагрузку. Поэтому не стоит повсюду использовать тег <cftransaction> - это замедлит исполнение страницы. Лучшей практикой будет использование транзакций, по умолчанию используемых базой данных, и только в особых случаях, когда следует управлять конкурентными запросами, можно использовать <cftransaction>.
Лучшим решением является хранение всей логики транзакций внутри хранимых процедур. В этом смысле, то, что можно делать средствами базы данных, следует переложить на базу данных, а не на ColdFusion. Хранение логики транзакций и SQL в одном месте позволяет легче управлять всем этим.Если вы используете Microsoft Access, то вам всегда придется использовать только тег <cftransaction>, т.к. Access не поддерживает хранимые процедуры.
Если приложение использует самый тяжелый уровень блокировки транзакций, то следует проводить предварительные тесты на отсутствие ошибок времени исполнения запросов. При необходимости можно увеличить допустимое время исполнения запросов.Если поместить в один тег <cftransaction> несколько запросов, использующих разные источники данных, то они не будут являться частью одной логической транзакции. В этом случае следует операции с каждым источником данных проводить как отдельные транзакции и контролировать правильное последовательное исполнение запросов.
© 2002-2005 г. Вадим Пушкарев