При работе с крупными базами данных на платформе 1С:Предприятие разработчики часто сталкиваются с проблемой резкого падения производительности при проведении тяжелых документов, таких как Расчет себестоимости выпуска (РСС) в УПП 1.3 или аналогичных механизмов в ERP. Особенно ярко это проявляется при попытке очистить старые движения и записать новые (например, наборы от 150 000 до 500 000 строк). Операция отмены проведения или перезаписи пустого набора может занимать часы, хотя раньше те же объемы обрабатывались за считанные минуты.
В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем неочевидные механизмы взаимодействия 1С с СУБД MS SQL Server и предложим комплексные методы оптимизации как на уровне программного кода, так и на уровне настроек базы данных.
Регистр бухгалтерии спроектирован на уровне СУБД гораздо сложнее, чем обычные регистры накопления или сведений. На уровне MS SQL он состоит не из одной таблицы, а из целого ряда связанных сущностей:
_AccRg<n> — основная таблица, где хранятся реквизиты проводки, период, регистратор, счета дебета и кредита, а также числовые показатели (сумма, количество)._AccRgED<n> (Extra Dimensions) — таблица значений субконто. Вся аналитика по субконто хранится отдельно от основных движений.Когда мы вызываем метод Записать() для набора записей, СУБД вынуждена выполнять каскадные операции INSERT и DELETE одновременно во всех этих таблицах, а также обновлять связанные с ними индексы. Если на счетах, по которым делаются движения, настроено много субконто (особенно имеющих составной тип данных), объем таблицы аналитики увеличивается экспоненциально. При очистке старых проводок СУБД тратит колоссальное время на поиск и каскадное удаление строк по внешним ключам в таблице _AccRgED.
Если для регистра бухгалтерии настроен обмен данными (даже если активных узлов немного), при записи набора записей платформа 1С пытается зарегистрировать изменения для каждого узла. Это приводит к массовой вставке записей в служебную таблицу регистрации изменений вида _AccRgChngRRef.
Давайте разберем по шагам, как решить эту проблему программно. Перед выполнением тяжелых регламентных расчетов имеет смысл принудительно отключать авторегистрацию изменений на время записи движений:
// Отключаем авторегистрацию изменений для оптимизации записи
Движения.Хозрасчетный.ОбменДанными.Получатели.АвтоЗапись = Ложь;
// Если логика системы позволяет не регистрировать движения вообще
Движения.Хозрасчетный.ОбменДанными.Загрузка = Истина;
Это позволит избежать паразитной нагрузки на СУБД, связанной с обслуживанием таблиц планов обмена во время формирования сотен тысяч проводок.
При записи очень большого объема данных в одну транзакцию MS SQL Server автоматически производит эскалацию блокировок — вместо блокировки отдельных строк блокируется вся таблица регистра. Это вызывает колоссальные задержки и конфликты блокировок.
Для решения этой проблемы отлично подходит метод записи данных порциями. Давайте рассмотрим пример кода, который позволяет записывать движения пачками (например, по 5 000 строк). Это существенно снижает нагрузку на дисковую подсистему и предотвращает эскалацию блокировок:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Движение = Движения.Добавить();
ЗаполнитьЗначенияСвойств(Движение, Выборка);
// Записываем порциями по достижении лимита
Если Движения.Количество() >= 5000 Тогда
Движения.Записать(Ложь);
// Важно: после записи методом Записать(Ложь) набор необходимо очистить в памяти,
// чтобы не накапливать оперативную память, либо использовать промежуточную выгрузку.
КонецЕсли;
КонецЦикла;
Если Движения.Количество() > 0 Тогда
Движения.Записать(Ложь);
EndIf;
Обратите внимание, что в типовых конфигурациях (например, в УПП 1.3) уже может быть заложена аналогичная константа или переменная, регулирующая размер набора (например, РазмерНабораПриЗаписиРегистровУчетаЗатрат со значением по умолчанию 1000). Проанализируйте ее значение в вашей системе.
Если в регистре бухгалтерии включен режим разделения итогов, СУБД создает в таблицах итогов служебное поле _splitter.
Решение: На время выполнения пакетных регламентных операций (таких как закрытие месяца или расчет себестоимости) рекомендуется временно отключать разделение итогов, а после завершения расчетов — включать его обратно.
Стандартное средство «Тестирование и исправление» (ТИ) в 1С выполняет лишь базовые операции обслуживания. При записи 150 000+ строк в одной транзакции статистика распределения данных в MS SQL мгновенно устаревает. Оптимизатор СУБД начинает строить неверные планы выполнения запросов (например, вместо быстрого поиска по индексу Index Seek начинает выполнять полное сканирование огромной таблицы Table Scan / Index Scan).
Для исправления ситуации настройте на уровне СУБД регулярное обновление статистики с параметром FULLSCAN непосредственно перед выполнением тяжелых транзакций:
-- Пример SQL-запроса для обновления статистики по таблицам регистра бухгалтерии
UPDATE STATISTICS _AccRg640 WITH FULLSCAN, COLUMNS;
UPDATE STATISTICS _AccRgED640 WITH FULLSCAN, COLUMNS;
Замените имя таблицы на фактическое имя таблиц вашего регистра в SQL-базе (его можно узнать с помощью функции ПолучитьСтруктуруХраненияБазыДанных).
Даже если в базе данных во время расчета себестоимости работает один пользователь, включенные механизмы RLS (Record Level Security) накладывают серьезный отпечаток на скорость записи. Платформа при вызове метода Записать() под обычным пользователем неявно генерирует тяжелые запросы с множественными соединениями (JOIN) к таблицам прав доступа.
Решение: Выполняйте все массовые регламентные операции исключительно под пользователем с полными правами, у которого полностью отключен RLS на уровне ролей, либо принудительно инициализируйте привилегированный режим в коде:
УстановитьПривилегированныйРежим(Истина);
// Выполнение тяжелой записи набора
Движения.Хозрасчетный.Записать();
УстановитьПривилегированныйРежим(Ложь);
Очень частой скрытой причиной «зависания» регистра бухгалтерии при записи новых движений является наличие в базе записей с некорректными датами (например, пустая дата 01.01.0001 или даты из далекого будущего). При попытке записать новые проводки система пытается рассчитать итоги, но некорректная дата ломает логику интервального расчета итогов, заставляя СУБД уходить в бесконечный цикл или пересчитывать итоги за всю историю базы данных.
Проверьте регистр на наличие некорректных дат с помощью простого консольного запроса к регистру бухгалтерии и при обнаружении исправьте поврежденные регистраторы.
Запись и удаление сотен тысяч строк в рамках одной транзакции генерируют гигантский объем информации в логе транзакций MS SQL Server (файл .ldf).
Проанализируйте следующие системные параметры:
FULL (Полная модель восстановления), лог транзакций мгновенно разрастается. Переведите базу на время тяжелых расчетов в модель восстановления SIMPLE (Простая).tempdb на максимально быстрый RAM-диск или NVMe SSD существенно увеличивает общую скорость транзакций при записи больших наборов, так как именно в tempdb строятся временные таблицы и промежуточные расчеты СУБД.Применяя данные методы в комплексе, мы сможем добиться кратного ускорения проведения тяжелых документов и полностью избавиться от многочасовых зависаний системы при записи в регистр бухгалтерии.