При переходе с файловой базы на клиент-серверный вариант работы разработчики нередко сталкиваются с ситуацией, когда типовые формы начинают открываться в разы медленнее. Яркий пример — форма подбора номенклатуры в документах продажи (конфигурация «1С:Управление торговлей 11»), которая в файловом режиме может открываться за 2–4 секунды, а на сервере СУБД (особенно PostgreSQL) зависать на 30–40 секунд и более. Разберем по шагам причины такого поведения платформы и детально рассмотрим варианты решения проблемы.
Для обнаружения причины зависания формы в клиент-серверном режиме первым делом необходимо запустить отладку на сервере 1С:Предприятия. Если замерять только клиентский контекст, мы увидим лишь общее время вызова открытия формы без детализации серверных процедур.
Проанализируем выполнение процедур модуля формы и подключенных общих модулей при вызове события ПриСозданииНаСервере. В типовом решении узким местом часто оказывается вызов функции проверки актуальности цен на будущие даты — ЕстьЗначенияЦенПозжеДатыПодбора(). Внутри этой функции платформа выполняет следующий запрос:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
МАКСИМУМ(ЦеныНоменклатурыСрезПоследних.Период) КАК Период
ИЗ
РегистрСведений.ЦеныНоменклатуры.СрезПоследних(, ВидЦены В (&ВидыЦен)) КАК ЦеныНоменклатурыСрезПоследних
На первый взгляд запрос прост, однако именно он при определенных объемах данных приводит к многосекундным задержкам.
Рассмотрим механизм формирования виртуальной таблицы на уровне СУБД:
СрезПоследних без указания конкретной даты транслируется платформой 1С во вложенный SQL-запрос с группировкой по всем измерениям регистра (Номенклатура, Характеристика, Упаковка и т.д.). СУБД вынуждена сканировать физическую таблицу регистра, находить максимальный период для каждой возможной комбинации аналитики, формируя полный актуальный срез на текущую дату, и только после этого вычислять внешний МАКСИМУМ(Период).HashAggregate). При большом объеме записей операция превышает лимит оперативной памяти (work_mem) и сбрасывается во временные файлы на диск, что многократно увеличивает время выполнения.ВЫБРАТЬ РАЗРЕШЕННЫЕ заставляет платформу внедрять условия ограничений прав доступа на уровне записей прямо в подзапросы среза, еще сильнее утяжеляя план выполнения.Посмотрим на практическую цель исходного кода: разработчикам типовой конфигурации требовалось лишь определить, есть ли вообще в базе цены, установленные на дату позже момента подбора. Для решения этой задачи совершенно не требуется рассчитывать срез последних по всей номенклатуре.
Перепишем запрос к реальной таблице регистра сведений, используя условие существования хотя бы одной подходящей записи (механизм быстрого выхода):
ВЫБРАТЬ ПЕРВЫЕ 1
1 КАК ПолеОтбора
ИЗ
РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
ГДЕ
ЦеныНоменклатуры.ВидЦены В (&ВидыЦен)
И ЦеныНоменклатуры.Период > &ДатаПодбора
Разберем преимущества такой конструкции:
ПЕРВЫЕ 1 позволяет СУБД прервать сканирование индексов сразу при обнаружении первой совпавшей строки. Время выполнения сокращается с десятков секунд до сотых долей миллисекунды.ВидЦены в регистре сведений является либо первым по порядку в метаданных, либо для него включено свойство Индексировать. Это позволит СУБД задействовать прямой поиск по индексу.Если в бизнес-процессах предприятия ввод цен будущими датами не используется, можно полностью отключить ресурсоемкую проверку в общем модуле:
ЕстьЗначенияЦенПозжеДатыПодбора() жестко возвращаем значение Ложь без отправки запросов в базу данных. В результате подбор товара строится по быстрому срезу последних без фильтрации по дате.УстановкаЦенНоменклатуры в процедуре ПередЗаписью() или через подписку на событие.Если в информационной базе активно используются ограничения на уровне записей (RLS), служебные запросы для проверки дат могут работать значительно дольше. Если проверка носит чисто системный характер и не возвращает пользователю закрытую информацию, снимем накладные расходы ограничений прав:
УстановитьПривилегированныйРежим(Истина);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 1
| 1
|ИЗ
| РегистрСведений.ЦеныНоменклатуры КАК ЦеныНоменклатуры
|ГДЕ
| ЦеныНоменклатуры.ВидЦены В (&ВидыЦен)
| И ЦеныНоменклатуры.Период > &ДатаПодбора";
Запрос.УстановитьПараметр("ВидыЦен", ВидыЦен);
Запрос.УстановитьПараметр("ДатаПодбора", ДатаПодбора);
ЕстьЦены = НЕ Запрос.Выполнить().Пустой();
УстановитьПривилегированныйРежим(Ложь);
Привилегированный режим исключает внедрение громоздких условий безопасности в тело SQL-запроса, снижая нагрузку на планировщик запросов СУБД.
Если доработка конфигурации по каким-либо причинам нежелательна (база находится на полной поддержке), необходимо привести в порядок настройки сервера баз данных:
Seq Scan вместо использования индексов). Запустим сбор статистики через консоль администрирования или утилиту командной строки:
vacuumdb --analyze ИмяБазыДанных
postgresql.conf. Значения autovacuum_vacuum_scale_factor (рекомендуется 0.05–0.1) и autovacuum_analyze_scale_factor (рекомендуется 0.02–0.05) должны позволять СУБД своевременно обновлять статистику по часто модифицируемым таблицам.work_mem (например, до 32–64 Мб в зависимости от общего объема ОЗУ сервера), чтобы операции сортировки и хеширования не сбрасывались во временные файлы на диск.Комбинация грамотной оптимизации алгоритма 1С и регулярного обслуживания базы данных позволяет сократить время открытия формы подбора товара с десятков секунд до мгновенного отклика.