Стоит ли разрабатывать собственный журнал регистрации в 1С: плюсы, минусы и альтернативные решения

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

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

Архитектурные проблемы стандартного журнала регистрации 1С

Прежде чем принимать решение о написании собственного велосипеда, давайте разберем, почему стандартный ЖР начинает тормозить. Начиная с версии платформы 1С 8.3.5, по умолчанию используется новый формат журнала регистрации на базе СУБД SQLite (файлы с расширением .lgd). Однако встроенный драйвер платформы работает с этой базой данных весьма специфически:

  1. Отсутствие автоматического сжатия: В платформе не реализован полноценный механизм автоматического выполнения команды VACUUM для SQLite. Это означает, что даже после удаления старых записей из ЖР размер файла на диске не уменьшается, а структура индексов дефрагментируется. При достижении файлом размера более 1–2 ГБ скорость чтения падает в геометрической прогрессии.
  2. Блокировки при записи: При высокой интенсивности работы пользователей запись в SQLite-файл может вызывать микрозадержки и транзакционные ожидания, снижая общую производительность системы.

Альтернативный шаг: Первое, что мы рекомендуем сделать для ускорения работы — попробовать вернуть старый последовательный текстовый формат журнала регистрации (файлы с расширением .lgp). Сделать это можно через параметры запуска конфигуратора или утилиты администрирования. Текстовый формат делит логи по периодам и записывает данные последовательно без использования механизмов СУБД, что в ряде высоконагруженных систем значительно повышает скорость записи и решает проблему взаимных блокировок при фиксации логов.

С какими подводными камнями придется столкнуться при написании своего ЖР?

Если вы всё же решили писать собственный журнал регистрации внутри базы 1С (например, используя РегистрСведений и ПодпискиНаСобытия на уровне объектов), приготовьтесь к решению сложнейших архитектурных проблем:

1. Проблема транзакционности (Ахиллесова пята самописных логов)

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

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

2. Проблема взаимных блокировок (Deadlocks)

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

3. Стремительный рост базы данных

Давайте проведем простой математический расчет. Предположим, средний размер XML-представления измененных реквизитов одного документа составляет 100 Кб. Если в день в базе проводится 1000 документов, и каждый документ перезаписывается в среднем 3 раза в процессе согласования, то только за один день ваш регистр логов вырастет на:

100 Кб * 1000 * 3 = 300 МБ в день!

За год база вырастет более чем на 100 ГБ только за счет таблиц логирования. Это приведет к усложнению резервного копирования, росту стоимости дискового пространства и замедлению обслуживания всей СУБД.

Альтернатива №1: Платформенный механизм «История данных» (Data History)

Прежде чем писать код, давайте проанализируем штатные возможности современной платформы 1С 8.3. В платформу встроен мощный оптимизированный механизм «История данных». Его ключевые особенности:

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

Альтернатива №2: Версионирование объектов из БСП

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

Альтернатива №3: Современный корпоративный стандарт (ClickHouse / ELK)

В крупных HighLoad-системах ведение логов внутри основной транзакционной базы считается критической ошибкой проектирования. Современный подход заключается в выгрузке логов во внешние аналитические системы:

Практический пример: Создание легковесного логирования изменений через подписки на события

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

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

Пример программного кода для сравнения и фиксации изменений:


Процедура ПередЗаписьюОбъектаДляЛогирования(Источник, Отказ) Экспорт
    
    // Исключаем работу механизма при загрузке данных (например, через обмен)
    Если Источник.ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    // Если объект новый, фиксируем только факт создания
    Если Источник.ЭтоНовый() Тогда
        ЗаписатьСобытиеВЛог(Источник, "Создание", "Объект создан впервые");
        Возврат;
    КонецЕсли;
    
    // Получаем старую версию объекта напрямую из базы данных для сравнения реквизитов
    СсылкаНаОбъект = Источник.Ссылка;
    СтарыйОбъект = СсылкаНаОбъект.ПолучитьОбъект();
    
    Если СтарыйОбъект = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Изменения = Новый ТекстовыйДокумент;
    МетаданныеОбъекта = Источник.Метаданные();
    
    // Проверяем изменение стандартных реквизитов и реквизитов объекта
    Для Каждого Реквизит Из МетаданныеОбъекта.Реквизиты Цикл
        ИмяРеквизита = Реквизит.Имя;
        Если Источник[ИмяРеквизита] <> СтарыйОбъект[ИмяРеквизита] Тогда
            Изменения.ДобавитьСтроку(СтрШаблон("Изменен реквизит '%1'. Старое значение: '%2', Новое значение: '%3'", 
                ИмяРеквизита, СокрЛП(СтарыйОбъект[ИмяРеквизита]), СокрЛП(Источник[ИмяРеквизита])));
        КонецЕсли;
    КонецЦикла;
    
    // Если зафиксированы изменения, записываем их в оптимизированный регистр сведений
    Если Изменения.КоличествоСтрок() > 0 Тогда
        ЗаписатьСобытиеВЛог(Источник, "Изменение", Изменения.ПолучитьТекст());
    КонецЕсли;

КонецПроцедуры

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

Подведем итоги

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

  1. Попробуйте перевести текущий журнал регистрации на старый формат .lgp (текстовый) для ускорения чтения и записи логов.
  2. Если требуется детальное версионирование полей объектов — протестируйте встроенную платформенную «Историю данных» или подсистему версионирования БСП.
  3. Для крупных высоконагруженных систем настройте трансляцию логов во внешнюю базу ClickHouse.
  4. Пишите собственный регистр логов через подписки на события только в том случае, если вам нужно отслеживать строго ограниченный набор реквизитов у небольшого количества видов документов.
← На главную