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