Как работают управляемые блокировки и индексы в 1С 8.3?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Работа с высоконагруженными системами на платформе 1С:Предприятие 8.3 требует от разработчика глубокого понимания внутренних механизмов управления данными. Одной из самых важных тем является использование управляемых блокировок и оптимизация индексов. В этой статье мы подробно разберем, как платформа взаимодействует с СУБД, зачем нужны разделяемые блокировки и как правильно индексировать данные для достижения максимальной производительности.

Уровни изоляции СУБД и Менеджер блокировок 1С

Рассмотрим, как именно платформа 1С управляет транзакциями. В режиме управляемых блокировок клиент-серверная база данных работает на уровне изоляции READ COMMITTED SNAPSHOT. Проанализируем, что это дает нам на практике: в этом режиме читающие транзакции не блокируют пишущие, а пишущие — читающие. Это достигается за счет версионирования строк в СУБД.

Однако уровня Snapshot недостаточно для обеспечения логической целостности бизнес-процессов (например, при контроле остатков). Здесь в игру вступает Менеджер блокировок 1С. Это программный слой на уровне сервера приложений 1С, который управляет «виртуальными» блокировками, не накладывая реальных блокировок на уровне строк в SQL (кроме моментов непосредственной записи). Выясним разницу между режимами блокировок:

  1. Исключительный режим (Exclusive - X): устанавливается, когда мы планируем изменять данные. Пока одна транзакция удерживает X-блокировку на ресурс, никакая другая транзакция не может ни прочитать (с установкой S-блокировки), ни изменить этот ресурс.
  2. Разделяемый режим (Shared - S): необходим, когда мы читаем данные, которые не должны измениться до конца нашей транзакции. Другие транзакции могут одновременно читать эти же данные с S-блокировкой, но никто не сможет их модифицировать (установить X-блокировку), пока мы их не «отпустим».

Зачем нужна блокировка перед методом ПолучитьОбъект()?

Разберем типичную ситуацию: программист считывает объект через ПолучитьОбъект(), меняет реквизит и записывает его. Почему в типовых конфигурациях (ERP, УТ 11, БП 3.0) перед этим часто встречается явная блокировка? Посмотрим на пример кода:


Блокировка = Новый БлокировкаДанных;
ЭлементБлокировки = Блокировка.Добавить("Документ.ЭтапПроизводства2_2");
ЭлементБлокировки.УстановитьЗначение("Ссылка", Выборка.Ссылка);
ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
Блокировка.Заблокировать();

ДокументОбъект = Выборка.Ссылка.ПолучитьОбъект();
// Манипуляции с данными
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);

Проанализируем ситуацию: метод ПолучитьОбъект() просто считывает данные. Если между чтением и записью другая транзакция изменит этот же объект, первая транзакция при вызове Записать() получит ошибку «Объект изменен или удален». Чтобы гарантировать, что с момента начала нашей работы объект никто не «перехватит», мы устанавливаем управляемую блокировку ДО получения объекта в память сервера приложений.

Принципы индексирования данных в 1С

Выясним, как платформа создает индексы в СУБД. В 1С нет возможности напрямую использовать конструкцию INCLUDE (покрывающие столбцы), поэтому платформа полагается на композитные (составные) индексы. Рассмотрим основные правила:

  1. Кластерный индекс: Всегда строится по первичному ключу. Для документов и справочников это _IDRRef (Ссылка). Это означает, что физически данные в таблице упорядочены по GUID ссылки.
  2. Индексирование реквизитов: Когда вы устанавливаете свойство «Индексировать», платформа создает некластерный индекс вида Реквизит + Ссылка. Поле Ссылка добавляется автоматически, так как оно является ключом кластерного индекса и необходимо СУБД для поиска остальных полей строки (Key Lookup).
  3. Первое измерение регистров: В регистрах накопления и сведений первое измерение индексируется всегда в составе основного индекса. Именно поэтому в запросах важно всегда указывать отбор по первому измерению — это позволяет СУБД выполнить Index Seek вместо Index Scan.

Обратим внимание на нюанс с Разделителем областей данных (в режиме SaaS). Если база работает в модели разделения, первым полем во всех индексах становится ОбластьДанных. Это может приводить к тому, что в плане запроса мы видим Index Seek, но фактически СУБД сканирует весь диапазон данных внутри одной области (одного клиента), что при большом объеме данных может быть медленным.

Работа с объектом БлокировкаДанных и источниками данных

Рассмотрим важную техническую деталь при работе с коллекциями. Часто возникает желание передать НаборЗаписей регистра непосредственно в блокировку. Посмотрим на стандартную ошибку:


// ОШИБОЧНЫЙ КОД
Набор = РегистрыСведений.Цены.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(Ссылка);
Блокировка = Новый БлокировкаДанных;
Элемент = Блокировка.Добавить("РегистрСведений.Цены");
Элемент.ИсточникДанных = Набор; // Здесь возникнет ошибка "Несоответствие типов"

Выясним причину: свойство ИсточникДанных принимает только РезультатЗапроса, ТаблицуЗначений или ТабличнуюЧасть. Чтобы заблокировать данные на основе набора записей, необходимо либо установить значения фильтров вручную через УстановитьЗначение(), либо выгрузить набор в таблицу:


ТаблицаБлокировки = Набор.Выгрузить();
Элемент.ИсточникДанных = ТаблицаБлокировки;
Элемент.ИспользоватьИзИсточникаДанных("Номенклатура", "Номенклатура");

Оптимизация внутренних коллекций: Соответствие и ТЗ

Помимо СУБД, важно оптимизировать работу в оперативной памяти сервера. Разберем два инструмента:

Соответствие (Map): работает по принципу хэш-таблицы. Поиск ключа в соответствии имеет сложность O(1). Платформа вычисляет хэш от объекта (например, ссылки) и мгновенно находит значение. Если вам нужно часто искать данные по ключу в памяти, используйте Соответствие вместо поиска в ТаблицеЗначений.

Индексы Таблицы Значений: Мало кто знает, но ТаблицаЗначений поддерживает индексирование. Рассмотрим пример:


ТЗ.Индексы.Добавить("Артикул");
Строка = ТЗ.Найти(ЗначениеАртикула, "Артикул");

Проанализируем эффект: после добавления индекса метод Найти() и НайтиСтроки() начнет работать значительно быстрее на больших таблицах (десятки тысяч строк), так как вместо полного перебора будет использоваться внутреннее дерево поиска. Однако помните, что любой индекс требует времени на построение и перестраивается при изменении данных в колонке.

Подводя итог, можно сказать, что управляемые блокировки — это мощный инструмент в руках разработчика, позволяющий тонко настраивать параллельность работы системы. Сочетание грамотных блокировок с правильным индексированием таблиц и использованием оптимальных коллекций в памяти позволяет создавать решения, стабильно работающие при любом количестве пользователей.

← На главную