1С:ERP 2.5 на PostgreSQL: когда планировщик выбирает катастрофический план
500 пользователей, 1С:ERP 2.5 на PostgreSQL - и планировщик периодически выбирает планы, от которых всё встаёт. pg_hint_plan, enable_hashjoin и статистика для 1С: разбираем и даём чеклист.
1С:ERP 2.5 на PostgreSQL - проблемы производительности при большой нагрузке и методы оптимизации
Мы ведём несколько инсталляций 1С:ERP 2.5 на PostgreSQL, и в одной из них примерно раз в неделю прилетает звонок: «всё зависло, пользователи не могут работать». Инсталляция немаленькая - около 500 активных пользователей, оперативных данных в базе на несколько лет, запросов параллельно несколько сотен. И каждый такой эпизод сводился к одному: планировщик PostgreSQL выбрал план, от которого конкретный запрос начинал гнать данные полным сканом или вставал в nested loop на миллионе строк.
Это история про то, как мы с этим разбирались. Без хэппи-энда в духе «и теперь всё работает идеально» - но с набором инструментов, которые реально помогли.
Почему 1С - это особый случай для планировщика
PostgreSQL принимает решение о плане выполнения на основе статистики: количество строк, распределение значений, корреляция данных. Статистику собирает ANALYZE, и в штатной конфигурации PostgreSQL делает это автоматически через autovacuum.
Проблема 1С:ERP в том, что структура данных там специфична. Регистры сведений, регистры накоплений, журнал регистрации - это таблицы, которые растут быстро и неравномерно. За один рабочий день одна таблица может набрать несколько миллионов строк, а потом регламентная задача ночью часть из них удалит. Если ANALYZE не успел снять свежую статистику до пика нагрузки - оценка планировщика по числу строк расходится с реальностью в разы. Иногда на порядки.
Второй момент: 1С генерирует запросы с большим количеством параметров и вложенных подзапросов. Планировщик оценивает их статистически независимо, хотя данные на самом деле коррелированы. Это приводит к тому, что плановая оценка кардинальности промежуточного результата уходит в фантастику, и на это фантастическое число строк строится план.
Что происходит с hash join
Один из типичных сценариев: планировщик выбирает hash join для двух больших регистров, оценив каждый из них в несколько тысяч строк. По факту там оказывается несколько миллионов с каждой стороны. Hash join строит хеш-таблицу в памяти под меньший из операндов - если оценка ошиблась в тысячу раз, хеш-таблица не помещается в work_mem, PostgreSQL начинает писать её на диск батчами, и запрос, который должен был отработать за секунды, идёт минуты.
В pg_stat_statements это выглядит как запрос с нормальным средним временем выполнения, но катастрофическим максимальным. Пока статистика свежая - всё хорошо. Как только она устаревает - следующий аналогичный запрос улетает в туман.
Временное решение через enable_hashjoin = off на уровне сессии или глобально мы пробовали - помогает, но это рубильник, который ломает другие запросы, где hash join реально оптимален. Глобальное отключение не вариант.
pg_hint_plan: точечное управление планами
Расширение pg_hint_plan позволяет встраивать подсказки прямо в текст запроса в виде комментариев. Для 1С это нетривиально: запросы генерируются платформой, и вмешаться в них напрямую нельзя.
Но pg_hint_plan умеет работать через таблицу подсказок hint_plan.hints - соответствие задаётся по нормализованному тексту запроса без параметров. Схема работы:
- Ловим проблемный запрос через
pg_stat_statementsили логи сlog_min_duration_statement. - Берём нормализованный текст (где параметры заменены на
$1,$2...). - Пишем запись в
hint_plan.hintsс указанием нужного плана.
Например, для запроса, где хотим принудить merge join вместо hash join:
INSERT INTO hint_plan.hints (norm_query_string, application_name, hints)
VALUES (
'SELECT ... FROM "ТаблицаA" AS t1 JOIN "ТаблицаB" AS t2 ON ...',
'',
'MergeJoin(t1 t2)'
);
Это работает, но требует аккуратности: нормализованный текст надо получить точно - малейшее расхождение, и хинт не применится. Мы завели отдельный скрипт, который вытаскивает нормализованный текст из pg_stat_statements и сразу готовит INSERT для проверки.
Чеклист настройки PostgreSQL под 1С:ERP с большой нагрузкой
Ниже то, что у нас реально в конфиге и что обосновано:
default_statistics_target = 200-500(по умолчанию 100). Для таблиц с неравномерным распределением данных - увеличить на уровне конкретных столбцов черезALTER TABLE ... ALTER COLUMN ... SET STATISTICS. Для таблицы_AccumRgTэто меняет жизнь.autovacuum_analyze_scale_factor = 0.01для регистров накоплений - снизить порог до 1%, при котором запускаетсяANALYZE. По умолчанию 20%, это катастрофа при быстрорастущих таблицах.work_mem- не поднимать глобально выше 32-64 МБ при большом числе параллельных сессий. Считайте:work_mem * max_parallel_workers_per_gather * число сессий- это реальное потребление памяти в пике. Лучше поднять целевым образом на уровне роли для отчётов.enable_hashjoin = offна уровне сессии для специфичных длинных отчётов, если они запускаются в явном контексте (регламентные задания с известной точкой входа).pg_hint_plan- устанавливать и держать таблицу подсказок в порядке. Хинты протухают при изменении структуры запросов платформой - нужен мониторинг.- Партиционирование регистров - для журнала регистрации и некоторых регистров накоплений партиционирование по периоду радикально улучшает работу планировщика: он оценивает данные внутри партиции, а не всей таблицы. Встроенный механизм 1С поддерживает партиционирование, но требует отдельной настройки.
- Регулярный
ANALYZEвручную для самых горячих таблиц - черезpg_cronили планировщик заданий 1С раз в несколько часов.
Где мы сейчас
Эпизоды с зависанием стали редкими - с еженедельных мы перешли к «раз за несколько недель, и уже не на всю базу». Это прогресс, но не победа.
Основная проблема не решена архитектурно: планировщик PostgreSQL проектировался не для профиля нагрузки 1С. Статистика в 1С - это живой зверь, и никакой разумный autovacuum не успевает за пиковыми изменениями в горячих таблицах. pg_hint_plan закрывает точечные случаи, но это ручная работа, которую надо поддерживать.
Задачи по интеграции и поддержке 1С-систем у нас продолжаются - если у вас похожий профиль нагрузки, детали разнятся, но диагностический маршрут примерно тот же.
- 1С на PostgreSQL: шесть граблей при миграции с MS SQL 2019 · 28 февраля 2023
- PostgreSQL 15 в продакшне: первые недели после перевода боевого кластера · 12 января 2023