При работе с крупными конфигурациями, такими как 1С:ERP Управление предприятием 2.4 или 1С:Управление торговлей 11, многие разработчики сталкиваются с катастрофическим падением производительности при открытии форм документов, связанных с ценообразованием. Одной из самых проблемных зон является документ Регистрация цен номенклатуры поставщиков. В некоторых случаях время ожидания открытия формы или выполнения команды заполнения может превышать 60 секунд даже при небольшом количестве строк.
В этой статье мы подробно проанализируем ситуацию, выясним причины «чудной» скорости работы типовых механизмов и рассмотрим конкретные шаги по оптимизации системы.
Рассмотрим конкретный пример. Имеется база ERP 2.4.5.82, развернутая в файловом режиме на локальной машине (процессор i3, HDD, 16 Гб ОЗУ). При выполнении типового запроса из общего модуля УстановкаЦенСервер замер производительности показывает, что метод Запрос.Выполнить() занимает 64 секунды, что составляет более 70% всего времени работы модуля. Проблема усугубляется тем, что даже на серверных мощностях с использованием MS SQL Server открытие документа может занимать 3–5 секунд, что для оперативной работы пользователя также является неприемлемым.
Проанализируем текст запроса, который генерируется системой. Основная тяжесть приходится на блок пересчета коэффициентов упаковок. Система пытается универсально обработать все возможные варианты измеряемых величин (вес, объем, площадь, длина, мощность и т.д.). Рассмотрим фрагмент кода, порождающий такие тормоза:
ВЫБОР
КОГДА ЦеныНоменклатуры.Упаковка.ТипИзмеряемойВеличины = ЗНАЧЕНИЕ(Перечисление.ТипыИзмеряемыхВеличин.Вес)
И ЦеныНоменклатуры.Номенклатура.ВесИспользовать
И ЕСТЬNULL(ЦеныНоменклатуры.Упаковка.Знаменатель, 0) <> 0
И ЕСТЬNULL(ЦеныНоменклатуры.Номенклатура.ВесЕдиницаИзмерения.Числитель, 0) <> 0
ТОГДА ВЫРАЗИТЬ(...)
...
КОНЕЦ
Выясним причину, почему этот код работает медленно. Проблема кроется в обращении к полям через точку в языке запросов 1С. Каждое такое обращение, например, ЦеныНоменклатуры.Номенклатура.ВесЕдиницаИзмерения.Числитель, заставляет платформу создавать неявное левое соединение (LEFT JOIN) на уровне SQL-запроса. В данном случае мы видим цепочку из трех-четырех соединений для каждого условия КОГДА. Учитывая количество условий в блоке ВЫБОР, итоговый SQL-текст превращается в «простыню» с десятками соединений к справочникам номенклатуры, упаковок и единиц измерения.
Для файлового режима работы 1С это становится критическим фактором, так как встроенный движок БД не обладает столь мощным оптимизатором запросов, как полноценные СУБД. Наличие медленного жесткого диска (HDD) дополнительно замедляет процесс из-за интенсивной работы с временными таблицами во время выполнения этих соединений.
Важно понимать, что в модуле УстановкаЦенСервер текст запроса не является статичным. Он собирается программно с использованием функций справочника УпаковкиЕдиницыИзмерения. Рассмотрим, как это происходит:
Запрос.Текст = СтрЗаменить(Запрос.Текст,
"&ТекстЗапросаКоэффициентУпаковки1",
Справочники.УпаковкиЕдиницыИзмерения.ТекстЗапросаКоэффициентаУпаковки(
"ЦеныНоменклатуры.Упаковка",
"ЦеныНоменклатуры.Номенклатура"));
Метод ТекстЗапросаКоэффициентаУпаковки генерирует огромную конструкцию, которая повторяется в запросе многократно (для каждой временной таблицы и основного выбора). Это создает избыточную нагрузку на компилятор запросов СУБД.
Прежде чем приступать к рефакторингу кода, необходимо обеспечить системе адекватную среду выполнения. 1С:ERP — это тяжелое решение, которое практически не предназначено для полноценной работы в файловом режиме, за исключением задач разработки или демонстрации на пустых базах. Рассмотрим обязательные шаги:
Если аппаратных средств недостаточно, перейдем к оптимизации самого запроса. Вместо того чтобы обращаться через точки к свойствам номенклатуры и упаковок в основном запросе, мы можем предварительно собрать все данные во временную таблицу. Разберем этот алгоритм по шагам:
Шаг 1. Создадим временную таблицу, содержащую только уникальные ссылки на номенклатуру и упаковки, участвующие в документе.
Шаг 2. Отдельным запросом выберем все необходимые реквизиты (Числитель, Знаменатель, ТипИзмеряемойВеличины) в плоскую таблицу, выполнив соединения явно и только один раз.
Шаг 3. В основном запросе соединимся с этой подготовленной таблицей по ссылке. Это избавит СУБД от необходимости строить план с десятками левых соединений в теле цикла или блока ВЫБОР.
Проанализируем пример схемы такой оптимизации:
// Помещаем данные во временную таблицу с явным соединением
ВЫБРАТЬ
Упаковки.Ссылка КАК Упаковка,
Упаковки.Числитель КАК Числитель,
Упаковки.Знаменатель КАК Знаменатель,
Номенклатура.ВесИспользовать КАК ВесИспользовать
ПОМЕСТИТЬ ВТ_ПараметрыУпаковок
ИЗ
Справочник.УпаковкиЕдиницыИзмерения КАК Упаковки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
ПО Упаковки.Владелец = Номенклатура.Ссылка
ГДЕ
Упаковки.Ссылка В (&СписокУпаковок)
ИНДЕКСИРОВАТЬ ПО
Упаковка;
Многие специалисты по ERP рекомендуют радикальный подход: если типовая форма документа работает слишком медленно из-за перегруженности серверными вызовами и сложной логики «при открытии», стоит вынести процесс загрузки и расчета цен во внешнюю обработку. Рассмотрим преимущества этого метода:
Ситуация с медленной работой Установки цен в ERP 2.4 — это классический пример конфликта между универсальностью типового решения и производительностью конкретной внедренной базы. Для решения проблемы мы рекомендуем:
LEFT JOIN в SQL-трейсе.Таким образом, комбинируя аппаратное усиление и грамотный рефакторинг запросов (избавление от обращений через точку в пользу временных таблиц), можно добиться сокращения времени работы документа в десятки раз.