Привет, Хабр!
Блокировки — одна из тех тем в 1С, которые все знают на уровне «ну там что-то с параллельным доступом», но мало кто понимает до конца.
В платформе 1С:Предприятие есть два разных режима управления блокировками — автоматический и управляемый. Выбор между ними определяет, как ваша система будет себя вести под нагрузкой.
Когда два пользователя одновременно меняют один и тот же документ или регистр, возникает конфликт. Без блокировок один перезапишет данные другого — классическая проблема lost update. Или один прочитает данные, которые второй ещё не дозаписал.
Блокировки гарантируют, что параллельные операции не сломают данные. Но за это приходится платить — пока один пользователь держит блокировку, другие ждут. Вопрос в том, кто управляет этим процессом: СУБД автоматически или мы вручную.
Автоматический режим
В автоматическом режиме платформа 1С полностью делегирует блокировки серверу СУБД. Когда транзакция читает или пишет данные, СУБД сама ставит блокировки нужной гранулярности — на строку, страницу или таблицу.
Как это работает:
// Проведение документа в автоматическом режиме Процедура ОбработкаПроведения(Отказ, Режим) // Читаем остатки — СУБД ставит shared lock на прочитанные строки Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ Количество ИЗ РегистрНакопления.ОстаткиТоваров.Остатки |ГДЕ Номенклатура = &Номенклатура"; Запрос.УстановитьПараметр("Номенклатура", Номенклатура); Результат = Запрос.Выполнить(); // Пишем движения — СУБД ставит exclusive lock Движения.ОстаткиТоваров.Записать(); КонецПроцедуры
Между чтением остатков и записью движений проходит время. За это время другая транзакция может прочитать те же остатки (shared lock не блокирует shared lock), рассчитать свои движения на основе тех же данных, и обе запишут результат. Итогом получаем то, что остатки ушли в минус, хотя каждая транзакция по отдельности проверяла, что товар есть.
СУБД решает это через эскалацию блокировок и уровни изоляции. Но у платформы 1С нет тонкого контроля над уровнями изоляции в автоматическом режиме. MS SQL Server в автоматическом режиме 1С работает на уровне SERIALIZABLE, а он очень стргогий. Это убивает параллельность, транзакции выстраиваются в очередь, а при высокой нагрузке начинаются дедлоки.
Главная проблема автоматического режима в избыточных блокировках. СУБД не знает бизнес-логику. Она не знает, что вам нужно заблокировать только остатки по конкретному товару на конкретном складе. Она блокирует на уровне строк или страниц таблицы, часто захватывая больше, чем нужно. При эскалации может заблокировать всю таблицу целиком.
Управляемый режим
В управляемом режиме СУБД работает на уровне изоляции READ COMMITTED (для MS SQL — с READ_COMMITTED_SNAPSHOT). Это значит: читаем без блокировок (читаем снимок данных), а блокировки ставим сами, через механизм управляемых блокировок платформы 1С.
Процедура ОбработкаПроведения(Отказ, РежимПроведения) // 1. Сначала ставим управляемую блокировку БлокировкаДанных = Новый БлокировкаДанных; ЭлементБлокировки = БлокировкаДанных.Добавить( "РегистрНакопления.ОстаткиТоваров"); ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; ЭлементБлокировки.УстановитьЗначение("Номенклатура", Номенклатура); ЭлементБлокировки.УстановитьЗначение("Склад", Склад); БлокировкаДанных.Заблокировать(); // 2. Теперь читаем остатки — мы уверены, что никто параллельно не изменит Запрос = Новый Запрос; Запрос.Текст = "ВЫБРАТЬ Количество ИЗ РегистрНакопления.ОстаткиТоваров.Остатки |ГДЕ Номенклатура = &Номенклатура И Склад = &Склад"; Запрос.УстановитьПараметр("Номенклатура", Номенклатура); Запрос.УстановитьПараметр("Склад", Склад); Результат = Запрос.Выполнить(); // 3. Проверяем и пишем // ... Движения.ОстаткиТоваров.Записать(); КонецПроцедуры
Мы ставим блокировку до чтения, и ставим её точечно, только по конкретной номенклатуре на конкретном складе. Другой пользователь, проводящий документ по другому товару, не будет ждать, его блокировка не пересекается с нашей.
Гранулярность: почему это решает
В автоматическом режиме СУБД блокирует строки таблицы. Если два документа списывают разные товары, но записи оказались на одной странице данных, один ждёт другого. Если СУБД решит эскалировать блокировку до таблицы встают все.
В управляемом режиме блокировка ставится по значениям измерений регистра. Блокировка по Номенклатура = "Молоко" и блокировка по Номенклатура = "Хлеб" — это два независимых блока. Они не пересекаются, никто никого не ждёт.
Можно блокировать с разной точностью:
// Грубо: блокируем весь регистр (плохо для параллельности) ЭлементБлокировки = БлокировкаДанных.Добавить( "РегистрНакопления.ОстаткиТоваров"); ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; // Точнее: по номенклатуре ЭлементБлокировки.УстановитьЗначение("Номенклатура", Номенклатура); // Ещё точнее: по номенклатуре И складу ЭлементБлокировки.УстановитьЗначение("Номенклатура", Номенклатура); ЭлементБлокировки.УстановитьЗначение("Склад", Склад);
Чем точнее блокировка,тем больше параллельных операций возможно. Но и тем больше ответственности на нас.
Блокировка из диапазона значений
Когда документ содержит несколько строк с разными товарами, нужно заблокировать все:
БлокировкаДанных = Новый БлокировкаДанных; Для Каждого СтрокаТЧ Из Товары Цикл ЭлементБлокировки = БлокировкаДанных.Добавить( "РегистрНакопления.ОстаткиТоваров"); ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; ЭлементБлокировки.УстановитьЗначение("Номенклатура", СтрокаТЧ.Номенклатура); ЭлементБлокировки.УстановитьЗначение("Склад", СтрокаТЧ.Склад); КонецЦикла; // Все блокировки ставятся одним вызовом — атомарно БлокировкаДанных.Заблокировать();
Заблокировать() ставит все элементы за один вызов. Если какой-то из ресурсов уже заблокирован другой транзакцией — текущая будет ждать (или получит ошибку по таймауту). Не нужно делать Заблокировать() в цикле, это и медленнее, и может привести к дедлоку.
Дедлоки: как возникают и как избежать
Дедлок — ситуация, когда транзакция А ждёт ресурс, заблокированный транзакцией Б, а Б ждёт ресурс, заблокированный А. Обе стоят вечно.
Классический сценарий:
Транзакция 1: Блокирует Товар A, потом пытается заблокировать Товар B Транзакция 2: Блокирует Товар B, потом пытается заблокировать Товар A получаем Дедлок
Поэтому мотаем на всегда блокируйте ресурсы в одном и том же порядке. Если все транзакции блокируют товары в порядке возрастания кода — дедлок невозможен:
// Сортируем строки табличной части перед блокировкой МассивНоменклатуры = Новый Массив; Для Каждого СтрокаТЧ Из Товары Цикл МассивНоменклатуры.Добавить(СтрокаТЧ.Номенклатура); КонецЦикла; // Сортируем по ссылке (внутреннему идентификатору) // и блокируем в этом порядке
Вообще в 1 1С дедлоки чаще возникают не из-за порядка блокировок в коде, а из-за эскалации блокировок на уровне СУБД в автоматическом режиме. Ещё одна причина перейти на управляемый.
Разделяемые и исключительные блокировки
Управляемые блокировки бывают двух видов:
Исключительная (РежимБлокировкиДанных.Исключительный) — никто не может ни читать, ни писать заблокированные данные. Используется, когда вы собираетесь менять данные и хотите гарантировать, что между чтением и записью никто не вмешается.
Разделяемая (РежимБлокировкиДанных.Разделяемый) — другие транзакции могут ставить разделяемую блокировку на те же данные (и читать), но не могут ставить исключительную (и писать).
// Разделяемая — для чтения, которое должно быть консистентным ЭлементБлокировки.Режим = РежимБлокировкиДанных.Разделяемый; // Исключительная — для чтения с последующей записью ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Если после чтения вы будете писать — исключительная. Если только читаете и хотите гарантировать, что данные не изменятся пока вы их обрабатываете — разделяемая.
Пространства блокировок
Блокировки можно ставить не только на регистры и справочники, но и на произвольные «пространства» — строковые ключи:
БлокировкаДанных = Новый БлокировкаДанных; ЭлементБлокировки = БлокировкаДанных.Добавить( "РегистрНакопления.ОстаткиТоваров"); ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный; ЭлементБлокировки.ОбластьБлокировкиДанных = "ОсновныеДанные";
Но чаще пространства блокировок используются неявно, платформа сама определяет пространство по имени таблицы базы данных.
Таймауты и обработка ошибок
Когда транзакция не может получить блокировку, она ждёт. По умолчанию 20 секунд (настраивается). Если за это время ресурс не освободился получаем исключение:
Попытка БлокировкаДанных.Заблокировать(); Исключение // Не удалось получить блокировку за отведённое время Сообщить("Данные заблокированы другим пользователем. Попробуйте позже."); Отказ = Истина; Возврат; КонецПопытки;
Всегда обрабатывайте ошибку блокировки. «
Возможные проблемки
Забыли поставить блокировку. Перешли на управляемый режим, убрали автоматические блокировки, а управляемые не добавили. Данные читаются без блокировки, записываются без защиты — lost update гарантирован.
Блокировка после чтения. Поставили Заблокировать() после запроса. Бесполезно, данные уже прочитаны, между чтением и блокировкой кто-то мог их изменить. Всегда: сначала блокировка, потом чтение.
Слишком широкая блокировка. Заблокировали весь регистр вместо конкретных измерений. Работает, но параллельность убита — все пользователи стоят в очереди.
Блокировка вне транзакции. Управляемые блокировки действуют в рамках транзакции. Если вы вызываете Заблокировать() вне явной или неявной транзакции — блокировка ставится и тут же снимается. Бессмысленно.
Долгая транзакция. Заблокировали ресурс в начале большой транзакции, потом делаете тяжёлые вычисления, HTTP-запросы, формирование печатных форм. Всё это время другие пользователи ждут. Блокируйте как можно позже, держите как можно меньше. Тяжёлую работу до блокировки, под блокировкой только чтение-проверка-запись.
Миграция с автоматического на управляемый
Порядок:
В свойствах конфигурации ставим «Режим управления блокировкой данных» = «Управляемый».
Проходим по всем модулям, где есть запись в регистры и справочники внутри транзакций. Добавляем
БлокировкаДанныхс правильной гранулярностью.Тестируем параллельное проведение документов. Особенно остатки, взаиморасчёты, партионный учёт.
Мониторим ожидания на блокировках через журнал регистрации и технологический журнал (событие
TLOCK).
Можно мигрировать по частям, для отдельных объектов конфигурации можно задать режим «Управляемый» при общем «Автоматическом». Но в итоге лучше прийти к полностью управляемому режиму.
Сравнение двух режимов, если коротко
Автоматический: проще для разработчика (не надо думать о блокировках), но хуже для параллельности. СУБД блокирует грубо, уровень изоляции высокий, дедлоки при нагрузке.
Управляемый: сложнее для разработчика (нужно явно ставить блокировки), но лучше для параллельности. Блокировки точечные, по бизнес-логике. СУБД работает на READ COMMITTED, не мешает чтениям.
Для новых проектов только управляемый. Для существующих на автоматическом планируйте миграцию, если есть проблемы с производительностью и параллельностью.

Проблемы с блокировками редко возникают изолированно: чаще это симптом того, что система росла кусками, а решения принимались без общей архитектурной модели. Пока знания остаются разрозненными, такие места приходится чинить постфактум. Курс «Архитектор 1С» помогает собрать это в целую картину: от проектирования решений и интеграций до контроля качества, производительности и устойчивости системы.
Пройдите вступительный тест, чтобы понять, насколько у вас уже есть база для архитектурного уровня задач и какие темы стоит усилить в первую очередь.
А если хотите сначала познакомиться с подходом и экспертами курса, можно начать с открытых уроков:
31 марта в 20:00. «Этапы и артефакты 1С-проектов 2026: ИИ-интеграции и гибкая разработка». Записаться
7 апреля в 20:00. «Модульное (unit) тестирование в 1С». Записаться
21 апреля в 19:00. «BPMN для 1С: Как правильно моделировать интеграции? (Разбираем 4 уровня детализации)». Записаться
