Что выбрать для хранения миллионов записей с табличной частью: справочник с ТЧ, регистр сведений или подчиненный справочник?

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

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

В этой статье мы подробно разберем три основных подхода к решению этой задачи, проанализируем внутренние механизмы платформы 1С и СУБД, а также выясним, какое решение будет наиболее отказоустойчивым и быстрым на больших объемах данных.

Вариант 1. Использование справочника с табличной частью

На первый взгляд, это самое очевидное и простое решение. Мы создаем справочник СправочникСсылка.Уведомления, добавляем в него реквизиты шапки, а список получателей храним в табличной части Адресаты. Давайте проанализируем, к каким последствиям это приведет при работе с миллионами строк.

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

Если в вашей табличной части содержится 200–300 строк, то при обычном обходе выборки в цикле или при проверке условий в фоновом процессе сервер 1С будет гонять гигабайты лишних данных из СУБД в оперативную память сервера приложений и на клиент. Это вызовет колоссальные просадки производительности и перерасход памяти.

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

Ограничения СУБД. Табличная часть физически хранится в отдельной таблице СУБД с жестким кластерным индексом Ссылка + НомерСтроки. Мы никак не можем повлиять на этот индекс. Если нам потребуется быстро находить информацию в разрезе сотрудников (например, построить отчет «Все уведомления по сотруднику Иванову за прошлый год»), СУБД будет вынуждена делать полное сканирование (Table Scan) огромной таблицы ТЧ, что приведет к зависанию отчетов.

Вариант 2. Разделение данных: Справочник (шапка) + Регистр сведений (строки)

Рассмотрим более продвинутый и профессиональный подход. Шапку уведомления мы оставляем в «легком» справочнике СправочникСсылка.Уведомления (в нем нет табличных частей, только базовые реквизиты). А список сотрудников и статусов отправки переносим в независимый регистр сведений РегистрСведений.АдресатыУведомлений.

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

  1. Мгновенное чтение шапки. Обращение к реквизитам справочника-шапки происходит моментально, так как у него нет тяжелой табличной части. Платформа кэширует только легкие реквизиты.
  2. Запись без блокировок. Мы можем обновлять статус доставки для каждого сотрудника по отдельности, не перезаписывая весь объект целиком. Для этого мы записываем точечный набор записей регистра сведений по конкретному измерению. Параллельные процессы записи не будут мешать друг другу.
  3. Гибкое индексирование. Регистр сведений позволяет нам самостоятельно управлять кластерным индексом. Если мы расположим измерения в порядке Сотрудник + Уведомление, то поиск всех писем по конкретному физическому лицу среди сотен миллионов записей СУБД выполнит за миллисекунды (операция Index Seek).
  4. Простая очистка устаревших данных. Рано или поздно база данных переполнится, и нам потребуется удалить историю уведомлений старше трех месяцев. В случае с регистром сведений мы можем быстро очистить старые записи порциями через метод Записать() с установленным отбором, не затрагивая сами объекты справочника. Это минимизирует нагрузку на СУБД.

Рассмотрим пример кода для записи статуса отправки в такой регистр сведений:


// Пример записи статуса отправки в независимый регистр сведений
НаборЗаписей = РегистрыСведений.АдресатыУведомлений.СоздатьНаборЗаписей();
НаборЗаписей.Отбор.Уведомление.Установить(СсылкаНаУведомление);
НаборЗаписей.Отбор.Сотрудник.Установить(ТекущийСотрудник);

НоваяЗапись = НаборЗаписей.Добавить();
НоваяЗапись.Уведомление = СсылкаНаУведомление;
НоваяЗапись.Сотрудник = ТекущийСотрудник;
НоваяЗапись.СтатусОтправки = Перечисления.СтатусыОтправки.Доставлено;
НоваяЗапись.ДатаОтправки = ТекущаяДата();

НаборЗаписей.Записать(Истина); // Записываем только конкретную строку без блокировки всего объекта

Вариант 3. Использование подчиненного справочника

Еще одна интересная альтернатива — это связка «Справочник-родитель + Подчиненный справочник». В данном случае шапка уведомления — это элемент родительского справочника, а каждая строка получателя — это элемент подчиненного справочника СправочникСсылка.АдресатыУведомлений, у которого стандартный реквизит Владелец ссылается на родительское уведомление.

Этот вариант очень похож по своим плюсам на вариант с регистром сведений:

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

Сравнительный анализ решений

Давайте сопоставим все три архитектурных решения в виде итоговой таблицы для наглядности:

Критерий сравнения Справочник с ТЧ Справочник + Регистр сведений Подчиненный справочник
Скорость чтения шапки Низкая (читается вся ТЧ) Высокая (ТЧ отсутствует) Высокая (ТЧ отсутствует)
Блокировки при записи строк Высокие (блокируется весь объект) Минимальные (запись по набору) Минимальные (запись элемента)
Возможность ссылки на строку Нет (у строк ТЧ нет GUID) Нет (нужен искусственный ключ) Да (полноценная ссылка)
Удобство очистки истории Очень низкое (нужна перезапись объектов) Высокое (удаление наборов записей) Среднее (удаление объектов)
Размер базы данных в СУБД Средний Минимальный (оптимальные таблицы) Максимальный (избыточные метаданные)

Выводы и рекомендации

Подводя итог нашему анализу, мы можем дать следующие рекомендации по проектированию систем под высокие нагрузки:

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

Для хранения истории событий, уведомлений и логов идеальным решением является архитектурный шаблон «Легкий справочник для шапки + Независимый регистр сведений для строк». Он обеспечивает максимальную скорость работы СУБД, гибкое индексирование по любым измерениям и простую процедуру архивации или удаления устаревших записей.

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

← На главную