Транзакция вообще – это минимальная логически осмысленная операция, которая имеет смысл и может быть совершена только полностью.
Транзакция в информатике – это группа логически объединенных последовательных операций по работе с данными, обрабатываемая или отменяемая целиком. То есть все изменения данных, вносимые транзакцией, должны быть либо зафиксированы (COMMIT), либо отменены (ROLLBACK).
Свойства транзакции:
- все или ничего,
- должна или пройти, или не пройти полностью,
- действия других пользователей не должны повлиять на мои результаты.
Когда речь идет об однопользовательском режиме, транзакция нужна для защиты от аппаратных сбоев. Но когда требуется обеспечить работу нескольких пользователей (прохождение нескольких транзакций) одновременно, появляются новые задачи.
Например, в транзакции 1 выполняется следующий набор действий (проведение документа):
В транзакции 2 выполняется точно такой же набор действий (проведение документа, для определенности будем считать, что такого же и с такими же данными), но начинается она, например, когда первая транзакция дошла до шага 3.
В этом случае у системы должно быть некое правило, в соответствии с которым она определит дальнейший ход событий для транзакции 2. Система может, например:
Уже из изложенного видно, что даже если разрешить транзакции 2 только чтение, то данные этого чтения могут оказаться неточными, потому что транзакция 1 может быть отменена. Но параллельность работы при этом выше. И наоборот, только последовательный доступ к данным повышает точность и согласованность данных, но количество параллельно выполняемых транзакций может снижаться.
К несогласованностям данных, которые следует принимать во внимание, относятся следующие:
То, насколько транзакции изолированы друг от друга (обратно – то, насколько в них допускаются несогласованные данные), называется уровнем изоляции транзакций. Уровни изоляции описаны с точки зрения того, какие из побочных эффектов параллелизма разрешены.
Определяют следующие уровни изоляции (в порядке ее повышения):
В таблице 3.6.1 показаны побочные эффекты параллелизма, допускаемые различными уровнями изоляции.
Таблица 3.6.1. Побочные эффекты параллелизма, допускаемые различными уровнями изоляции
| Уровень изоляции | «Грязное» чтение | Неповторяющееся чтение | Фантомное чтение | 
|---|---|---|---|
| Read Uncommitted | Да | Да | Да | 
| Read Committed Snapshot | Нет | Да | Да | 
| Read Committed | Нет | Да | Да | 
| Repeatable Read | Нет | Нет | Да | 
| Serializable | Нет | Нет | Нет | 
Уровень изоляции теоретически может устанавливаться как для всей СУБД, так и для одной транзакции и даже с определенными ограничениями (ограничений мало) изменяться в течение транзакции.
Однако при работе с информационными системами на платформе «1С:Предприятие» следует всегда понимать, что в случае, когда используются транзакции, уровень изоляции регулируется только изменением режима управления блокировкой данных. Эти уровни устанавливаются средствами платформы «1С:Предприятие» в свойствах конфигурации для всей базы. Также для отдельной транзакции уровень можно установить в качестве параметра процедуры глобального контекста НачатьТранзакцию() – такая установка параметра имеет смысл, если для свойства конфигурации «Режим управления блокировкой данных» выбрано значение «Автоматический и Управляемый». Какие уровни для каких СУБД используются, показано в таб. 3.6.2.
Таблица 3.6.2. Блокировки СУБД, используемые в транзакции в зависимости от режима управления блокировкой данных, версии платформы и от СУБД
| Вид блокировки | Уровень изоляции транзакций | ||
|---|---|---|---|
| Для 8.2 | Для 8.3 | ||
| Автоматические блокировки | |||
| Файловая БД | Таблиц | Serializable | Serializable | 
| MS SQL Server | Записей | Repetable Read или Serializable* | Repetable Read или Serializable* | 
| IBM DB2 | Записей | Repetable Read или Serializable* | Repetable Read или Serializable* | 
| PostgreSQL | Таблиц | Serializable | Serializable | 
| Oracle Database | Таблиц | Serializable | Serializable | 
| Управляемые блокировки | |||
| Файловая БД | Таблиц | Serializable | Serializable | 
| MS SQL Server 2000 | Записей | Read Committed | Read Committed | 
| MS SQL Server 2005 и выше | Записей | Read Committed | Read Committed Snapshot | 
| IBM DB2 | Записей | Read Committed | Read Committed | 
| PostgreSQL | Записей | Read Committed | Read Committed | 
| Oracle Database | Записей | Read Committed | Read Committed | 
* Repeatable Read используется для объектных сущностей (все, у чего есть поле Ссылка – справочники, документы и т. п.), Serializable – для необъектных. Платформа исходит из того, что объекты будут искаться по уникальной ссылке, поэтому проблема фантомного чтения для объектов неактуальна (платформа не позволит добавить объект с такой же ссылкой).
Любая операция чтения данных, выполняемая вне транзакции, считается безответственной. Поведение системы при безответственном чтении (например, при выполнении отчетов) отличается в различных режимах блокировок. Отличия безответственного чтения в разных режимах работы управления блокировками (автоматическом или управляемом) для платформы 8.3 приведены в таблице 3.6.3.
Таблица 3.6.3. Отличия безответственного чтения в разных режимах работы блокировок для 8.3 (при установленном режиме совместимости конфигурации с 8.3 и выше)
| Чтение вне транзакции | |
|---|---|
| Автоматические блокировки | |
| Файловая БД | «Грязное» чтение | 
| MS SQL Server | «Грязное» чтение | 
| IBM DB2 | «Грязное» чтение | 
| PostgreSQL | Согласованное чтение | 
| Oracle Database | Согласованное чтение | 
| Управляемые блокировки | |
| Файловая БД | «Грязное» чтение | 
| MS SQL Server 2000 | «Грязное» чтение | 
| MS SQL Server 2005 и выше | Согласованное чтение | 
| IBM DB2 | «Грязное» чтение | 
| PostgreSQL | Согласованное чтение | 
| Oracle Database | Согласованное чтение | 
Если для защиты изменений данных транзакция устанавливает блокировку, то это всегда исключительная (монопольная) блокировка Х, и эта блокировка держится до тех пор, пока транзакция не завершится, независимо от уровня изоляции, установленного для транзакции. Уровень изоляции транзакции влияет на то, ставится вообще блокировка или нет, и на что она ставится. Например, на уровне Serializable появляются блокировки Range.
Если транзакция устанавливает блокировку для защиты чтения данных, то, когда речь идет об обычном чтении в транзакции, ставится разделяемая (совместимая) блокировка S, а когда о чтении ДЛЯ ИЗМЕНЕНИЯ – блокировка обновления U. Детальнее о совместимости блокировок говорится в следующей главе. Уровень изоляции транзакции влияет не только на то, ставится ли вообще блокировка и на что она ставится, но и на то, когда она снимается.
Снятие блокировок (на уровне СУБД) происходит так:
Сводные данные по всем режимам, доступным в «1С:Предприятии», приведены в таблице 3.6.4 (недоступные режимы не приводятся).
Таблица 3.6.4. Сводная таблица по всем режимам, доступным в «1С:Предприятии»
| Транзакция | Уровень изоляции | Действие | Блокировка | «Грязное» чтение | Неповт. чтение | Чтение фантомов | 
|---|---|---|---|---|---|---|
| Вне транзакции | READ UNCOMMITTED | чтение | нет | + | + | + | 
| Вне транзакции* | READ COMMITTED SNAPSHOT* | чтение | нет | - | + | + | 
| Управляемая транзакция | READ COMMITTED SNAPSHOT* | чтение | нет | - | + | + | 
| запись | X | - | + | + | ||
| READ COMMITTED | чтение | S (запрос) | - | + | + | |
| запись | X | |||||
| Автоматическая транзакция | REPEATABLE READ | чтение объектов | S | - | - | + | 
| чтение объектов"ДЛЯ ИЗМЕНЕНИЯ" | U | |||||
| запись объектов | X | |||||
| SERIALIZABLE | чтение регистров | S, RangeS | - | - | - | |
| чтение регистров "ДЛЯ ИЗМЕНЕНИЯ" | U, RangeU | |||||
| запись регистров | X, RangeX | 
* Только начиная с 8.3 с режимом совместимости с 8.3 и выше
Транзакции могут быть явными и неявными.
Явные транзакции начинаются средствами языка «1С» процедурой глобального контекста НачатьТранзакцию(), завершаются процедурой глобального контекста ЗафиксироватьТранзакцию(), отменяются процедурой ОтменитьТранзакцию().
Неявные транзакции начинаются, завершаются и отменяются средствами платформы.
Любые операции, изменяющие данные (например, проведение и отмена проведения документов), выполняются только в транзакции.
События прикладных объектов конфигурации, обработчики которых выполняются в неявной транзакции, приведены в таблице 3.6.5. Речь идет и об обработчиках, которые вызываются через модули этих прикладных объектов, и об обработчиках, которые вызываются через подписки на события.
Таблица 3.6.5. Обработчики событий прикладных объектов, выполняемые в транзакции
| Источник | Событие | Выполняется в транзакции | 
|---|---|---|
| БизнесПроцессОбъект.<Имя бизнес-процесса> | ПередЗаписью | Да | 
| ВнешнийИсточникДанныхТаблицаНаборЗаписей.<Имя внешнего источника>. <Имя таблицы внешнего источника данных> | ||
| ВнешнийИсточникДанныхТаблицаОбъект.<Имя внешнего источника>. <Имя таблицы внешнего источника данных> | ||
| ДокументОбъект.<Имя документа> | ||
| ЗадачаОбъект.<Имя задачи> | ||
| КонстантаМенеджерЗначения.<Имя константы> | ||
| ПерерасчетНаборЗаписей.<Имя перерасчета> | ||
| ПланВидовРасчетаОбъект.<Имя плана видов расчета> | ||
| ПланВидовХарактеристикОбъект.<Имя плана видов характеристик> | ||
| ПланОбменаОбъект.<Имя плана обмена> | ||
| ПланСчетовОбъект.<Имя плана счетов> | ||
| ПоследовательностьНаборЗаписей.<Имя последовательности> | ||
| РегистрБухгалтерииНаборЗаписей.<Имя регистра бухгалтерии> | ||
| РегистрНакопленияНаборЗаписей.<Имя регистра накопления> | ||
| РегистрРасчетаНаборЗаписей.<Имя регистра расчета> | ||
| РегистрСведенийНаборЗаписей.<Имя регистра сведений> | ||
| СправочникОбъект.<Имя справочника> | ||
| БизнесПроцессОбъект.<Имя бизнес-процесса> | ПриЗаписи | Да | 
| ВнешнийИсточникДанныхТаблицаНаборЗаписей.<Имя внешнего источника>. <Имя таблицы внешнего источника данных> | ||
| ВнешнийИсточникДанныхТаблицаОбъект.<Имя внешнего источника>. <Имя таблицы внешнего источника данных> | ||
| ДокументОбъект.<Имя документа> | ||
| ЗадачаОбъект.<Имя задачи> | ||
| КонстантаМенеджерЗначения.<Имя константы> | ||
| ПерерасчетНаборЗаписей.<Имя перерасчета> | ||
| ПланВидовРасчетаОбъект.<Имя плана видов расчета> | ||
| ПланВидовХарактеристикОбъект.<Имя плана видов характеристик> | ||
| ПланОбменаОбъект.<Имя плана обмена> | ||
| ПланСчетовОбъект.<Имя плана счетов> | ||
| ПоследовательностьНаборЗаписей.<Имя последовательности> | ||
| РегистрБухгалтерииНаборЗаписей.<Имя регистра бухгалтерии> | ||
| РегистрНакопленияНаборЗаписей.<Имя регистра накопления> | ||
| РегистрРасчетаНаборЗаписей.<Имя регистра расчета> | ||
| РегистрСведенийНаборЗаписей.<Имя регистра сведений> | ||
| СправочникОбъект.<Имя справочника> | ||
| ЗадачаОбъект.<Имя задачи> | ПередВыполнением | Да | 
| ЗадачаОбъект.<Имя задачи> | ПриВыполнении | Да | 
| ДокументОбъект.<Имя документа> | ОбработкаПроведения | Да | 
| ДокументОбъект.<Имя документа> | ПередУдалением | Да | 
| ДокументОбъект.<Имя документа> | ОбработкаУдаленияПроведения | Да | 
События расширений управляемых форм, обработчики которых выполняются в неявной транзакции, приведены в таблице 3.6.6.
Таблица 3.6.6. Обработчики событий расширений управляемых форм, выполняемые в транзакции
| Источник | Событие | Выполняется в транзакции | 
|---|---|---|
| Расширение управляемой формы для бизнес-процесса | ПриЗаписиНаСервере | Да | 
| Расширение управляемой формы для документа | ||
| Расширение управляемой формы для задачи | ||
| Расширение управляемой формы для записи регистра сведений | ||
| Расширение управляемой формы для записи таблицы внешнего источника данных | ||
| Расширение управляемой формы для констант | ||
| Расширение управляемой формы для набора записей | ||
| Расширение управляемой формы для объекта таблицы внешнего источника данных | ||
| Расширение управляемой формы для объектов | ||
| Расширение управляемой формы для плана видов характеристик | ||
| Расширение управляемой формы для справочника | 
События расширений обычных форм, обработчики которых выполняются в неявной транзакции, приведены в таблице 3.6.7.
Таблица 3.6.7. Обработчики событий расширений обычных форм, выполняемые в транзакции
| Источник | Событие | Выполняется в транзакции | 
|---|---|---|
| Расширение формы вида расчета | ПриЗаписи | Да | 
| Расширение формы документа | ||
| Расширение формы задачи | ||
| Расширение формы записи регистра сведений | ||
| Расширение формы констант | ||
| Расширение формы набора записей регистра бухгалтерии | ||
| Расширение формы набора записей регистра накопления | ||
| Расширение формы набора записей регистра расчета | ||
| Расширение формы набора записей регистра сведений | ||
| Расширение формы объекта бизнес-процесс | ||
| Расширение формы узла | ||
| Расширение формы элемента вида характеристик | ||
| Расширение формы элемента плана счетов | ||
| Расширение формы элемента справочника | 
События расширений управляемых форм, обработчики которых выполняются вне неявной транзакции, приведены в таблице 3.6.8.
Таблица 3.6.8. Обработчики событий расширений управляемых форм, выполняемые вне транзакции
| Источник | Событие | Выполняется в транзакции | 
|---|---|---|
| Расширение управляемой формы для бизнес-процесса | ПередЗаписьюНаСервере | Нет | 
| Расширение управляемой формы для документа | ||
| Расширение управляемой формы для задачи | ||
| Расширение управляемой формы для записи регистра сведений | ||
| Расширение управляемой формы для записи таблицы внешнего источника данных | ||
| Расширение управляемой формы для констант | ||
| Расширение управляемой формы для набора записей | ||
| Расширение управляемой формы для объекта таблицы внешнего источника данных | ||
| Расширение управляемой формы для объектов | ||
| Расширение управляемой формы для плана видов характеристик | ||
| Расширение управляемой формы для справочника | ||
| Расширение управляемой формы для бизнес-процесса | ПослеЗаписиНаСервере | Нет | 
| Расширение управляемой формы для документа | ||
| Расширение управляемой формы для задачи | ||
| Расширение управляемой формы для записи регистра сведений | ||
| Расширение управляемой формы для записи таблицы внешнего источника данных | ||
| Расширение управляемой формы для констант | ||
| Расширение управляемой формы для набора записей | ||
| Расширение управляемой формы для объекта таблицы внешнего источника данных | ||
| Расширение управляемой формы для объектов | ||
| Расширение управляемой формы для плана видов характеристик | ||
| Расширение управляемой формы для справочника | ||
| Расширение управляемой формы для бизнес-процесса | ПослеЗаписи | Нет | 
| Расширение управляемой формы для документа | ||
| Расширение управляемой формы для задачи | ||
| Расширение управляемой формы для записи регистра сведений | ||
| Расширение управляемой формы для записи таблицы внешнего источника данных | ||
| Расширение управляемой формы для констант | ||
| Расширение управляемой формы для набора записей | ||
| Расширение управляемой формы для объекта таблицы внешнего источника данных | ||
| Расширение управляемой формы для объектов | ||
| Расширение управляемой формы для плана видов характеристик | ||
| Расширение управляемой формы для справочника | 
События расширений обычных форм, обработчики которых выполняются вне неявной транзакции, приведены в таблице 3.6.9.
Таблица 3.6.9. Обработчики событий расширений обычных форм, выполняемые вне транзакции
| Источник | Событие | Выполняется в транзакции | 
|---|---|---|
| Расширение формы вида расчета | ПослеЗаписи | Нет | 
| Расширение формы документа | ||
| Расширение формы задачи | ||
| Расширение формы записи регистра сведений | ||
| Расширение формы констант | ||
| Расширение формы набора записей регистра бухгалтерии | ||
| Расширение формы набора записей регистра накопления | ||
| Расширение формы набора записей регистра расчета | ||
| Расширение формы набора записей регистра сведений | ||
| Расширение формы объекта бизнес-процесс | ||
| Расширение формы узла | ||
| Расширение формы элемента вида характеристик | ||
| Расширение формы элемента плана счетов | ||
| Расширение формы элемента справочника | 
Прочие события объектов, их форм, обычных и управляемых, и расширений этих форм выполняются вне неявных транзакций.
Чтобы удостовериться, выполняется обработчик события в транзакции или нет, нужно открыть его в синтакс-помощнике, и если это так, в блоке «Описание» будет написано «возникает в транзакции», «вызывается... до окончания транзакции» и т. п.
Наличие параметра Отказ в параметрах обработчика событий не служит признаком, того, что обработчик выполняется в транзакции: например, обработчик события ОбработкаПроверкиЗаполнения() содержит параметр Отказ, но выполняется вне неявной транзакции.
Операции чтения могут выполняться в неявной транзакции. Это происходит, когда требуется обеспечить чтение согласованного набора данных. На текущий момент нам известно о двух случаях, когда это происходит:
Обращаем внимание на то, что раз разговор идет о транзакционном чтении, то в этих случаях могут возникать (и возникают) конфликты блокировок в совершенно неожиданных местах: при работе с формами, в отчетах и, казалось бы, в безобидных обработках. Ситуация с отчетами ухудшается тем, что на выбор – выполняется чтение в транзакции или нет – влияет не код конфигурации, а пользовательские настройки. А чтение в объектной технике – это настолько важный вопрос, что ему посвящен отдельный раздел 3.15 «Особенности чтения в объектной модели».
Узнать, выполняется чтение с транзакции или нет, можно в профайлере SQL Server. Настроив трассировку, например, в соответствии с разделом 4.12 «Работа в профайлере. Как получить план запроса», нужно с помощью отладчика, выполнить подозрительную строку. Если транзакция имеет место, в трассировке появятся BEGIN TRANSACTION и COMMIT TRANSACTION.
Транзакции, как явные, так и неявные, могут быть вложенными. Обычный случай, например, запись элемента одного справочника из процедуры ПриЗаписи модуля другого справочника или из подписок на события, обрабатываемых внутри транзакции. Применение такого подхода при всем его удобстве, однако, может создавать проблемы производительности, поэтому пользоваться им надо аккуратно, а злоупотреблять, создавая каскады записывающихся друг из друга объектов, не стоит вообще.
Если внутри транзакции произошла исключительная ситуация, она откатывается. Если внутри транзакции были вложенные транзакции или она сама являлась вложенной, откатываются все транзакции, независимо от того, на каком из уровней вложенности это произошло.
Проще говоря, никто не запрещает начинать одну транзакцию внутри другой, но ведет себя все это почти всегда как одна транзакция самого верхнего уровня, т. е. вложенные транзакции игнорируются. Нюансы поведения, связанные с отработкой некоторых ошибок в коде, приведены ниже.
Если исключительную ситуацию отработать с помощью скобок Попытка... Исключение... КонецПопытки, расположенных внутри транзакции, то транзакция не завершится в момент возникновения исключительной ситуации, она дойдет до следующего обращения к данным – чтения или записи, продолжая при этом блокировать ресурсы, и откатится с сообщением: «В данной транзакции уже происходили ошибки!».
Если таких ошибок много и они начинают создавать проблемы пользователям, то без модификации кода, удаления этих скобок очень трудно найти, где они произошли и почему.
Описанное поведение системы позволяет говорить о том, что при возникновении ситуации, когда внутри транзакций нужно использовать скобки Попытка... Исключение... КонецПопытки, необходимо, отработав исключительную ситуацию, вызывать оператор ВызватьИсключение.
Случается, однако, что транзакция, встретившись с ошибкой, находящейся в скобках Попытка... Исключение... КонецПопытки, продолжает работу. Такое различие в поведении будет зависеть от типа исключительной ситуации. Исключительные ситуации бывают восстановимыми (после которых можно продолжить работу и завершить транзакцию) и невосстановимыми (после которых нельзя продолжить работу и завершить транзакцию, например, ошибка базы данных). Внешне это может выглядеть как зависимость от того, как код, вызвавший исключение, и скобки Попытка... Исключение... КонецПопытки расположены относительно ближайшей к ним транзакции.
Пример 1
НачатьТранзакцию();
Попытка
ЭтотОбъект.ВыполнитьНесуществующийМетод();
// (такого метода у объекта не создавали)
Исключение
КонецПопытки;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ПлатежноеПоручение.Ссылка
|ИЗ
| Документ.ПлатежноеПоручение КАК ПлатежноеПоручение";
Результат = Запрос.Выполнить();
СпрСсылка = Справочники.Организации.НайтиПоКоду("000001");
СпрОбъект = СпрСсылка.ПолучитьОбъект();
СпрОбъект.НаименованиеПолное = ТекущаяДата();
// это чтобы отследить, зафиксирована транзакция или нет
СпрОбъект.Записать();
ЗафиксироватьТранзакцию();
Код выполнится. Транзакция успешно завершится. Исключительная ситуация оказалась расценена как восстановимая.
Пример 2
Сначала в процедуру ПриЗаписи модуля справочника Организации добавим строку:
ЭтотОбъект.ВыполнитьНесуществующийМетод();
// (такого метода у объекта не создавали)
Далее изменим код из примера 1:
НачатьТранзакцию();
Попытка
СпрСсылка = Справочники.Организации.НайтиПоКоду("000001");
СпрОбъект = СпрСсылка.ПолучитьОбъект();
СпрОбъект.НаименованиеПолное = ТекущаяДата();
//это чтобы отследить, зафиксирована транзакция или нет
СпрОбъект.Записать(); // неявная транзакция, там, как помним, ошибка
Исключение
КонецПопытки;
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| ПлатежноеПоручение.Ссылка
|ИЗ
| Документ.ПлатежноеПоручение КАК ПлатежноеПоручение";
Результат = Запрос.Выполнить();
ЗафиксироватьТранзакцию();
Получим ошибку:
Ошибка при вызове метода контекста (Выполнить)
Результат = Запрос.Выполнить();
По причине: Ошибка выполнения запроса.
По причине: В данной транзакции уже происходили ошибки!
Исключительная ситуация, хотя она вызвана аналогичной строкой кода, потянула за собой дополнительные последствия (откат транзакции записи элемента справочника) и оказалась расценена как невосстановимая.