Почему ПланыОбмена.ВыбратьИзменения() блокирует базу и как избавиться от конфликтов блокировок?

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

При реализации механизмов обмена данными в 1С:Предприятие 8 разработчики часто сталкиваются с ситуацией, когда вызов метода ПланыОбмена.ВыбратьИзменения() приводит к длительным блокировкам таблиц. Пользователи начинают жаловаться на невозможность записать документы или справочники, возникают ошибки "Конфликт блокировок при выполнении транзакции". Особенно остро эта проблема проявляется при интеграции через Веб-сервисы (Web-services) или HTTP-сервисы. Рассмотрим подробно, почему это происходит и как оптимизировать процесс обмена.

Разберем механизм работы метода ВыбратьИзменения()

Для начала проанализируем, что именно делает платформа в момент вызова ВыбратьИзменения(Узел, НомерСообщения). Этот метод выполняет две критически важные операции:

  1. Получение данных: Платформа обращается к таблицам регистрации изменений (так называемые таблицы _DocumentChngR, _ReferenceChngR и другие) и выбирает ссылки на объекты, зарегистрированные для конкретного узла.
  2. Обновление номера сообщения: Это ключевой момент. У всех записей, где номер сообщения был равен NULL (то есть изменения еще ни разу не включались в пакет), проставляется переданный в метод НомерСообщения.

С точки зрения СУБД, вторая операция является командой UPDATE. Для выполнения обновления строк платформа обязана наложить эксклюзивную блокировку на эти строки. Если объем данных велик (тысячи объектов), 1С или СУБД могут выполнить эскалацию блокировки, закрыв доступ не к отдельным строкам, а ко всей таблице регистрации изменений целиком. Пока эта транзакция не завершится, любой процесс в системе, пытающийся записать объект, входящий в состав плана обмена, будет стоять в очереди и в итоге может "отвалиться" по таймауту.

Выясним основную причину длительных блокировок в сервисах

Рассмотрим ситуацию, типичную для обмена через веб-сервисы. Проанализируем настройки метаданных. У каждой операции веб-сервиса в конфигураторе есть свойство "В транзакции". Если эта галочка установлена, то вся логика, написанная внутри функции-обработчика веб-сервиса, выполняется в рамках одной глобальной транзакции.

Выясним, к чему это приводит:

  1. Метод ВыбратьИзменения() выполняется в самом начале выгрузки. Он мгновенно накладывает блокировки на таблицы регистрации, чтобы проставить номера сообщений.
  2. Далее код начинает длительный процесс: формирование XML или JSON, сериализацию данных, возможно, запись во временные файлы или отправку по сети.
  3. Пока весь этот код не отработает до конца и функция веб-сервиса не вернет результат, транзакция не закроется.
  4. Следовательно, блокировки на таблицах регистрации будут висеть всё время выгрузки (иногда минуты и даже десятки минут), парализуя работу всей базы.

Решение №1: Самый простой и эффективный способ — зайти в свойства операции вашего веб-сервиса и снять флаг "В транзакции". В этом случае метод ВыбратьИзменения() создаст свою собственную, очень короткую внутреннюю транзакцию, быстро обновит номера и тут же отпустит блокировки, позволяя пользователям работать дальше, пока код занимается подготовкой данных.

Рассмотрим использование метода через проверку активности транзакции

Если вы не уверены, открыта ли транзакция в данный момент, мы можем использовать метод ТранзакцияАктивна(). Посмотрим на пример кода, который поможет продиагностировать проблему:


Если ТранзакцияАктивна() Тогда
    ЗаписьЖурналаРегистрации("ОбменДанными", УровеньЖурналаРегистрации.Предупреждение, , , "Внимание! Выбор изменений запущен внутри внешней транзакции!");
КонецЕсли;
ВыборкаИзмененных = ПланыОбмена.ВыбратьИзменения(Узел, НомерОтправленного);

Помните, что ВыбратьИзменения() всегда должен работать максимально автономно. Если вам необходимо выполнять выгрузку именно в транзакции (что бывает редко), старайтесь максимально сократить время между выбором изменений и завершением транзакции.

Проанализируем альтернативный способ: Ручное управление номерами сообщений

Если стандартный метод ВыбратьИзменения() всё равно вызывает проблемы (например, из-за специфики СУБД или огромных объемов), мы можем реализовать логику "вручную". Рассмотрим алгоритм, который позволяет избежать блокировок методом проставления номеров:

  1. Вместо ВыбратьИзменения() используем обычный запрос к таблице изменений. В запросе выбираем только те записи, у которых НомерСообщения IS NULL.
  2. Выгружаем данные на основе этого запроса. На этом этапе мы вообще ничего не блокируем и не меняем в базе.
  3. После того как принимающая сторона подтвердила получение данных, вызываем метод ПланыОбмена.УдалитьРегистрациюИзменений(Узел, НомерПринятого).

Посмотрим на пример кода для удаления регистрации:


// Предположим, мы получили подтверждение от удаленного узла
// НомерПринятого — это номер сообщения, которое успешно загрузила та сторона
ПланыОбмена.УдалитьРегистрациюИзменений(Узел, НомерПринятого);

Важное замечание: Если вы не вызывали ВыбратьИзменения(), то у ваших объектов в таблице изменений номер сообщения остался NULL. В этом случае УдалитьРегистрациюИзменений их не удалит, так как этот метод удаляет только записи с номером сообщения меньшим или равным указанному. Поэтому использование "ручного" запроса требует аккуратности: либо вы все-таки вызываете ВыбратьИзменения() коротким импульсом, либо используете удаление по конкретным ссылкам.

Разберем финт с "прокруткой" выборки

Существует еще один подход, который иногда применяется в типовых конфигурациях. Мы можем быстро "пробежать" по выборке, чтобы зафиксировать номера, и сразу закрыть это соединение. Рассмотрим пример:


ВыборкаИзмененных = ПланыОбмена.ВыбратьИзменения(Узел, НомерОтправленного);
// Просто обходим выборку, ничего не делая, чтобы принудительно зафиксировать номера
Пока ВыборкаИзмененных.Следующий() Цикл
    // Пустой цикл
КонецЦикла;
// После этого номера проставлены. Если внешней транзакции нет, блокировки сняты.
// Теперь мы можем спокойно выбирать данные запросом по НомеруСообщения

Однако, как показывает практика, если флаг "В транзакции" у веб-сервиса включен, даже такой обход не спасет от блокировок до конца работы всего сервиса. Поэтому приоритетным решением остается настройка свойств сервиса.

Посмотрим на использование объекта ЗаписьСообщенияОбмена

Для правильной организации процесса обмена рекомендуем использовать специализированный объект ЗаписьСообщенияОбмена. Рассмотрим, как он помогает структурировать код:


ЗаписьСообщения = ПланыОбмена.СоздатьЗаписьСообщения();
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл(ПутьКФайлу);

ЗаписьСообщения.НачатьЗапись(ЗаписьXML, Узел);
// В этот момент ЗаписьСообщения.НомерСообщения уже содержит следующий номер
Выборка = ПланыОбмена.ВыбратьИзменения(Узел, ЗаписьСообщения.НомерСообщения);

Пока Выборка.Следующий() Цикл
    Данные = Выборка.Получить();
    ЗаписатьДанные(ЗаписьXML, Данные);
КонецЦикла;

ЗаписьСообщения.ЗакончитьЗапись();
ЗаписьXML.Закрыть();

Использование ЗаписьСообщенияОбмена гарантирует, что заголовок сообщения будет сформирован корректно, а номера сообщений в узле плана обмена обновятся синхронно с регистрацией изменений. Но опять же, помните: весь этот блок не должен находиться внутри долгой транзакции веб-сервиса.

Подведем итоги и сформулируем рекомендации

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

  1. Проверьте свойства сервисов: Убедитесь, что у операций Web и HTTP сервисов, выполняющих обмен, снят флаг "В транзакции".
  2. Минимизируйте транзакции: Не оборачивайте вызов ВыбратьИзменения() в явные блоки НачатьТранзакцию() ... ЗафиксироватьТранзакцию(), если внутри них выполняется длительная логика.
  3. Используйте порции: Если изменений слишком много (сотни тысяч), попробуйте обрабатывать их порциями, чтобы не вызывать эскалацию блокировок на уровне СУБД.
  4. Контролируйте блокировки: При использовании управляемого режима блокировок, перед вызовом выбора изменений полезно установить блокировку на сам узел плана обмена, чтобы избежать Deadlock при одновременном старте нескольких сессий обмена.

Соблюдение этих правил позволит сделать обмен данными в 1С быстрым, стабильным и незаметным для конечных пользователей системы.

← На главную