При разработке интеграций, загрузок данных из внешних систем или сложных регламентных обработок программисты 1С часто сталкиваются с лавинообразным ростом журнала регистрации (ЖР). Лог-файлы базы данных стремительно увеличиваются в размерах, свободное место на диске сервера заканчивается, а производительность всей системы падает из-за постоянных дисковых операций записи. Особенно остро эта проблема проявляется при циклической построчной записи в регистры сведений.
В рамках этой статьи мы подробно разберем, почему возникает данная проблема, рассмотрим наиболее эффективные пути ее решения, проанализируем примеры кода и предостережем от опасных способов оптимизации, которые могут парализовать работу всей информационной базы.
Рассмотрим исходный код, с которым столкнулся автор темы. В цикле происходит обработка полученных данных и запись каждой строки отдельно:
Для каждого Строка из ТаблицаОстатков цикл
// ... получение ссылки номенклатуры
МенеджерЗаписи = РегистрыСведений.ИнформативныеОстаткиТоваровПоМагазинам.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Магазин = СкладРЦ;
МенеджерЗаписи.Номенклатура = Номенклатура;
МенеджерЗаписи.Количество = Строка.Остаток;
МенеджерЗаписи.Записать();
КонецЦикла;
Проанализируем поведение платформы 1С при таком подходе. Объект МенеджерЗаписи предназначен для интерактивной или атомарной работы с одной конкретной записью регистра. Когда мы вызываем метод Записать() у менеджера записи в цикле, платформа на каждую итерацию совершает следующие действия:
_$Data$_.Update или _$Data$_.New).Если в вашей таблице 100 000 строк, платформа инициирует 100 000 отдельных транзакций СУБД и создаст 100 000 записей в файле журнала регистрации (1Cv8.lgd). Это порождает колоссальную транзакционную нагрузку и приводит к моментальному раздуванию логов.
Самый простой, безопасный и идеологически правильный способ оптимизации — замена индивидуального менеджера записи на работу с набором записей (РегистрСведенийНаборЗаписей). Разберем по шагам, как это работает.
Когда мы записываем данные через набор, платформа производит запись всей пачки данных в рамках одной транзакции. В результате в журнал регистрации помещается всего одно единственное событие об изменении данных для всего набора, независимо от того, сколько строк (10, 100 или 10 000) в нем содержалось. Это позволяет сократить количество логов в тысячи раз.
Рассмотрим пример того, как переписать исходный код автора на использование набора записей с отбором по ключевому измерению (складу):
ТаблицаОстатков = ПолучитьОстатокСкладов(COMСоединение);
// Создаем пустой набор записей регистра
НаборЗаписей = РегистрыСведений.ИнформативныеОстаткиТоваровПоМагазинам.СоздатьНаборЗаписей();
// Устанавливаем жесткий отбор по измерению, чтобы перезаписать данные только по конкретному складу
НаборЗаписей.Отбор.Магазин.Установить(СкладРЦ);
// Наполняем набор записей в цикле новыми данными
Для каждого Строка из ТаблицаОстатков цикл
СтрокаГуидНоменклатуры = COMСоединение.String(Строка.Номенклатура.UUID());
ГуидНоменклатуры = Новый УникальныйИдентификатор(СтрокаГуидНоменклатуры);
Номенклатура = Справочники.Номенклатура.ПолучитьСсылку(ГуидНоменклатуры);
НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Магазин = СкладРЦ;
НоваяЗапись.Номенклатура = Номенклатура;
НоваяЗапись.Количество = Строка.Остаток;
КонецЦикла;
// Записываем весь набор за один раз.
// Параметр "Истина" указывает на необходимость замещения существующих в базе данных записей, подходящих под отбор
НаборЗаписей.Записать(Истина);
Важное примечание по оптимизации памяти: Если объем импортируемых данных исчисляется сотнями тысяч или миллионами строк, единовременная загрузка их всех в один набор записей может привести к переполнению оперативной памяти сервера приложений 1С. В таких случаях рекомендуется производить запись порциями (батчами) по 1000 - 5000 строк, используя соответствующие отборы.
В ходе обсуждения на форумах часто всплывает программный метод полного отключения регистрации событий изменения данных с помощью встроенного языка 1С. Рассмотрим этот метод подробнее, чтобы понять его критические недостатки.
Для программного отключения логирования используется следующий код:
Процедура РегистрироватьСобытияЖурналаРегистрацииПриМассовойЗаписи(Регистрировать) Экспорт
УстановитьПривилегированныйРежим(Истина);
ИспользованиеСобытия = Новый ИспользованиеСобытияЖурналаРегистрации();
ИспользованиеСобытия.Использование = Регистрировать; // Истина или Ложь
// Отключаем фиксацию создания новых данных
УстановитьИспользованиеСобытияЖурналаРегистрации("_$Data$_.New", ИспользованиеСобытия);
// Отключаем фиксацию изменения существующих данных
УстановитьИспользованиеСобытияЖурналаРегистрации("_$Data$_.Update", ИспользованиеСобытия);
УстановитьПривилегированныйРежим(Ложь);
КонецПроцедуры
Внимание! Использование данного метода в транзакционных бизнес-процессах крайне опасно по нескольким причинам:
УстановитьИспользованиеСобытияЖурналаРегистрации отключает ведение логов не для конкретного сеанса или фонового задания, а глобально для всей информационной базы. Если в момент работы вашей обработки другой пользователь создаст критически важный документ или изменит цены, эти действия также не попадут в журнал регистрации, что нарушит требования информационной безопасности.1Cv8.lgd). Вызов метода изменения настроек логирования накладывает монопольную блокировку на этот файл. В результате все остальные пользователи системы, чьи сеансы в этот момент пытаются что-то записать в ЖР (даже просто зафиксировать факт авторизации или ошибку), начнут аварийно завершать работу по таймауту блокировки или зависать.Если после перехода на наборы записей журнал регистрации все равно увеличивается слишком быстро, проанализируем ситуацию с точки зрения архитектуры хранения данных.
Часто в регистры сведений пытаются записывать "транзитные" или слишком динамические данные (например, ежеминутные GPS-координаты машин, сырые логи технологических обменов, подробные временные статусы). Запись таких данных штатными средствами 1С перегружает не только ЖР, но и транзакционный лог основной СУБД (MS SQL, PostgreSQL).
Рассмотрим альтернативные системные подходы:
.elf) настройте разделение файлов по дням или неделям и архивацию старых периодов. Для нового формата SQLite (.lgd) настройте автоматическое сокращение журнала через планировщик задач операционной системы с использованием штатных консольных утилит 1С или регулярное выполнение команды VACUUM для сжатия базы данных логов.Для того чтобы ваша массовая обработка не приводила к аварийному росту журнала регистрации, всегда придерживайтесь золотого правила разработки в 1С: никогда не выполняйте построчную запись данных через Менеджер Записи в цикле. Перевод алгоритмов на использование объекта НаборЗаписей с установкой точечных отборов решает проблему раздувания ЖР в 95% случаев, ускоряет работу программы в десятки раз и гарантирует бесконфликтную работу пользователей в многопользовательском режиме.