Как эффективно заполнить ресурс в регистре сведений при большом объеме данных (1.5 млн записей)?

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

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

Анализ причин падения фоновых заданий

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

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

Выбор оптимальной стратегии: один поток или многопоточность?

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

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

Оптимизация кода записи через МенеджерЗаписи

Если специфика регистра требует использования объекта РегистрСведенийМенеджерЗаписи (например, когда измерения разные для каждой строки), необходимо придерживаться следующих правил. Выясним, как ускорить этот процесс:

Во-первых, обязательно используйте свойство ОбменДанными.Загрузка = Истина. Это отключает проверки в модулях набора записей, что критично при массовых операциях. Рассмотрим пример правильной организации цикла:


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

Использование Наборов Записей для ускорения

Посмотрим на ситуацию с точки зрения производительности СУБД. Запись через НаборЗаписей обычно быстрее, чем через МенеджерЗаписи, так как платформа оптимизирует SQL-запросы. Даже если у вас независимый регистр и вы меняете только одну запись, запись набора из одной строки может работать эффективнее в массовом цикле.

Однако помните: при записи набора записей с установленным отбором, 1С сначала удаляет все записи, соответствующие отбору, а затем вставляет новые. Для независимого регистра без отборов это может быть опасно, поэтому всегда проверяйте установку фильтров.

Работа с транзакциями и логом SQL

Разберем важный нюанс: размер порции в одной транзакции. Не стоит пытаться записать все 1.5 млн записей в одной транзакции — это гарантированно приведет к ошибке переполнения лога транзакций. Опытным путем установлено, что оптимальный размер порции составляет от 1 000 до 10 000 записей.

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

  1. Выбираем порцию данных (например, 5000 записей), требующих обновления.
  2. Открываем одну транзакцию.
  3. Циклом записываем все 5000 изменений.
  4. Фиксируем транзакцию.
  5. Делаем небольшую паузу (Sleep) или просто переходим к следующей итерации, позволяя системе "вдохнуть" и освободить ресурсы.

Обработка составных типов и NULL в запросах

Часто причиной того, что фоновые задания "молотят вхолостую", является неверный запрос в начале обработки. Выясним причину на примере составных типов. Если вы ищете записи, где ресурс еще не заполнен, и используете обращение через точку (например, БизнесПроцесс.Шаблон), помните:

В языке запросов 1С обращение через точку к пустому значению составного типа или ссылке приведет к значению NULL. Проверка ЕСТЬ NULL — это эффективный способ отсечь уже обработанные данные на уровне СУБД. Это гораздо быстрее, чем выгружать все 1.5 млн записей в память 1С и проверять их в коде.


ВЫБРАТЬ ПЕРВЫЕ 10000
    Рег.Измерение1,
    Рег.Измерение2
ИЗ
    РегистрСведений.ИмяРегистра КАК Рег
ГДЕ
    Рег.НовыйРесурс = ЗНАЧЕНИЕ(Справочник.ЗначенияРесурса.ПустаяСсылка)
    И НЕ Рег.БизнесПроцесс.Ссылка ЕСТЬ NULL

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

Чтобы избежать падения всей системы при заполнении 1.5 млн записей, придерживайтесь следующих советов:

Таким образом, для успешного решения задачи нам не обязательно использовать экстремальное распараллеливание. Сочетание правильной работы с транзакциями, использования ОбменДанными.Загрузка и грамотно составленных запросов для выбора порций данных позволяет обработать 1.5 млн записей за разумное время (от 30 минут до пары часов) без риска остановки работы пользователей.

← На главную