При разработке многопользовательских систем на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда установленная программно управляемая блокировка (объект БлокировкаДанных) визуально «не работает»: другой сеанс спокойно читает или даже записывает данные в тот же самый регистр. Давайте подробно разберем, как устроен этот механизм, почему он отличается от блокировок на уровне СУБД и как правильно написать код, чтобы избежать конфликтов при доступе к данным.
Первое и самое важное, что мы должны уяснить: управляемые блокировки — это логический механизм, реализованный внутри кластера серверов 1С, а не физический запрет на уровне таблиц базы данных (SQL). Рассмотрим ситуацию: когда мы вызываем метод Заблокировать(), платформа не вешает «замок» на таблицу в SQL Server. Вместо этого она делает запись в своей внутренней таблице менеджера блокировок: «Этот сеанс заблокировал этот ресурс».
Проанализируем принцип «джентльменского соглашения», на котором строится работа в управляемом режиме. Если Сеанс №1 установил исключительную блокировку на регистр, а Сеанс №2 просто выполняет запрос к этому регистру без использования объекта БлокировкаДанных, то Сеанс №2 успешно прочитает данные. Это происходит потому, что обычный запрос в управляемом режиме не запрашивает у менеджера блокировок разрешение на чтение. Чтобы блокировка сработала, оба участника (и тот, кто блокирует, и тот, кто читает) должны явно объявить свои намерения через программный код.
Посмотрим на пример кода, который часто вызывает вопросы у начинающих разработчиков. Проблема здесь заключается в ожидании того, что исключительный режим автоматически «спрячет» данные от всех остальных.
Выясним причину неэффективности такого подхода:
НачатьТранзакцию() может привести к непредсказуемому поведению. Рекомендуется всегда явно указывать режим.ДЛЯ ИЗМЕНЕНИЯ. В управляемом режиме (8.2 и 8.3) эта конструкция в тексте запроса полностью игнорируется платформой.БлокировкаДанных.Рассмотрим по шагам, как должен выглядеть надежный код для работы с регистром сведений в транзакции. Разберем пример, где мы хотим прочитать записи регистра ОтложенныеОбращенияКВебСервисам, обработать их и быть уверенными, что никто другой в этот момент их не перехватит.
НачатьТранзакцию(РежимУправленияБлокировкойДанных.Управляемый);
Попытка
// 1. Создаем объект блокировки
Блокировка = Новый БлокировкаДанных;
// 2. Описываем пространство блокировки
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ОтложенныеОбращенияКВебСервисам");
// 3. Устанавливаем режим.
// Исключительный — никто другой не сможет установить блокировку (ни S, ни X).
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
// 4. Важно: если нужно заблокировать не весь регистр, а часть, указываем измерения
// ЭлементБлокировки.УстановитьЗначение("Организация", ВыбраннаяОрганизация);
// 5. Вызываем блокировку ДО выполнения запроса
Блокировка.Заблокировать();
// 6. Теперь выполняем чтение данных
Запрос = Новый Запрос("ВЫБРАТЬ * ИЗ РегистрСведений.ОтложенныеОбращенияКВебСервисам ГДЕ...");
Результат = Запрос.Выполнить();
// Логика обработки данных
// ...
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Проанализируем ситуацию со стороны СУБД. В режиме управляемых блокировок 1С использует уровень изоляции Read Committed (часто с механизмом Snapshot Isolation в SQL Server). Это означает следующее: когда одна транзакция изменяет данные, СУБД не блокирует чтение этих данных для других транзакций. Вместо этого другие транзакции получают «старую», последнюю зафиксированную версию данных из tempdb.
Таким образом, если вы хотите создать очередь сообщений на регистре сведений, вы обязаны приучить все свои процедуры (и те, что пишут, и те, что читают очереди) использовать объект БлокировкаДанных с одинаковой областью (именем регистра и ключевыми измерениями). Только в этом случае второй сеанс «наткнется» на установленную блокировку первого сеанса в менеджере блокировок 1С и будет ожидать завершения транзакции.
Посмотрим на важный нюанс: гранулярность блокировки. Если вы вызываете Блокировка.Добавить("РегистрСведений.ИмяРегистра") без указания измерений, вы блокируете весь регистр. В высоконагруженных системах это может привести к значительным ожиданиям (Timeout).
Чтобы повысить параллельность работы, всегда старайтесь сузить область блокировки. Например, если записи разделены по узлам обмена или организациям, обязательно укажите это:
ЭлементБлокировки = Блокировка.Добавить("РегистрСведений.ОтложенныеОбращенияКВебСервисам");
ЭлементБлокировки.УстановитьЗначение("УзелИнформационнойБазы", ТекущийУзел);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();
В этом случае другой сеанс, работающий с другим узлом, сможет беспрепятственно установить свою блокировку и продолжить работу, не дожидаясь вашей транзакции.
Подводя итог нашему разбору, выделим ключевые правила работы с управляемыми блокировками:
Управляемый.ДЛЯ ИЗМЕНЕНИЯ в текстах запросов — этот метод больше не управляет блокировками в современных конфигурациях.Соблюдение этих правил позволит вам создавать стабильные и производительные решения, корректно работающие в условиях многопользовательской среды.