Как оптимизировать массовую запись данных и избежать блокировок в 1С:Предприятие?

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

В процессе работы с крупными базами данных 1С:Предприятие программисты часто сталкиваются с проблемой катастрофического падения производительности при необходимости обновить или записать десятки тысяч записей в регистры или справочники. Типичная ситуация: запуск обработки по обновлению цен или остатков приводит к тому, что вся работа пользователей в системе парализуется из-за блокировок таблиц базы данных. Чтобы этого избежать, необходимо провести предварительный замер производительности контура 1С и поиск узких мест. В данной статье мы подробно разберем, как правильно организовать процесс массовой записи данных, минимизировать время выполнения операции и обеспечить стабильную работу системы для остальных пользователей.

Анализ причин возникновения блокировок

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

Разберем основные факторы, замедляющие запись:

  1. Автоматические проверки в подписках на события: Каждая запись объекта инициирует выполнение кода в модулях объекта и связанных подписках.
  2. Обновление индексов: При каждой записи СУБД обновляет индексы таблицы, что является ресурсозатратной операцией.
  3. Длительность транзакции: Чем больше объектов обрабатывается внутри одного блока НачатьТранзакцию()...ЗафиксироватьТранзакцию(), тем выше риск конфликта блокировок.

Способ 1: Использование свойства ОбменДанными.Загрузка

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

Рассмотрим пример кода для справочника "Номенклатура":


ОбъектНоменклатура = Справочники.Номенклатура.НайтиПоКоду("00001").ПолучитьОбъект();
ОбъектНоменклатура.ОбменДанными.Загрузка = Истина;
ОбъектНоменклатура.Наименование = "Новое наименование";
ОбъектНоменклатура.Записать();

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

Способ 2: Пакетная обработка данных (Разделение транзакций)

Выясним, как правильно распределить нагрузку на сервер. Вместо того чтобы пытаться записать все данные одним огромным блоком, мы разделим их на порции (пакеты). Это позволит системе "отпускать" блокировки между пакетами, давая возможность другим процессам вклиниться в работу. Для более сложной реализации параллельной обработки можно применить менеджер потоков: реализация задачи в потоках, что позволит задействовать все ресурсы сервера.

Посмотрим на пример реализации пакетной записи цен номенклатуры в регистр сведений:


// Определим размер пакета
РазмерПакета = 1000;
Счетчик = 0;

НачатьТранзакцию();

Для Каждого СтрокаДанных Из ТаблицаЦен Цикл
    
    МенеджерЗаписи = РегистрыСведений.ЦеныНоменклатуры.СоздатьМенеджерЗаписи();
    МенеджерЗаписи.Номенклатура = СтрокаДанных.Номенклатура;
    МенеджерЗаписи.Цена = СтрокаДанных.Цена;
    МенеджерЗаписи.Период = ТекущаяДата();
    
    // Отключаем лишние проверки для ускорения
    МенеджерЗаписи.ОбменДанными.Загрузка = Истина;
    МенеджерЗаписи.Записать();
    
    Счетчик = Счетчик + 1;
    
    // Если достигли размера пакета - фиксируем и начинаем новую транзакцию
    Если Счетчик % РазмерПакета = 0 Тогда
        ЗафиксироватьТранзакцию();
        НачатьТранзакцию();
        // Здесь можно добавить небольшую паузу, если нагрузка критична
    КонецЕсли;
    
КонецЦикла;

Если ТранзакцияАктивна() Тогда
    ЗафиксироватьТранзакцию();
КонецЕсли;

В данном примере мы используем НачатьТранзакцию() для объединения 1000 записей. Это "золотая середина": запись происходит намного быстрее, чем по одной строке, но при этом блокировка не удерживается слишком долго.

Способ 3: Оптимизация записи в Регистры Сведений (Наборы записей)

Проанализируем ситуацию, когда нам нужно обновить данные по конкретному регистратору или по определенному измерению. Использование МенеджерЗаписи в цикле — не самый быстрый вариант. Гораздо эффективнее работать с объектом РегистрСведенийНаборЗаписей.

Разберем алгоритм работы с набором:

  1. Создаем объект НаборЗаписей.
  2. Устанавливаем фильтры по измерениям (Отбор).
  3. Загружаем или очищаем данные.
  4. Записываем весь набор одним вызовом.

Пример кода для эффективного обновления цен по одной группе товаров:


Набор = РегистрыСведений.ЦеныНоменклатуры.СоздатьНаборЗаписей();
Набор.Отбор.Номенклатура.Установить(ВыбранныйТовар);

// Читаем текущие записи, если нужно их модифицировать
Набор.Прочитать();

Для Каждого Запись Из Набор Цикл
    Запись.Цена = Запись.Цена * 1.1; // Увеличиваем на 10%
КонецЦикла;

Набор.ОбменДанными.Загрузка = Истина;
Набор.Записать();

Использование НаборЗаписей значительно сокращает количество SQL-запросов к серверу базы данных, так как платформа оптимизирует передачу данных "одним куском". Если вы хотите увидеть, как эти запросы выглядят со стороны СУБД, рекомендуем анализировать SQL сервер глазами 1С-ника.

Рекомендации по обеспечению стабильности

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

1. Мониторинг фоновых заданий. Массовые операции лучше всего выносить в фоновые задания. Это освобождает интерфейс пользователя и позволяет серверу распределять ресурсы более гибко. Используйте ФоновыеЗадания.Выполнить() для запуска тяжелых расчетов.

2. Явное управление блокировками. В управляемом режиме блокировок рекомендуется использовать объект БлокировкаДанных.

3. Проверка на наличие изменений. Проанализируем простой совет: не записывайте данные, если они не изменились. Перед вызовом Записать() сравните старое значение с новым. В масштабе 100 000 записей пропуск даже 20% неизмененных строк даст ощутимый прирост скорости.

Заключение

Мы разобрали комплексный подход к решению проблемы медленной записи данных в 1С. Подведем итоги: для максимальной производительности используйте ОбменДанными.Загрузка = Истина, объединяйте записи в пакеты по 500-2000 штук внутри транзакций и отдавайте предпочтение работе с Наборами записей вместо индивидуальных Менеджеров записи. Применяя эти правила, вы сможете обрабатывать миллионы строк данных без ущерба для комфортной работы пользователей.

← На главную