Управляемые блокировки в 1С. Механизм выглядит простым (объект БлокировкаДанных, три метода — добавить, установить значение, заблокировать), а на практике поведение отличается от ожидаемого настолько, что в типовых конфигурациях до сих пор находят ошибки, связанные с блокировками.
Два уровня блокировок: СУБД и 1С
В автоматическом режиме блокировок 1С полностью полагается на СУБД. SQL Server или PostgreSQL сами решают, какие блокировки ставить: shared (S) при чтении, exclusive (X) при записи, с возможной эскалацией до уровня таблицы. Уровень изоляции — Serializable (MS SQL) или Read Committed с дополнительными S-блокировками.
Проблема: СУБД ничего не знает о бизнес-логике 1С. Она видит таблицы и строки, а не «документы», «регистры» и «измерения». Когда два пользователя одновременно проводят расходные накладные с разными товарами, но по одному складу, СУБД может забло��ировать весь регистр остатков (эскалация), хотя конфликт — только по конкретным позициям номенклатуры.
Управляемый режим решает эту проблему. 1С понижает уровень изоляции на СУБД до Read Committed (MS SQL) или стандартного Read Committed (PostgreSQL) и берёт управление параллельностью на себя. Менеджер управляемых блокировок 1С понимает структуру данных — измерения регистров, реквизиты документов — и может блокировать точнее: не таблицу целиком, а конкретные записи по значениям измерений.
Архитектурно получается два слоя:
Сначала запрос проходит через менеджер управляемых блокировок 1С. Если конфликт обнаружен на этом уровне — СУБД вообще не получает запрос, и клиент получает ошибку «Конфликт блокировок».
Если на уровне 1С конфликта нет, запрос передаётся в СУБД, где работает стандартный механизм блокировок СУБД (уже на более мягком уровне изоляции).
Пространства блокировок
Основное понятие, без которого управляемые блокировки не понять. Пространство блокировок — это «что именно мы блокируем». Для каждого объекта метаданных 1С определяет одно или несколько пространств.
Для регистра накопления ОстаткиТоваровНаСкладах с измерениями Склад и Номенклатура существуют два пространства:
— РегистрНакопления.ОстаткиТоваровНаСкладах — блокировка по измерениям (Склад, Номенклатура) — РегистрНакопления.ОстаткиТоваровНаСкладах.НаборЗаписей — блокировка по Регистратору (ссылке на документ)
Эти два пространства не пересекаются. Блокировка по измерениям не конфликтует с блокировкой по регистратору.
Для справочников, документов и других ссылочных объектов пространство одно — по ссылке. Для регистров сведений — зависит от того, подчинён ли регистр регистратору: если подчинён — два пространства (измерения и регистратор), если независимый — одно (измерения + период).
Практика
Классический сценарий — расходная накладная списывает товар. Нужно проверить, что остатки не уйдут в минус. Без управляемых блокировок:
// ПЛОХО: автоматический режим Процедура ОбработкаПроведения(Отказ) // 1. Записываем движения Движения.ОстаткиТоваров.Записать(); // 2. Проверяем остатки Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ Номенклатура, КоличествоОстаток |ИЗ РегистрНакопления.ОстаткиТоваров.Остатки() |ГДЕ КоличествоОстаток < 0"; Если Запрос.Выполнить().НеПустой() Тогда Отказ = Истина; КонецЕсли; КонецПроцедуры
Проблема: между шагами 1 и 2 другой пользователь может успеть прочитать старые остатки и тоже записать движения. Оба проведения пройдут, остатки уйдут в минус.
С управляемыми блокировками:
Процедура ОбработкаПроведения(Отказ) // 0. Ставим управляемую блокировку ДО записи движений Блокировка = Новый БлокировкаДанных; ЭлементБлокировки = Блокировка.Добавить( "РегистрНакопления.ОстаткиТоваров"); ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; ЭлементБлокировки.УстановитьЗначение("Склад", Склад); ЭлементБлокировки.ИсточникДанных = ТоварыТЧ; ЭлементБлокировки.ИспользоватьИзИсточникаДанных( "Номенклатура", "Номенклатура"); Блокировка.Заблокировать(); // 1. Записываем движения Движения.ОстаткиТоваров.Записать(); // 2. Проверяем остатки (теперь безопасно) // ...проверка... КонецПроцедуры
Блокировка ставится ДО записи и ДО чтения. Мы блокируем не «всё на складе», а конкретные позиции номенклатуры на конкретном складе. Другой пользователь, проводящий накладную с тем же товаром, будет ждать. С другим товаром пройдёт параллельно.
БлокироватьДляИзменения
У наборов записей регистров накопления и бухгалтерии есть свойство БлокироватьДляИзменения. Когда оно установлено в Истина, платформа автоматически ставит управляемую блокировку при вызове Записать().
Движения.ОстаткиТоваров.БлокироватьДляИзменения = Истина; Движения.ОстаткиТоваров.Записать();
Кажется удобным, зачем писать ручную блокировку? Но есть подвох: БлокироватьДляИзменения ставит блокировку в момент записи, то есть уже после того, как вы сформировали движения. А правильная последовательность:
Заблокировать
Прочитать актуальные остатки
Записать движения
Проверить, что остатки не ушли в минус
Если вы используете БлокироватьДляИзменения, то блокировка появляется на шаге 3, а чтение остатков (шаг 2) происходит без блокировки. Между чтением и блокировкой — окно для гонки.
На практике БлокироватьДляИзменения работает корректно, когда контроль остатков выполняется ПОСЛЕ записи движений (как в типовых конфигурациях). Но если порядок «прочитал → проверил → записал» — нужна ручная блокировка до чтения.
Эскалация и её последствия
Управляемые блокировки, как и блокировки СУБД, могут эскалироваться. Если вы поставили много блокировок на отдельные записи одного пространства — менеджер 1С может решить эскалировать их до блокировки всего пространства.
Эскалация всегда происходит до уровня таблицы (пространства). Запретить эскалацию управляемых блокировок нельзя.
Это означает: если ваш документ списывает 500 позиций номенклатуры и вы ставите блокировку на каждую позицию — с определённого порога менеджер решит «проще заблокировать весь регистр», и заблокирует. Все остальные пользователи, работающие с этим регистром, встанут в очередь.
Порог эскалации зависит от версии платформы и настроек, но помнить о нём нужно. Если документы с большим количеством строк — частый сценарий, стоит подумать об архитектуре: разбить проведение на части, использовать фоновые задания для тяжёлых документов.
Разделение итогов регистров накопления
Смежная тема, напрямую связанная с блокировками. Регистры накопления хранят итоги — предрассчитанные остатки. При записи движений платформа обновляет итоги. Если два пользователя пишут движения по одному и тому же набору измерений — они конкурируют за строку итогов.
Режим разделения итогов решает эту проблему. Когда он включён, каждая транзакция пишет итоги в отдельную «копию», а при чтении платформа суммирует все копии. Конкуренция за строку итогов исчезает.
Включить разделение итогов: в конфигураторе — свойство регистра «Разрешить разделение итогов» = Истина, в режиме 1С:Предприятие — РегистрыНакопления.ОстаткиТоваров.УстановитьРежимРазделенияИтогов(Истина).
Без разделения итогов два пользователя, проводящих документы с одинаковыми Склад + Номенклатура, будут ждать друг друга даже если управляемые блокировки настроены идеально — потому что конфликт возникнет на уровне СУБД при обновлении строки итогов.
Отдельно стоит знать: параллельная запись в периодический независимый регистр сведений может блокироваться даже при разных периодах. Если измерения совпадают, но периоды разные — логически записи не конфликтуют.
Типичные ошибки
Блокировка после чтения. Прочитали остатки, потом поставили блокировку, потом записали. Между чтением и блокировкой — окно. Другая транзакция могла изменить остатки. Правильно: сначала блокировка, потом чтение, потом запись.
Блокировка не того пространства. Заблокировали РегистрНакопления.Остатки.НаборЗаписей (по регистратору), а контроль остатков делаете по измерениям. Пространства не пересекаются — блокировка бесполезна. Нужно блокировать РегистрНакопления.Остатки (пространство измерений).
Избыточная блокировка. Заблокировали весь регистр без указания значений измерений. Это эквивалент блокировки всей таблицы — никто не сможет работать с этим регистром, пока транзакция не завершится.
Длинная транзакция. Блокировка живёт, пока жива транзакция. Если транзакция проведения документа выполняет тяжёлые вычисления, вызывает внешние сервисы или пишет в 15 регистров — блокировка висит всё это время. Чем короче транзакция, тем меньше ожидания. Если контроль остатков выполняется в начале проведения, а потом ещё 10 секунд записываются движения в другие регистры — блокировка на первый регистр висит лишние 10 секунд.
Константы как хранилище. Использование констант для часто меняющихся данных — верный способ получить блокировку на уровне всей таблицы констант. Одна запись в константу блокирует все константы конфигурации на время транзакции. Для изменяемых данных используйте регистры сведений.
Диагностика
Технологический журнал (ТЖ) — основной инструмент. Событие TLOCK показывает, какие управляемые блокировки были установлены. Событие TDEADLOCK — обнаружена взаимоблокировка. Событие TTIMEOUT — ожидание блокировки превысило таймаут.
В ТЖ видно: пространство блокировки, значения полей, режим (shared/exclusive), длительность ожидания. Этого достаточно, чтобы найти конфликтующие транзакции и понять, какой код ставит проблемную блокировку.

Проблемы с блокировками редко живут сами по себе: обычно за ними стоят более глубокие ошибки в границах ответственности, модели данных и проектировании процессов. На курсе «Функциональный архитектор 1С» как раз разбирают этот уровень — от требований, бизнес-процессов и интеграционных потоков до целевой архитектуры системы, чтобы решения в 1С были не только рабочими, но и устойчивыми под реальную нагрузку и развитие.
Чтобы узнать больше о формате обучения и познакомиться с преподавателями, приходите на бесплатные уроки:
11 марта в 20:00. «Модель „чёрный ящик“: препарируем сложные системы». Записаться
Дата и время не указаны. «Инвестиция в себя: стратегия карьеры функционального архитектора». Записаться
Полный список бесплатных уроков марта смотрите в дайджесте.
