Как максимально быстро перезаполнить регистр сведений, подчиненный регистратору

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

В практике разработчика 1С часто возникают задачи, связанные с обработкой огромных массивов данных. Одной из самых трудоемких операций является полное перезаполнение или массовое обновление записей в регистрах сведений, которые имеют режим записи «Подчинение регистратору». Традиционный подход, требующий создания набора записей с обязательным отбором по регистратору для каждого документа, при больших объемах (миллионы строк) работает крайне медленно из-за огромного количества транзакций и обращений к СУБД.

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

Проблема: Почему подчиненные регистры пишутся медленно?

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

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

Если у нас 100 000 регистраторов, система выполнит 100 000 отдельных транзакций записи. Каждая транзакция сопровождается блокировками, обновлением индексов и проверками ссылочной целостности. Это создает колоссальную нагрузку на сервер и дисковую подсистему. Давайте выясним, как это ускорить.

Решение 1: Использование возможностей платформы 8.3.26+

Это самый современный и предпочтительный метод. Начиная с версии платформы 8.3.26, разработчики 1С радикально изменили подход к записи данных в регистры. Теперь менеджер регистра сведений получил возможность записывать таблицу значений целиком, игнорируя обязательную привязку к конкретному регистратору в рамках одного вызова.

Рассмотрим подробнее, как это работает. Раньше метод Записать() у менеджера регистра использовался в основном для независимых регистров. Теперь для подчиненных регистров мы можем подготовить одну большую таблицу значений со всеми данными (включая колонку Регистратор и НомерСтроки) и передать её платформе.

Рассмотрим пример кода:


// Предположим, у нас есть ТаблицаНовыхДанных с колонками:
// Период, Регистратор, НомерСтроки, Измерение1, Ресурс1...

Менеджер = РегистрыСведений.НазваниеВашегоРегистра;
Менеджер.Записать(ТаблицаНовыхДанных);

Почему это быстрее? Платформа сама оптимизирует процесс записи на уровне СУБД, стараясь минимизировать количество команд INSERT и максимально эффективно использовать пакетную запись. При этом важно помнить про режим ОбменДанными.Загрузка = Истина, который отключает лишние проверки и обработчики событий, что дает дополнительный прирост скорости.

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

Решение 2: Прямая работа с СУБД (SQL методы)

Если вы работаете на более ранних версиях платформы или объем данных настолько велик, что штатные средства не справляются, можно рассмотреть вариант прямого взаимодействия с таблицами SQL. Посмотрим на этот процесс с точки зрения администратора БД.

Выясним причину замедления на уровне SQL — это обновление индексов. При массовой вставке строк SQL-сервер вынужден перестраивать индексы (деревья) для каждой новой порции данных. Для ускорения процесса опытные разработчики используют следующую стратегию:

  1. Отключение индексов: На время загрузки некластерные индексы таблицы регистра переводятся в состояние DISABLED.
  2. Очистка таблицы: Вместо медленного DELETE используется команда TRUNCATE TABLE (если нужно очистить весь регистр) или DELETE порциями без ведения детального лога транзакций.
  3. Bulk Insert: Данные загружаются через специализированные механизмы быстрой вставки (например, утилита bcp или INSERT INTO ... SELECT из временной таблицы).
  4. Включение индексов: После завершения загрузки индексы перестраиваются (Rebuild).

Внимание! Этот метод является «грязным» с точки зрения архитектуры 1С. Платформа может не «узнать» об изменениях в базе (кэш объектов на сервере приложений останется старым). После таких манипуляций необходимо как минимум перезапустить сеансы или очистить кэш, а также убедиться в целостности итогов, если регистр связан с какими-либо расчетами.

Решение 3: Параллелизация через фоновые задания

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

Основная идея заключается в том, чтобы разбить весь объем регистраторов на «пакеты» и обрабатывать каждый пакет в отдельном потоке. Разберем по шагам:

  1. Формируем массив всех уникальных Регистраторов, которые требуют перезаписи.
  2. Делим этот массив на N частей (например, по 1000 регистраторов в каждой), где N зависит от количества ядер процессора на сервере.
  3. Запускаем фоновые задания, передавая в каждое свой пакет регистраторов.

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


// Код в основной обработке
МассивРегистраторов = ПолучитьСписокРегистраторовДляОбновления();
КоличествоПотоков = 8;
РазмерПорции = Цел(МассивРегистраторов.Количество() / КоличествоПотоков);

Для НомерПотока = 1 По КоличествоПотоков Цикл
    ПараметрыЗадания = Новый Структура("ПорцияРегистраторов", ПолучитьЧастьМассива(МассивРегистраторов, НомерПотока, РазмерПорции));
    ФоновыеЗадания.Выполнить("ВашМодуль.ВыполнитьПерезаписьПорции", ПараметрыЗадания);
КонецЦикла;

Важный нюанс: при параллельной записи необходимо избегать взаимных блокировок (deadlocks). Убедитесь, что разные потоки не пытаются одновременно записывать данные, относящиеся к одним и тем же диапазонам индексов или одним и тем же регистраторам.

Решение 4: Архитектурные хитрости и временные таблицы

Иногда быстрее не обновлять существующий регистр, а «подменить» его содержимое. Рассмотрим сценарий использования временных объектов.

Если задача позволяет, можно создать независимый вспомогательный регистр с аналогичной структурой. Загрузка в независимый регистр всегда происходит быстрее, так как платформа позволяет использовать метод Загрузить() для набора записей без привязки к регистратору (даже в старых версиях). После того как данные подготовлены в промежуточном регистре, мы можем перенести их в основной регистр порциями или через SQL-запрос на уровне сервера, что будет значительно быстрее, чем построчная обработка в коде 1С.

Также не забывайте про оптимизацию состава измерений. Каждое лишнее измерение — это дополнительный индекс. Если какие-то данные можно вынести в Ресурсы или Реквизиты, это существенно повысит скорость записи, так как индексы по ним не строятся.

Выводы и рекомендации

Подведем итог нашему анализу:

  1. Если ваша платформа выше 8.3.26 — используйте новый метод Менеджер.Записать(ТаблицаЗначений). Это самый безопасный и быстрый способ.
  2. При работе на старых версиях — используйте параллелизацию через фоновые задания, разбивая данные по регистраторам.
  3. Для экстремальных объемов данных (десятки и сотни миллионов строк) допустимо использование прямых SQL-запросов с временным отключением индексов, но только при наличии квалифицированного администратора БД и понимания рисков.
  4. Всегда устанавливайте флаг ОбменДанными.Загрузка = Истина перед записью набора, чтобы минимизировать накладные расходы платформы.

Выбор конкретного метода зависит от версии вашей системы, доступности SQL-консоли и критичности времени простоя базы. Сочетание этих подходов позволит вам сократить время перезаписи регистра с часов до считанных минут.

← На главную