Оптимальное получение реквизитов через точку: почему это замедляет систему и как сделать правильно

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

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

Механика обращения через точку: что видит СУБД

Когда мы пишем ИНН = КонтрагентСсылка.ИНН;, платформа 1С не просто запрашивает одно поле из таблицы базы данных. На уровне SQL-запроса выполняется команда SELECT * FROM ... WHERE ID = .... Это означает, что из базы данных извлекаются абсолютно все реквизиты объекта: примитивные типы (строки, числа), ссылки на другие объекты, а также инициализируются структуры для работы с табличными частями.

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

Кэширование объектов: спасение или ловушка?

Одним из аргументов в пользу «точки» является механизм кэширования (так называемый L2-кэш объектов). Выясним, как он работает. Когда объект считывается через точку впервые, он попадает в кэш сервера приложений на определенное время (по умолчанию около 20 минут). Если в этом же сеансе вы снова обратитесь к любому реквизиту этой же ссылки, данные будут взяты из памяти, и повторного запроса к СУБД не будет.

Рассмотрим пример, где точка может быть эффективнее запроса:


// Если в цикле 1000 итераций, но всего 5 уникальных счетов
Для Каждого Проводка Из Проводки Цикл
    Если Проводка.Счет.Количественный Тогда // Счет закэшируется после первого обращения
        // Логика обработки
    КонецЕсли;
КонецЦикла;

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

Проблема N+1 и антипаттерны в коде

Самая распространенная ошибка — использование обращения через точку в теле цикла по большой выборке данных. В программировании это называется проблемой «N+1». Вместо того чтобы получить данные для всех 100 записей одним запросом, система выполняет 100 отдельных запросов к базе данных.

Посмотрим на пример крайне неоптимального кода, который часто встречается в «самописных» обменах данными:


Для Каждого Док Из МассивДокументов Цикл
    // Здесь Док.Ссылка - это обращение через точку в цикле
    Для Каждого Рекв Из Док.Ссылка.Метаданные.Реквизиты Цикл
        Если Док[Рекв.Имя] <> Док.Ссылка[Рекв.Имя] Тогда 
            Изменен = Истина;
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

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

Использование функций БСП как «золотая середина»

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

Рассмотрим, почему это лучше:

  1. Точечный запрос: Внутри функции динамически строится запрос ВЫБРАТЬ Реквизит ИЗ Таблица ГДЕ Ссылка = &Ссылка. СУБД возвращает только нужные данные.
  2. Отсутствие кэширования объекта целиком: Память rphost не забивается ненужными данными других полей.
  3. Универсальность: Вам не нужно каждый раз писать текст запроса вручную, достаточно вызвать функцию.

Пример правильного получения данных:


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

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

Когда все же стоит использовать запросы?

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

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

  1. Выгрузите все необходимые ссылки в массив.
  2. Напишите запрос, где условием будет ГДЕ Ссылка В (&МассивСсылок).
  3. Выполните запрос один раз и получите результат в таблицу значений или соответствие.
  4. Используйте полученные данные в цикле, обращаясь к локальной коллекции в памяти.

Проанализируем пример оптимизации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Номенклатура.Ссылка КАК Ссылка,
    |   Номенклатура.Артикул КАК Артикул
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |   Номенклатура.Ссылка В (&МассивСсылок)";

Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
ТаблицаДанных = Запрос.Выполнить().Выгрузить();

// Теперь данные можно быстро найти, не обращаясь к базе в цикле
Для Каждого Строка Из ТаблицаДанных Цикл
    // Работаем с данными из таблицы
КонецЦикла;

Итоги и рекомендации

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

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

← На главную