Как влияет на производительность добавление реквизита на форму через точку в 1С?

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

При разработке интерфейсов на управляемых формах в «1С:Предприятии 8» часто возникает соблазн быстро вывести нужные данные сопряженного объекта, просто перетащив реквизит «через точку» (например, Объект.Контрагент.ИНН или Элементы.Список.ТекущаяСтрока.Номенклатура.Артикул). Это невероятно удобно, так как платформа берет на себя всю работу по получению и отображению данных. Однако за этим удобством скрываются сложные внутренние механизмы платформы, которые могут существенно замедлить работу системы и перегрузить оперативную память сервера приложений rphost. В этой статье мы подробно разберем, что происходит «под капотом» платформы при разыменовании ссылочных полей, как это работает в таблицах и динамических списках, а также рассмотрим альтернативные и более производительные варианты решения подобных задач.

Что происходит на сервере при обращении «через точку» в одиночном реквизите формы

Рассмотрим ситуацию, когда мы выносим на управляемую форму реквизит связанного справочника через точку от основного реквизита. Например, на форме документа у нас есть поле Контрагент (тип СправочникСсылка.Контрагенты), и мы добавляем на форму поле Объект.Контрагент.ИНН. Выясним, какую цепочку действий выполняет платформа:

  1. Полное чтение объекта из СУБД: Платформа «1С:Предприятие» спроектирована на основе объектной модели. Это значит, что для получения значения всего одного реквизита (в нашем примере — ИНН) сервер приложений выполняет запрос к базе данных и считывает весь объект целиком. Будут прочитаны абсолютно все реквизиты контрагента, включая его наименование, адреса, телефоны и даже табличные части, если они у него есть.
  2. Помещение объекта в кэш: Считанный из базы данных объект со всеми его потрохами помещается в объектный кэш сессии на сервере (в оперативную память процесса rphost). При повторном обращении к любому реквизиту этого контрагента платформа уже не будет обращаться к СУБД, а возьмет данные из кэша.
  3. Оптимизация передачи данных на клиент (Сервер → Клиент): Здесь платформа применяет оптимизацию трафика. Несмотря на то что сервер прочитал объект целиком, на сторону тонкого клиента или браузера передается только значение конкретного запрашиваемого реквизита (наш ИНН) и текстовое представление ссылки. Весь «тяжелый» объект на клиент не уходит.

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

Как работает разыменование ссылок в табличных частях и списках

Теперь проанализируем ситуацию, которая вызывает наибольшее количество споров: добавление реквизита через точку в колонку таблицы (например, отображение колонки Номенклатура.Артикул в табличной части документа). Здесь поведение платформы различается в зависимости от типа таблицы.

А. Обычные таблицы (Табличные части документов, Коллекции значений)

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

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

Б. Динамические списки

В динамических списках (которые функционируют на базе системы компоновки данных — СКД) обращение через точку работает совершенно иначе. Здесь платформа не использует объектный кэш напрямую для отображения строк, а трансформирует обращение через точку в SQL-запрос:

Для оптимизации динамических списков при работе с составными типами данных обязательно нужно использовать явное приведение типов в тексте запроса списка. Рассмотрим пример:


ВЫБРАТЬ
    Ссылка,
    Дата,
    Номер,
    ВЫРАЗИТЬ(ДокументОснование КАК Документ.ЗаказПокупателя).Менеджер КАК МенеджерОснования
ИЗ
    Документ.РеализацияТоваровУслуг

Использование конструкции ВЫРАЗИТЬ гарантирует, что СУБД сделает соединение только с одной таблицей (таблицей заказов покупателей), что сохранит высокую скорость работы динамического списка.

Проблема «залипания» и актуальности данных в кэше

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

  1. Пользователь открыл форму документа, где в табличной части отображается колонка Номенклатура.Артикул через точку. Данные считались из БД и закэшировались.
  2. В этот же момент другой пользователь (или фоновое задание интеграции) изменяет Артикул у одной из этих номенклатур в базе данных.
  3. У первого пользователя на форме по-прежнему будет отображаться старое значение артикула, так как платформа берет его из своего локального кэша формы.

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

Разбор альтернативного подхода: скрытые колонки для расчетов

В Сообщении 8 обсуждается интересный практический кейс: разработчик предлагает добавить в табличную часть формы невидимую колонку через точку (например, скрытый реквизит номенклатуры), чтобы затем выполнять по ней сложные расчеты итогов на клиенте (в процедурах без контекстного вызова сервера — НаКлиентеНаСервереБезКонтекста). Проанализируем плюсы и минусы такого архитектурного решения.

Преимущества решения:

Недостатки и подводные камни:

Как делать правильно: лучшие практики и оптимизация

Проанализировав все особенности поведения платформы, сформулируем свод правил для разработки высокопроизводительных систем:

1. Работа с единичными реквизитами на форме

Если вам нужно вывести реквизит связанного объекта на форму (например, телефон контрагента), оцените «тяжесть» объекта контрагента. Если объект содержит сотни реквизитов или тяжелые ТЧ, откажитесь от вывода через точку. Вместо этого добавьте на форму обычный реквизит формы (не реквизит объекта!) соответствующего типа и заполняйте его при событии ПриЧтенииНаСервере или при изменении основного реквизита.

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


&НаСервере
Процедура ЗаполнитьРеквизитыКонтрагента()
    
    // Получаем только ИНН и КПП, не считывая весь справочник Контрагенты
    СтруктураРеквизитов = ОбщегоНазначения.ЗначенияРеквизитовОбъекта(Объект.Контрагент, "ИНН, КПП");
    
    РеквизитФормыИНН = СтруктураРеквизитов.ИНН;
    РеквизитФормыКПП = СтруктураРеквизитов.КПП;
    
КонецПроцедуры

2. Работа с табличными частями

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

  1. Добавление обычных текстовых/числовых колонок в таблицу формы (не связанных с данными через точку).
  2. Пакетное заполнение этих колонок при открытии формы (событие ПриСозданииНаСервере или ПриЧтенииНаСервере) с помощью одного запроса, который выбирает только нужные поля для всех строк ТЧ.
  3. Обновление конкретной строки при изменении пользователем ключевого поля (например, номенклатуры) с помощью быстрого внеконтекстного серверного вызова.

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


&НаСервере
Процедура ЗаполнитьДополнительныеКолонкиТЧ()
    
    ТаблицаНоменклатуры = Объект.Товары.ВыгрузитьКолонку("Номенклатура");
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Номенклатура.Ссылка КАК Номенклатура,
        |	Номенклатура.Артикул КАК Артикул
        |ИЗ
        |	Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |	Номенклатура.Ссылка В(&МассивНоменклатуры)";
        
    Запрос.УстановитьПараметр("МассивНоменклатуры", ТаблицаНоменклатуры);
    РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    
    // Быстро заполняем колонки на форме сопоставлением
    Для Каждого СтрокаТЧ Из Объект.Товары Цикл
        СтрокаДанных = РезультатЗапроса.Найти(СтрокаТЧ.Номенклатура, "Номенклатура");
        Если СтрокаДанных <> Неопределено Тогда
            СтрокаТЧ.АртикулФормы = СтрокаДанных.Артикул;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Такой подход гарантирует, что из базы данных будут прочитаны строго необходимые поля (только артикул), СУБД отработает моментально, а оперативная память сервера не будет захламлена тяжелыми неиспользуемыми объектами.

← На главную