Как уменьшить конфликт блокировок транзакций при обмене в РИБ?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При работе с распределенными информационными базами (РИБ) большого объема в конфигурациях на обычных формах (например, Управление торговлей 10.3) разработчики регулярно сталкиваются с ситуацией, когда регламентный или интерактивный обмен намертво парализует работу пользователей. При формировании сообщения обмена в дочерний узел пользователи получают ошибку «Конфликт блокировки транзакций», проведение документов останавливается, а запись любых объектов зависает. Разберем по шагам, почему возникает эта коллизия, как точно локализовать проблему и какими путями минимизировать взаимные блокировки.

Почему возникает конфликт блокировок: технические причины

Выясним причину такого поведения системы. Во время выгрузки данных штатный метод ЗаписатьИзменения() выполняет выборку зарегистрированных данных из специальных служебных таблиц СУБД — таблиц регистрации изменений (имеющих физический префикс _ChangeRec). В конфигурациях с автоматическим режимом управления блокировками транзакций это приводит к серьезным проблемам:

  1. Автоматический режим блокировок и уровни изоляции. В конфигурации Управление торговлей 10.3 по умолчанию используется автоматический режим. При чтении данных СУБД накладывает разделяемые блокировки (S-блокировки) на уровне изоляции Repeatable Read или Serializable. Эти блокировки удерживаются до полного завершения транзакции выгрузки сообщения обмена, запрещая пользователям модифицировать те же самые строки и таблицы.
  2. Эскалация блокировок (Lock Escalation) в СУБД. Когда узел выгружает большой объем информации (файлы размером в сотни мегабайт, время формирования более 5–10 минут), количество захваченных строк и страниц в таблице регистрации изменений превышает системный порог СУБД (в MS SQL Server порог по умолчанию составляет порядка 5000 блокировок). СУБД выполняет укрупнение блокировок до всей физической таблицы (TAB). С этого момента любая попытка пользователя провести документ, записать контрагента или номенклатуру (что требует вставки записи в таблицу _ChangeRec) встает в очередь и ожидает окончания выгрузки обмена, завершаясь по таймауту ошибкой конфликта блокировок.
  3. Общая таблица констант. В базах с режимом совместимости 8.2 все константы хранятся в единой физической таблице _Consts. Если в состав плана обмена включена хотя бы одна константа, блокировка этой таблицы парализует работу практически всей базы.
  4. Устаревание статистики СУБД. Таблицы регистрации изменений непрерывно меняются: в них происходит поток вставок при проведении документов и массовое удаление записей после получения квитанций об успешном приеме. Статистика распределения данных быстро деградирует, оптимизатор СУБД начинает вместо точечного поиска по индексам делать сканирование всей таблицы (Index Scan или Table Scan), многократно увеличивая время удержания блокировок.

Локализация узкого места через профайлер и технологический журнал

Прежде чем перестраивать архитектуру обмена, локализуем конкретные таблицы, на которых сталкиваются транзакции. Проанализируем ситуацию двумя надежными инструментами:

  1. Использование SQL Server Profiler. Запустим трассировку со следующими событиями: Errors and Warnings: Execution Warning, а также Locks: Lock:Acquired и Locks: Lock:Cancel. Установим фильтр на длительность: Duration >= 1000 (время удержания более 1 секунды). Обязательно выведем поля ObjectID, IndexID, TextData и SPID. По идентификатору ObjectID определим имя таблицы в базе с помощью системной функции СУБД: SELECT OBJECT_NAME(ИдентификаторОбъекта); Это позволит точно увидеть, упираются ли транзакции в конкретную таблицу _ChangeRec, в регистр накопления или в общую таблицу констант.
  2. Настройка Технологического журнала 1С. Настроим сбор событий TLOCK и TDEADLOCK с фильтром по длительности (например, более 5 секунд). Журнал покажет контекст выполнения кода 1С, стек вызовов и метаданные, на которых сеанс обмена вытесняет пользовательские сеансы.

Архитектурное решение: выделенный буферный узел

Если информационная база работает в режиме высокой нагрузки (объем 400 Гб и выше, десятки и сотни параллельных пользователей), классическая схема «Центральная база напрямую обменивается со всеми филиалами» перестает справляться. Центральный узел вынужден по очереди (или параллельно) обслуживать множество подчиненных узлов, удерживая длительные блокировки.

Наиболее эффективным решением является развертывание буферного узла РИБ:

  1. Создадим подчиненный узел РИБ, в котором полностью запретим работу реальных пользователей (доступ открыт только для регламентных заданий обмена).
  2. Центральная рабочая база взаимодействует исключительно с этим одним буферным узлом. Поскольку обмен происходит между двумя узлами, сеанс выгрузки из рабочей базы выполняется значительно быстрее и предсказуемее.
  3. Буферный узел, в свою очередь, берет на себя всю тяжелую работу по формированию и приему сообщений для десятков дочерних периферийных узлов (магазинов, складов, филиалов).

В результате пользователи центрального офиса полностью изолированы от длительных блокировок, создаваемых периферией.

Настройка параметров транзакций и регламента обмена

Рассмотрим прикладные параметры, которые можно настроить без изменения архитектуры:

  1. Регулирование параметра «Количество элементов в транзакции». В настройках обмена стандартных конфигураций присутствует параметр порционной обработки. Крайне не рекомендуется устанавливать значение «1» — это порождает колоссальную нагрузку на дисковую подсистему и журнал транзакций СУБД (LDF) из-за постоянных операций COMMIT. Оптимальным диапазоном для снижения объема блокируемых данных является размер пачки от 100 до 1000 элементов на одну транзакцию. Это дробит монолитную транзакцию на мелкие части, давая возможность пользовательским сеансам вклиниваться между фиксациями.
  2. Увеличение частоты обменов. Чем реже запускается выгрузка, тем больше изменений накапливается в таблицах регистрации и тем дольше формируется файл выгрузки. Сократим интервал между обменами (например, выгрузка каждые 10–15 минут вместо одного раза в 2–3 часа). Небольшие пакеты данных обрабатываются СУБД за секунды и не успевают привести к эскалации блокировок.
  3. Исключение параллельных сеансов выгрузки. Не запускаем одновременный обмен сразу по нескольким узлам из одной информационной базы. Выгрузка и загрузка по разным планам обмена должны быть строго выстроены в единую последовательную очередь регламентных заданий.

Оптимизация на уровне СУБД MS SQL Server

Если узким местом выступает таблица регистрации изменений конкретного объекта (например, _ChangeRecXXX для документа реализации), применим административные меры на стороне СУБД:

  1. Отключение эскалации блокировок. Запретим СУБД укрупнять строчные и страничные блокировки до уровня таблицы. Выполним скрипт в среде SQL Server Management Studio: ALTER TABLE dbo._ChangeRecXXXX SET (LOCK_ESCALATION = DISABLE); После этого параллельные транзакции смогут обновлять и вставлять записи в разные строки и страницы одной и той же таблицы регистрации без блокировки таблицы целиком.
  2. Регламентное обновление статистики по таблицам обмена. Настроим отдельный SQL Agent Job, который каждые несколько часов (или перед пиковыми выгрузками) выполняет обновление статистики и перестроение индексов только для служебных таблиц обмена: UPDATE STATISTICS dbo._ChangeRecXXXX WITH FULLSCAN; Это исключит деградацию планов выполнения запросов, которые платформа строит при выгрузке изменений.

Оптимизация регистрации изменений в кодовой базе

Проанализируем программный код подписок на события. Распространенная ошибка — перенос условий отбора данных на момент выгрузки в файл:

  1. Фильтрация при регистрации, а не при выгрузке. Если объект попадает в таблицу регистрации изменений безусловно, а отсекается сложным кодом уже внутри метода ПриОтправкеДанныхПодчиненному(), платформа тратит колоссальное время на вычитку лишних записей из базы и удержание блокировок. Проверим подписки на события: отбор должен жестко выполняться в момент записи объекта с помощью процедуры ПланыОбмена.ЗарегистрироватьИзменения() или отказа от регистрации, чтобы таблица _ChangeRec содержала исключительно целевые данные.
  2. Исключение констант из плана обмена. Откроем состав плана обмена в конфигураторе. Проверим, включены ли константы в авторегистрацию. Рекомендуется отключить автоматическую регистрацию констант, не участвующих в регулярных бизнес-процессах филиалов, чтобы исключить блокировку физической таблицы _Consts.
  3. Переход на управляемый режим блокировок. В перспективе рекомендуется перевести свойства конфигурации и конкретного плана обмена в режим Управляемый. В этом режиме на сервере СУБД включается версионирование строк (Read Committed Snapshot Isolation), при котором операции чтения данных для обмена больше никогда не блокируют операции записи, выполняемые пользователями.
← На главную