Как ускорить запись данных в SQL-версии 1С до уровня файловой базы

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

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

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

Для начала проанализируем ситуацию. В файловом варианте 1С напрямую пишет в файл базы данных. В серверном же варианте между кодом и физическим диском стоят дополнительные звенья: сервер приложений 1С и SQL Server. Основная задержка при выполнении метода НаборЗаписей.Записать() в цикле обычно связана с журналом транзакций (LDF). SQL Server обязан гарантировать сохранность данных, поэтому после каждой записи он дожидается подтверждения от дисковой подсистемы о том, что транзакция зафиксирована в логе. Если мы пишем данные мелкими порциями в цикле, количество таких физических обращений к диску становится критическим.

Решение 1: Использование явных транзакций

Если мы будем записывать каждый набор записей отдельно, 1С и SQL будут создавать «неявную» транзакцию на каждую операцию. Это создает огромную нагрузку на дисковую подсистему. Рассмотрим, как можно ускорить процесс, объединив записи в одну логическую операцию.

Выясним причину: при использовании метода НачатьТранзакцию() мы сообщаем SQL Server, что все последующие изменения нужно накопить и зафиксировать «одним махом». Рассмотрим пример кода:


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

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

Решение 2: Параллельная запись через фоновые задания

Как показала практика обсуждения, это один из самых эффективных способов. Если один поток записи в SQL работает медленнее, чем в файловой базе, мы можем запустить 10 потоков одновременно. SQL Server прекрасно справляется с параллелизмом, в отличие от файловой базы, которая в этот момент просто «встанет» из-за блокировок файла.

Разберем по шагам, как реализовать многопоточную запись:

  1. Разобьем весь объем данных на несколько независимых пакетов.
  2. Для каждого пакета запустим фоновое задание, которое будет выполнять запись.
  3. Внутри фонового задания обязательно используем транзакции для каждого пакета.

Пример инициализации фоновых заданий:


// Разделяем массив ключей на части
РазмерПорции = 1000;
Для НомерПорции = 1 По КоличествоПорций Цикл
    ПараметрыЗапуска = Новый Массив;
    ПараметрыЗапуска.Добавить(ДанныеПорции);
    
    ФоновыеЗадания.Выполнить("МойОбщийModule.ЗаписатьПорциюДанных", ПараметрыЗапуска);
КонецЦикла;

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

Решение 3: Тонкая настройка SQL Server

Посмотрим на настройки самой СУБД. Зачастую SQL «из коробки» не оптимизирован под специфику 1С. Рассмотрим ключевые параметры:

  1. Модель восстановления (Recovery Model): Для массовой загрузки данных рекомендуется временно (или постоянно, если не требуется восстановление на момент времени) перевести базу в режим Simple. Это значительно сократит объем записываемой информации в журнал транзакций.
  2. Параметр MAXDOP (Max Degree of Parallelism): Для 1С рекомендуется устанавливать значение 1 (или в некоторых случаях 2 или 4, но не оставлять 0). Это предотвращает ситуацию, когда один сложный запрос «отъедает» все ресурсы процессора, мешая другим операциям.
  3. Обновление статистики: Если вы записываете данные в пустую или сильно изменившуюся таблицу, SQL может строить неверные планы запросов. Выполните команду UPDATE STATISTICS после или во время загрузки крупных порций.
  4. Размещение LDF-файла: Файл лога должен лежать на самом быстром диске (SSD/NVMe). Скорость записи в лог — это «бутылочное горлышко» SQL Server при записи данных.

Решение 4: Режим монопольного доступа и блокировки

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

Также убедитесь, что у регистра сведений установлен режим Управляемый для блокировок. Это позволит SQL Server более гибко управлять доступом к данным, не блокируя всю таблицу целиком при записи одного набора, что критично для многопоточной работы, описанной в Решении 2.

Заключение

Подведем итог. Чтобы SQL-база не уступала файловой в скорости записи, необходимо:

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

← На главную