Как быстро создать 5 миллионов (и более) номенклатурных позиций в 1С?

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

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

Исходная задача, с которой мы начинаем, – это создание 5 миллионов (а в идеале 50 миллионов) товаров, при этом текущая скорость составляет всего около 30 штук в секунду, что абсолютно неприемлемо. Мы видим, что даже на серверной базе производительность может быть неустойчивой, а сама система может вылетать с ошибкой "Неисправимая ошибка", создав лишь малую часть запланированных данных.

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

Оптимизация на уровне кода 1С

Медленная запись объектов в 1С часто связана с большим количеством внутренних проверок, обработчиков событий и механизмов, которые запускаются при каждом вызове метода Записать(). Для массовых операций эти проверки избыточны и замедляют процесс. Мы можем обойти большинство из них.

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

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

    Рассмотрим, как это реализовать:

    
    // Перед циклом устанавливаем флаг загрузки
    СпрНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
    СпрНоменклатура.ОбменДанными.Загрузка = Истина;
    
    Для Сч = 1 По КолСтр Цикл
        АртикулСтр = "А1-" + Сч;
        ТоварСтр = АртикулСтр;
    
        // Создаем новый элемент
        СпрНоменклатура.Новый(); // Очищает данные и готовится к созданию нового элемента
    
        СпрНоменклатура.Наименование = ТоварСтр;
        СпрНоменклатура.НаименованиеПолное = ТоварСтр;
        СпрНоменклатура.НаименованиеИностранное = ТоварСтр;
        СпрНоменклатура.Артикул = АртикулСтр;
        // ... (остальные реквизиты) ...
    
        // Важно: если нужен код, а ОбменДанными.Загрузка = Истина,
        // мы должны присвоить его вручную, иначе он не сгенерируется.
        СпрНоменклатура.Код = ТоварСтр; 
    
        СпрНоменклатура.Записать();
    КонецЦикла;
    

    Мы видим, что после установки ОбменДанными.Загрузка = Истина, система перестанет автоматически генерировать коды для новых элементов. Мы должны будем присваивать код вручную. Это небольшое усложнение, которое с лихвой окупается многократным увеличением скорости записи.

  2. Пакетная запись в транзакциях

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

    Мы рекомендуем начинать новую транзакцию, записывать в нее определенное количество элементов (например, от 500 до 5000, в зависимости от конфигурации и производительности), а затем фиксировать транзакцию. Размер пакета (количество элементов на одну транзакцию) необходимо подбирать опытным путем, делая замеры времени, чтобы найти оптимальный баланс.

    
    НачатьТранзакцию();
    Попытка
        Для Сч = 1 По КолСтр Цикл
            // ... (код создания и заполнения элемента номенклатуры) ...
    
            СпрНоменклатура.Записать();
    
            Если (Сч % 1000 = 0) Тогда // Фиксируем транзакцию каждые 1000 элементов
                ЗафиксироватьТранзакцию();
                НачатьТранзакцию();
            КонецЕсли;
        КонецЦикла;
    
        // Фиксируем оставшиеся элементы, если цикл завершился не на кратном 1000
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ВызватьИсключение;
    КонецПопытки;
    

    Этот подход позволяет снизить накладные расходы на управление транзакциями и увеличить общую пропускную способность записи данных.

  3. Отключение версионирования объектов

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

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

    
    СпрНоменклатура.ДополнительныеСвойства.Вставить("ПропуститьЗаписьВерсииОбъекта", Истина);
    СпрНоменклатура.Записать();
    
  4. Привилегированный режим

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

    
    // В начале процедуры загрузки
    УстановитьПривилегированныйРежим(Истина);
    
    // ... (ваш код загрузки) ...
    
    // В конце процедуры, если необходимо, вернуть обратно
    // УстановитьПривилегированныйРежим(Ложь);
    
  5. Отключение записи в Журнал Регистрации (ЖР)

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

    Настройки для журнала регистрации доступны в режиме "Предприятие" через раздел "Администрирование" -> "Обслуживание" -> "Настройка журнала регистрации". Здесь мы можем установить "Не регистрировать" для событий, связанных с изменением данных, на время массовой загрузки.

Архитектурные решения и масштабирование

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

  1. Использование Регистров сведений для каталогов

    Если 50 миллионов позиций — это общий каталог товаров (например, автозапчастей со всеми аналогами), из которых активно продается или закупается лишь небольшая часть (50-70 тысяч), то хранить весь каталог в справочнике Номенклатура может быть излишним и вредным для производительности оперативного учета.

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

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

  2. Многопоточность и фоновые задания

    Для ускорения массовых операций на серверной базе мы можем использовать многопоточность. Разделим задачу на несколько параллельных потоков, которые будут выполняться на сервере 1С. Это достигается с помощью механизмов фоновых заданий или методов ДлительныеОперации.ВыполнитьПроцедуру().

    Каждый поток будет обрабатывать свою часть данных. Это требует более сложной реализации, но позволяет максимально задействовать ресурсы сервера (ядра CPU).

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

    
    // Примерная структура для фонового задания
    Процедура ЗагрузитьПакетНоменклатуры(НачальныйСчетчик, КонечныйСчетчик) Экспорт
        // Устанавливаем привилегированный режим
        УстановитьПривилегированныйРежим(Истина);
        // Создаем новый элемент справочника для каждого фонового задания
        СпрНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
        СпрНоменклатура.ОбменДанными.Загрузка = Истина;
    
        НачатьТранзакцию();
        Попытка
            Для Сч = НачальныйСчетчик По КонечныйСчетчик Цикл
                АртикулСтр = "А1-" + Сч;
                ТоварСтр = АртикулСтр;
    
                // Установка ссылки нового объекта с уникальным GUID
                // Это важно при параллельной записи, чтобы избежать коллизий
                СпрНоменклатура.УстановитьСсылкуНового(ПолучитьСсылку(Новый UUID())); 
    
                СпрНоменклатура.Наименование = ТоварСтр;
                СпрНоменклатура.НаименованиеПолное = ТоварСтр;
                СпрНоменклатура.НаименованиеИностранное = ТоварСтр;
                СпрНоменклатура.Артикул = АртикулСтр;
                // ... (остальные реквизиты) ...
                СпрНоменклатура.Код = ТоварСтр; // Ручная генерация кода
    
                СпрНоменклатура.Записать();
    
                Если (Сч % 1000 = 0) Тогда
                    ЗафиксироватьТранзакцию();
                    НачатьТранзакцию();
                КонецЕсли;
            КонецЦикла;
            ЗафиксироватьТранзакцию();
        Исключение
            ОтменитьТранзакцию();
            ВызватьИсключение;
        КонецПопытки;
    КонецПроцедуры
    
    // Пример вызова в основном коде
    // (должен быть адаптирован для реального вызова ФоновыхЗаданий)
    // Количество потоков = N;
    // Размер пакета = КолСтр / N;
    // Для i = 1 по N Цикл
    //    ЗапуститьФоновоеЗадание("ЗагрузитьПакетНоменклатуры", НачалоПакета, КонецПакета);
    // КонецЦикла;
    

Прямой доступ к СУБД (экспертный уровень)

Самый быстрый, но и самый рискованный метод – это прямой доступ к таблицам базы данных через SQL-запросы. Этот метод позволяет вставлять миллионы записей за считанные минуты. Однако, он требует глубоких знаний внутренней структуры базы данных 1С, является нарушением лицензионного соглашения 1С и может привести к неработоспособности базы данных при неправильном использовании или изменении внутренней структуры данных в новых версиях платформы.

Мы рекомендуем использовать этот метод только в тестовых целях, при полном осознании всех рисков и наличии высокой квалификации. Для создания уникальных идентификаторов (GUID) в SQL мы можем использовать функцию NEWID().

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

Пример генерации нового GUID в MS SQL Server:

SELECT NEWID();

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

Оптимизация серверной инфраструктуры и СУБД

Помимо программного кода, значительное влияние на скорость массовой загрузки оказывает производительность серверного оборудования и настройки СУБД.

  1. Производительность дисковой подсистемы

    Дисковый ввод-вывод является одним из главных "узких мест" при работе с большими объемами данных. Мы настоятельно рекомендуем использовать твердотельные накопители (SSD) или, что еще лучше, NVMe-диски для размещения файлов базы данных 1С и СУБД. Это может многократно увеличить скорость операций чтения/записи.

  2. Достаточный объем оперативной памяти

    Недостаток оперативной памяти на сервере 1С и сервере СУБД приводит к постоянному обращению к диску для подкачки данных (свопингу), что катастрофически снижает производительность. Мы должны обеспечить достаточный объем RAM (например, 64-128 ГБ для средних систем, 256 ГБ и более для высоконагруженных).

  3. Настройка СУБД (MS SQL Server, PostgreSQL)

    Мы должны правильно настроить СУБД. Для MS SQL Server рекомендуется использовать кодировку Cyrillic_General_CI_AS на уровне сервера и базы данных. Ограничьте максимальный объем памяти, потребляемый сервером SQL, чтобы он не "съедал" всю память, оставляя ее для сервера 1С.

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

  4. Настройка сервера 1С

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

    Также важно отключить энергосберегающие режимы в BIOS сервера и операционной системе (например, EIST, Intel SpeedStep, C1E), чтобы обеспечить максимальную производительность процессора.

  5. Актуализация платформы

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

Диагностика и устранение ошибок (например, "Неисправимая ошибка")

Ошибка "На сервере 1С:Предприятия произошла неисправимая ошибка" часто связана с недостатком ресурсов (памяти) или чрезмерно большими транзакциями, особенно в файловой базе. Мы должны внимательно проанализировать следующие моменты:

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

← На главную