ADG Оставить заявку
Блог Данные и аналитика 5 мин чтения

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 - соответствие задаётся по нормализованному тексту запроса без параметров. Схема работы:

  1. Ловим проблемный запрос через pg_stat_statements или логи с log_min_duration_statement.
  2. Берём нормализованный текст (где параметры заменены на $1, $2...).
  3. Пишем запись в 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С-систем у нас продолжаются - если у вас похожий профиль нагрузки, детали разнятся, но диагностический маршрут примерно тот же.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.