При работе с распределенными информационными базами (РИБ) большого объема в конфигурациях на обычных формах (например, Управление торговлей 10.3) разработчики регулярно сталкиваются с ситуацией, когда регламентный или интерактивный обмен намертво парализует работу пользователей. При формировании сообщения обмена в дочерний узел пользователи получают ошибку «Конфликт блокировки транзакций», проведение документов останавливается, а запись любых объектов зависает. Разберем по шагам, почему возникает эта коллизия, как точно локализовать проблему и какими путями минимизировать взаимные блокировки.
Выясним причину такого поведения системы. Во время выгрузки данных штатный метод ЗаписатьИзменения() выполняет выборку зарегистрированных данных из специальных служебных таблиц СУБД — таблиц регистрации изменений (имеющих физический префикс _ChangeRec). В конфигурациях с автоматическим режимом управления блокировками транзакций это приводит к серьезным проблемам:
Управление торговлей 10.3 по умолчанию используется автоматический режим. При чтении данных СУБД накладывает разделяемые блокировки (S-блокировки) на уровне изоляции Repeatable Read или Serializable. Эти блокировки удерживаются до полного завершения транзакции выгрузки сообщения обмена, запрещая пользователям модифицировать те же самые строки и таблицы.TAB). С этого момента любая попытка пользователя провести документ, записать контрагента или номенклатуру (что требует вставки записи в таблицу _ChangeRec) встает в очередь и ожидает окончания выгрузки обмена, завершаясь по таймауту ошибкой конфликта блокировок.8.2 все константы хранятся в единой физической таблице _Consts. Если в состав плана обмена включена хотя бы одна константа, блокировка этой таблицы парализует работу практически всей базы.Index Scan или Table Scan), многократно увеличивая время удержания блокировок.Прежде чем перестраивать архитектуру обмена, локализуем конкретные таблицы, на которых сталкиваются транзакции. Проанализируем ситуацию двумя надежными инструментами:
Errors and Warnings: Execution Warning, а также Locks: Lock:Acquired и Locks: Lock:Cancel. Установим фильтр на длительность: Duration >= 1000 (время удержания более 1 секунды). Обязательно выведем поля ObjectID, IndexID, TextData и SPID. По идентификатору ObjectID определим имя таблицы в базе с помощью системной функции СУБД:
SELECT OBJECT_NAME(ИдентификаторОбъекта);
Это позволит точно увидеть, упираются ли транзакции в конкретную таблицу _ChangeRec, в регистр накопления или в общую таблицу констант.TLOCK и TDEADLOCK с фильтром по длительности (например, более 5 секунд). Журнал покажет контекст выполнения кода 1С, стек вызовов и метаданные, на которых сеанс обмена вытесняет пользовательские сеансы.Если информационная база работает в режиме высокой нагрузки (объем 400 Гб и выше, десятки и сотни параллельных пользователей), классическая схема «Центральная база напрямую обменивается со всеми филиалами» перестает справляться. Центральный узел вынужден по очереди (или параллельно) обслуживать множество подчиненных узлов, удерживая длительные блокировки.
Наиболее эффективным решением является развертывание буферного узла РИБ:
В результате пользователи центрального офиса полностью изолированы от длительных блокировок, создаваемых периферией.
Рассмотрим прикладные параметры, которые можно настроить без изменения архитектуры:
LDF) из-за постоянных операций COMMIT. Оптимальным диапазоном для снижения объема блокируемых данных является размер пачки от 100 до 1000 элементов на одну транзакцию. Это дробит монолитную транзакцию на мелкие части, давая возможность пользовательским сеансам вклиниваться между фиксациями.Если узким местом выступает таблица регистрации изменений конкретного объекта (например, _ChangeRecXXX для документа реализации), применим административные меры на стороне СУБД:
ALTER TABLE dbo._ChangeRecXXXX SET (LOCK_ESCALATION = DISABLE);
После этого параллельные транзакции смогут обновлять и вставлять записи в разные строки и страницы одной и той же таблицы регистрации без блокировки таблицы целиком.
UPDATE STATISTICS dbo._ChangeRecXXXX WITH FULLSCAN;
Это исключит деградацию планов выполнения запросов, которые платформа строит при выгрузке изменений.Проанализируем программный код подписок на события. Распространенная ошибка — перенос условий отбора данных на момент выгрузки в файл:
ПриОтправкеДанныхПодчиненному(), платформа тратит колоссальное время на вычитку лишних записей из базы и удержание блокировок. Проверим подписки на события: отбор должен жестко выполняться в момент записи объекта с помощью процедуры ПланыОбмена.ЗарегистрироватьИзменения() или отказа от регистрации, чтобы таблица _ChangeRec содержала исключительно целевые данные._Consts.Управляемый. В этом режиме на сервере СУБД включается версионирование строк (Read Committed Snapshot Isolation), при котором операции чтения данных для обмена больше никогда не блокируют операции записи, выполняемые пользователями.