Разработка и Конфигурация · 10 мин
Транзакции в 1С: принцип работы и вложенные транзакции
Что такое транзакция в 1С, как работают НачатьТранзакцию и ЗафиксироватьТранзакцию, есть ли вложенные транзакции, типичные ошибки и рекомендации.
Транзакция — это набор операций с базой данных, который выполняется как единое целое: либо все изменения фиксируются, либо ни одно из них не применяется. В 1С транзакции используются постоянно, даже если разработчик не пишет их явно: запись документа, проведение, обмен данными — всё это платформа оборачивает в транзакции автоматически.
Понимание механизма транзакций критично для разработчика: от него зависит целостность данных, поведение при ошибках и даже производительность системы. Ошибка в середине длинной обработки без транзакции может оставить базу в противоречивом состоянии — например, шапка документа записана, а табличная часть нет.
Отдельный вопрос, который регулярно возникает у начинающих: поддерживает ли 1С вложенные транзакции, то есть транзакцию внутри другой транзакции. Ответ неочевиден и имеет важные практические следствия, которые разобраны ниже.
Что такое транзакция и зачем она нужна в 1С
Транзакция опирается на классический принцип ACID: атомарность, согласованность, изолированность и долговечность. Для прикладного разработчика 1С важнее всего атомарность: если в процессе записи нескольких объектов произошла ошибка, база должна вернуться в исходное состояние, а не остаться «наполовину записанной».
Типовой пример — перенос остатков или групповое создание документов. Если обработка создаёт пятьсот документов и на трёхсотом падает с ошибкой, без транзакции в базе останутся 299 документов неизвестного качества. С транзакцией либо появятся все пятьсот, либо ни одного, и операцию можно повторить после исправления данных.
Платформа сама открывает транзакции при проведении документов, записи объектов с установленными флагами, обмене данными. Но в собственных обработках и процедурах разработчик управляет транзакциями вручную через встроенные методы языка.
Как управлять транзакциями вручную: основные методы
Во встроенном языке 1С есть три ключевых метода: НачатьТранзакцию(), ЗафиксироватьТранзакцию() и ОтменитьТранзакцию(). Дополнительно существует функция ТранзакцияАктивна(), которая позволяет проверить, находится ли код внутри открытой транзакции.
Стандартный шаблон ручной транзакции выглядит так: транзакция открывается, выполняются операции записи, при возникновении исключения в блоке Попытка...Исключение вызывается отмена, при успешном завершении — фиксация. Важно, чтобы каждый путь выполнения кода заканчивался либо фиксацией, либо откатом.
НачатьТранзакцию();
Попытка
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
РегистрНаборЗаписей.Записать();
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение ОписаниеОшибки();
КонецПопытки;
Если исключение возникает внутри транзакции и не обрабатывается, платформа при выходе из контекста отменит транзакцию автоматически. Но полагаться на это не стоит: явный вызов ОтменитьТранзакцию() делает код предсказуемым и читаемым.
Если Не ТранзакцияАктивна() Тогда
НачатьТранзакцию();
КонецЕсли;Есть ли в 1С вложенные транзакции
Формально вызвать НачатьТранзакцию() внутри уже открытой транзакции можно — ошибки не будет. Но настоящей вложенной транзакции при этом не создаётся. Платформа использует модель счётчика: каждый вызов НачатьТранзакцию() увеличивает счётчик на единицу, каждый вызов ЗафиксироватьТранзакцию() уменьшает его, а фактическая фиксация в СУБД происходит только когда счётчик достигает нуля.
Ключевое следствие: вызов ОтменитьТранзакцию() на любом уровне вложенности отменяет всю внешнюю транзакцию целиком. «Откатить только внутреннюю часть», как это позволяют точки сохранения (savepoint) в чистом SQL, в 1С штатными средствами нельзя.
Это частая причина трудноуловимых ошибок. Процедура, которая открывает и отменяет «свою» транзакцию, будучи вызванной внутри чужой транзакции, фактически откатит работу вызывающего кода. Поэтому библиотечные процедуры должны либо документировать требование «вызывать вне транзакции», либо проверять ТранзакцияАктивна() перед открытием.
Как платформа считает уровни
Если код открыл транзакцию трижды (счётчик равен 3), то для реальной фиксации нужно три вызова ЗафиксироватьТранзакцию(). Если после этого вызвать фиксацию в четвёртый раз, платформа выдаст ошибку, потому что активной транзакции уже нет. Симметрично, фиксация при нулевом счётчике и отмена без активной транзакции приводят к исключениям.
Чем заменить точки сохранения
Если нужна логика «откатить часть работы», используют обходные приёмы: разбиение обработки на независимые транзакции по порциям данных, предварительную проверку всех данных до начала записи, либо запись во временные структуры с последующей единой фиксацией. Пакетная обработка с транзакцией на каждую порцию — самый устойчивый вариант для больших объёмов.
Транзакции при записи и проведении документов
При проведении документа платформа автоматически открывает транзакцию: записывается сам документ, движения по всем регистрам, связанные изменения. Если в обработчике проведения возникает исключение, вся эта работа откатывается — документ остаётся непроведённым, а регистры нетронутыми.
Разработчику в обработчике проведения не нужно открывать свою транзакцию — она уже есть. Дополнительный вызов НачатьТранзакцию() только увеличит счётчик и не даст никакой дополнительной защиты. А вот вызов ОтменитьТранзакцию() в обработчике проведения — грубая ошибка: он отменит транзакцию платформы, и поведение станет непредсказуемым.
Управлять результатом в обработчиках следует через отказ: установка параметра Отказ = Истина или генерация исключения. Это штатный способ сообщить платформе, что операцию нужно прервать с откатом.
Блокировки и длительные транзакции
Пока транзакция открыта, СУБД удерживает блокировки на изменённые данные. В многопользовательской базе длительная транзакция — прямой путь к конфликтам: другие сеансы ждут освобождения записей, растут очереди ожидания, появляются ошибки превышения времени ожидания блокировки и взаимоблокировки.
Практические правила: транзакция должна быть максимально короткой; внутри неё нельзя выполнять интерактивные действия (вопросы пользователю, длительные расчёты, обращения к внешним ресурсам); тяжёлые обработки следует делить на порции с отдельной транзакцией на порцию.
В клиент-серверном варианте 1С использует управляемые блокировки, которые срабатывают при фактическом изменении данных. Это смягчает проблему, но не отменяет правила: чем меньше данных захвачено и чем быстрее транзакция завершается, тем стабильнее работает система под нагрузкой.
Практический порядок написания устойчивого кода
Перед тем как оборачивать операции в транзакцию, определите границы атомарности: какой минимальный набор изменений обязан быть единым. Затем проверьте, не выполняется ли код уже внутри транзакции платформы (обработчики записи, проведения) — там своя транзакция не нужна.
После внедрения кода проверьте поведение в трёх сценариях: штатное выполнение, ошибка в середине операций, повторный запуск после отката. Признак правильной реализации — после ошибки база полностью возвращается в исходное состояние, и операцию можно безопасно повторить.
- 1Определите минимальный набор операций, который должен быть атомарным
- 2Проверьте через ТранзакцияАктивна(), не открыта ли транзакция вызывающим кодом или платформой
- 3Откройте транзакцию и оберните операции в Попытка...Исключение
- 4В ветке Исключение вызовите ОтменитьТранзакцию() и пробросьте или запротоколируйте ошибку
- 5В штатной ветке вызовите ЗафиксироватьТранзакцию()
- 6Протестируйте откат: убедитесь, что после ошибки данные не изменились и повторный запуск возможен
Как проверить результат
Ни один объект не изменён, повторный запуск проходит без дублей
Часть объектов записана, появляются дубли или «битые» данные
Число вызовов НачатьТранзакцию равно числу ЗафиксироватьТранзакцию на каждом пути кода
Ошибки «транзакция не активна» или зависшие блокировки
Другие пользователи не получают ошибок ожидания блокировок
Массовые ошибки превышения времени ожидания во время обработки
Частые вопросы
Можно ли в 1С откатить только часть транзакции?
Штатно нет: вложенных транзакций и точек сохранения в модели 1С нет, откат всегда полный. Обходные варианты — деление работы на несколько независимых транзакций или предварительная проверка данных до начала записи.
Что произойдёт, если вызвать НачатьТранзакцию() дважды подряд?
Ошибки не будет: платформа просто увеличит внутренний счётчик до двух. Реальная фиксация в СУБД произойдёт только после двух вызовов ЗафиксироватьТранзакцию(). Но один вызов ОтменитьТранзакцию() отменит всё.
Нужно ли открывать транзакцию в обработчике проведения документа?
Нет. Платформа уже открыла транзакцию на всё проведение. Дополнительное открытие бесполезно, а вызов ОтменитьТранзакцию() в обработчике — опасная ошибка. Для прерывания используйте Отказ = Истина или исключение.
Откатит ли платформа транзакцию при необработанном исключении?
Да, при аварийном завершении контекста открытая транзакция отменяется автоматически. Однако явный вызов ОтменитьТранзакцию() в блоке Исключение делает код понятнее и защищает от изменений поведения при рефакторинге.
Как обработать миллион записей, не уронив производительность?
Разбейте работу на порции и открывайте отдельную транзакцию на каждую порцию. Так блокировки удерживаются недолго, а при сбое откатывается только текущая порция — обработку можно продолжить с места остановки.
Что важно запомнить
Транзакция в 1С гарантирует атомарность: либо все изменения, либо ни одного. Вложенных транзакций нет — есть счётчик вызовов, и откат на любом уровне отменяет всё. Держите транзакции короткими, не открывайте их в обработчиках проведения и всегда закрывайте на каждом пути выполнения кода.