Почему запрос в 1С на PostgreSQL выполняется в сотни раз медленнее при изменении одного параметра и как это исправить?

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

В процессе эксплуатации высоконагруженных систем на базе 1С и PostgreSQL программисты часто сталкиваются с аномальным поведением планировщика запросов. Типичная ситуация: один и тот же запрос при значении параметра Истина выполняется мгновенно (менее секунды), а при значении Ложь — "повелевает" системе задуматься на несколько минут, хотя итоговая выборка может быть пустой. Рассмотрим подробно, почему это происходит в современных редакциях PostgreSQL (13, 15) и как стабилизировать производительность системы.

Анализируем причину деградации производительности

Проанализируем ситуацию, описанную в теме. При использовании PostgreSQL 13 и выше механизм построения планов запросов стал более чувствителен к значениям параметров. В 1С это часто проявляется при работе с виртуальными таблицами СрезПоследних. Когда мы меняем булево условие (например, ПостояннаяЧасть), планировщик может радикально изменить стратегию выполнения.

Основные факторы, влияющие на такое замедление:

  1. Проблема Parameter Sniffing: PostgreSQL может построить "общий" план запроса (generic plan), который эффективен для одного набора данных, но катастрофичен для другого. В новых версиях алгоритм переключения между кастомным и общим планом был изменен, что иногда приводит к выбору неоптимального пути.
  2. Оценка стоимости Nested Loop: Планировщик ошибочно полагает, что из одной таблицы придет 1 строка, и выбирает вложенный цикл (Nested Loop). На деле строк оказывается десятки тысяч, и время выполнения растет экспоненциально. Это отчетливо видно в плане запроса: actual time=690.114..723.713 rows=74024 при ожидании rows=1.
  3. Изменения в планировщике версий 13+: В свежих редакциях оптимизатор стал агрессивнее использовать Incremental Sort и Nested Loops там, где раньше (в 12 версии) использовался более стабильный Hash Join.

Решение 1: Использование специализированных сборок PostgreSQL от 1С

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

  1. Специфические патчи: Сборка от 1С содержит исправления для оптимизатора, которые корректируют расчет стоимости соединений Nested Loop и Merge Join. Это заставляет планировщик адекватнее оценивать затраты на выполнение запросов, характерных для структуры метаданных 1С.
  2. Предустановленные настройки: В таких сборках параметры seq_page_cost, random_page_cost и cpu_operator_cost уже настроены так, чтобы минимизировать вероятность ошибочного выбора вложенных циклов на больших объемах данных.

Если вы используете ванильный PostgreSQL или сторонние сборки (например, стандартный PGPro без специфической настройки под 1С), риск столкнуться с "плавающими" планами запросов значительно выше.

Решение 2: Оптимизация запроса через временные таблицы

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

Вместо того чтобы соединять виртуальную таблицу СрезПоследних напрямую с другими таблицами или документами, вынесем получение среза в отдельный этап. Рассмотрим пример кода:


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

Почему это работает: Когда мы помещаем данные во временную таблицу, 1С выполняет отдельный SQL-запрос. Планировщику PostgreSQL гораздо проще построить оптимальный план для простой выборки из регистра. После этого, имея физическую временную таблицу (с индексами!), СУБД выполняет соединение максимально эффективно. Это исключает ситуацию, когда фильтр по параметру "пробрасывается" внутрь сложного подзапроса виртуальной таблицы некорректно.

Решение 3: Тонкая настройка параметров PostgreSQL

Если вы системный администратор и наблюдаете подобные проблемы, проанализируем настройки сервера. Иногда проблему можно нивелировать изменением конфигурации postgresql.conf:

  1. Модуль AQO (Adaptive Query Optimization): В некоторых случаях он может помочь, но иногда именно он становится причиной нестабильности. Попробуйте отключить его для теста: DROP EXTENSION aqo;.
  2. Параллелизм: Если запросы тормозят в многопоточном режиме, попробуйте ограничить количество рабочих процессов. Рассмотрим установку параметров:
    
    ALTER SYSTEM SET max_parallel_workers_per_gather TO 0;
    ALTER SYSTEM SET max_parallel_workers TO 0;
    
    Это отключит параллельное выполнение, что часто делает планы запросов 1С более предсказуемыми.
  3. Глубина статистики: Увеличьте параметр default_statistics_target со 100 до 500 или 1000 для таблиц регистров сведений и выполните ANALYZE. Это даст планировщику больше информации о распределении данных по измерениям.

Выясним причину через план запроса

При возникновении проблем всегда ловите План выполнения (Explain Analyze). В предоставленном автором плане мы видим Sort Method: quicksort Memory: 6807kB. Если памяти work_mem недостаточно, PostgreSQL начнет использовать диск для сортировки, что замедлит запрос еще сильнее. Убедитесь, что параметры памяти (shared_buffers, work_mem) установлены в соответствии с рекомендациями 1С для вашего объема ОЗУ.

Подведем итог: Ситуация с замедлением в 700 раз обусловлена не "поломкой" новых версий PostgreSQL, а изменением логики оптимизатора, который стал требовательнее к качеству статистики и структуре запросов. Использование сборок от 1С и временных таблиц — это "золотой стандарт" разработки, позволяющий избежать подобных проблем вне зависимости от версии СУБД.

← На главную