В практике разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда один и тот же запрос в клиент-серверном варианте работает с разной скоростью для разных пользователей. Особенно остро эта проблема проявляется при использовании PostgreSQL и виртуальных таблиц СрезПоследних. Рассмотрим ситуацию, когда у большинства сотрудников отчет или форма открываются за доли секунды, а у одного конкретного пользователя выполнение затягивается на несколько минут.
Проанализируем причины этого явления, замерим производительность системы, разберем технические нюансы взаимодействия 1С с PostgreSQL и предложим несколько эффективных способов решения проблемы.
Основная сложность заключается в том, как платформа 1С транслирует обращение к виртуальной таблице СрезПоследних в SQL-запрос. Когда мы задаем параметр Период, система не просто обращается к таблице итогов (как она делает это без указания периода), а формирует сложную конструкцию с подзапросами или использованием функции MAX(Period) для каждой комбинации измерений. В связке с RLS (Row-Level Security), ограничивающим доступ на уровне записей, итоговый SQL-запрос превращается в «массивное дерево» с множеством соединений.
Разберем, почему проблема может быть локальной (только у одного пользователя):
Seq Scan). Это происходит из-за специфического распределения данных, доступных именно этому сотруднику.Nested Loop (вложенные циклы), выполняя тяжелый поиск среза для каждой строки.Одним из самых надежных способов обойти неоптимальную работу виртуальных таблиц является самостоятельное написание логики получения последних записей через консоль запросов. Это позволяет нам явно контролировать использование индексов. Рассмотрим, как это сделать программно.
Вместо обращения к РегистрСведений.ИмяРегистра.СрезПоследних(&Период, ...), мы можем составить запрос следующим образом:
// Сначала подберем максимальные даты для каждого измерения
ВЫБРАТЬ
Т.Измерение1,
Т.Измерение2,
МАКСИМУМ(Т.Период) КАК Период
ПОМЕСТИТЬ ВТ_МаксДаты
ИЗ
РегистрСведений.ИмяРегистра КАК Т
ГДЕ
Т.Период <= &Период
И Т.Измерение1 В (ВЫБРАТЬ Ссылка ИЗ ВТ_СписокСсылок) // Фильтр из вашей задачи
СГРУППИРОВАТЬ ПО
Т.Измерение1,
Т.Измерение2
;
// Теперь получим сами данные, соединившись с основной таблицей регистра
ВЫБРАТЬ
Т_Данные.*
ИЗ
ВТ_МаксДаты КАК Даты
ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ИмяРегистра КАК Т_Данные
ПО Даты.Измерение1 = Т_Данные.Измерение1
И Даты.Измерение2 = Т_Данные.Измерение2
И Даты.Период = Т_Данные.Период
Почему это работает быстрее? Такой подход разбивает одну сложную операцию на две простые. PostgreSQL гораздо легче построить оптимальный план для группировки по индексированным полям, чем для коррелированного подзапроса внутри виртуальной таблицы.
Если вы используете временные таблицы перед соединением со срезом последних (как в примере автора темы), важно «подсказать» СУБД реальный объем данных. В 1С для этого используется метод МенеджерВременныхТаблиц, но на уровне SQL важно, чтобы была выполнена команда ANALYZE.
Рассмотрим рекомендации по работе с временными таблицами в 1С для PostgreSQL:
Если у вас есть доступ к управлению сервером СУБД, проанализируем настройки, которые могут влиять на «тормоза» конкретных пользователей. Проблема часто кроется в механизме GEQO (Genetic Query Optimizer) и JIT.
Посмотрим на важные параметры:
jit = off — попробуйте временно отключить JIT-компиляцию. Если запрос у «проблемного» пользователя сразу ускорится, значит, время уходило на компиляцию сложного SQL-дерева, порожденного RLS.plan_cache_mode = force_custom_plan — эта настройка заставляет PostgreSQL каждый раз строить план заново, не пытаясь использовать универсальный кэшированный план. Это лечит проблему «отравленного» кэша для конкретных параметров.from_collapse_limit и join_collapse_limit — увеличение этих значений (например, до 20) может помочь планировщику лучше обрабатывать запросы с большим количеством автоматических соединений от RLS.Разберем более радикальный, но эффективный способ, упомянутый в обсуждении. Если регистр сведений очень большой (база более 100 Гб), а срез последних нужен часто, можно разделить данные на оперативные и архивные.
Создайте два идентичных регистра:
РегистрОперативный — содержит записи только по активным объектам (например, по незакрытым сделкам или актуальным ценам за последний месяц).РегистрАрхивный — хранит всю историю.Большинство транзакционных запросов в документах перенаправьте на РегистрОперативный. Срез последних по нему будет работать мгновенно из-за малого объема данных. Регламентное задание может переносить старые записи из оперативного регистра в архив раз в сутки.
Чтобы окончательно выяснить причину, необходимо проанализировать, какой именно SQL-запрос уходит на сервер для «быстрого» и «медленного» пользователя. Настройте сбор ТЖ с событием DBPOSTGRS. В логах вы увидите текст запроса и, что самое важное, план выполнения (Explain Plan).
Проанализируем план выполнения:
Sequential Scan по большим таблицам. Это признак того, что индексы не используются.Actual time в узлах плана. Если общее время 5 минут, а сумма времен всех узлов — 1 секунда, значит, остальное время СУБД «думала» над планом (проблема JIT).Index Scan на тяжелый Hash Join.Подводя итог, можно сказать, что связка 1С и PostgreSQL требует более внимательного отношения к написанию запросов, чем MS SQL. Использование виртуальных таблиц с параметрами в условиях RLS — это «зона риска». Лучшей практикой будет либо выполнение таких запросов в привилегированном режиме, либо их ручная декомпозиция на простые составляющие.