Если ты хоть раз администрировал крупные инсталляции «1С:ERP», то знаешь эту классическую боль: на синтетических замерах база показывает отличные цифры, а в реальный рабочий день при паре тысяч сотрудников сервер внезапно задыхается. Транзакции выстраиваются в очереди, пользователи жалуются на зависшие накладные, а процессор загружен на 100%.
Инженеры Postgres Professional совместно с компанией «ИТ-Экспертиза» провели детальный стресс-тест: загнали в СУБД Postgres Pro Enterprise 30 000 одновременных рабочих мест 1С:ERP и выжали интегральный показатель APDEX 0,986 при нормативе 0,85. Я изучил результаты их работы и выделил главные грабли, которые вылезают при таком масштабе.
Шторм инвалидаций системного каталога
Главная специфика 1С в связке с реляционными СУБД — непрерывное создание и удаление временных таблиц. В классическом PostgreSQL каждая временная таблица оставляет след в системном каталоге (pg_class, pg_attribute, pg_depend), вызывая его раздувание (bloat).
В Postgres Pro Enterprise параметр enable_temp_memory_catalog держит метаданные временных объектов в оперативной памяти. Но при 30 000 сессий инженеры столкнулись с новой бедой: функция ReceiveSharedInvalidMessages() захлебнулась. Бэкенды тратили ресурсы CPU на рассылку широковещательных сообщений об изменениях, которые не нужны чужим процессам.
Проблему решил патч ядра (вошедший в релиз 18.6.1): он внедрил кеш TempRelOidCache и подавил лишние сообщения для pg_depend. Вместе с параметром skip_temp_rel_lock = on это сняло паразитную нагрузку на ядра.
Если в твоем стеке приложения массово создают temporary tables, отключай блокировки межпроцессной синхронизации для локальных временных объектов.
Рекурсивный скан и падение бэкендов
Вторая засада приводила к аварийному завершению сессий с ошибкой relation <OID> deleted while still in use. При инвалидации кеша вызывалась функция temp_catalog_beginscan(), которая из-за внутренней защиты от рекурсии при повторном входе возвращала NULL.
Разработчики переписали функцию get_index_entry(), внедрив прямое сопоставление типов атрибутов с функциями сравнения. Это полностью исключило повторные обращения к каталогу и сняло необходимость защиты от рекурсии.
Боевой тюнинг postgresql.conf
Вот ключевые параметры конфигурации, стабилизировавшие СУБД под лавиной параллельных транзакций:
# Оптимизация системного каталога под временные таблицы 1С
enable_temp_memory_catalog = on
skip_temp_rel_lock = on
skip_relcache_initfile_inval = on
# Ограничение перебора вариантов соединений планировщиком
geqo = off
from_collapse_limit = 14
join_collapse_limit = 14
# Агрессивный автовакуум для мгновенной зачистки мертвых кортежей
autovacuum_max_workers = 20
autovacuum_vacuum_cost_delay = 10
autovacuum_vacuum_cost_limit = 7000
autovacuum_vacuum_scale_factor = 0.01
autovacuum_vacuum_threshold = 1000
autovacuum_analyze_scale_factor = 0.01
autovacuum_analyze_threshold = 1000
Обрати внимание на порог автовакуума: стандартные 20% на многотерабайтной базе ведут к деградации. Порог в 1% (scale_factor = 0.01) заставляет воркеры вычищать кортежи непрерывно и небольшими порциями.
Выравнивание нагрузки по NUMA-узлам
Даже настроенная база упрется в архитектуру сервера, если забыть про NUMA. На тестах нулевая нода процессора простаивала на 40%, а первая была забита под 90%. Сетевая карта отправляла прерывания на один сокет, а процессы rphost боролись за локальную память.
Проблему решили L3 bond поверх сетевых карт из разных NUMA-доменов и привязкой процессов через taskset:
#!/usr/bin/env bash
# Распределение рабочих процессов rphost между NUMA-нодами 0 и 1
pgrep rphost | sed '1~2s/^/32-63,96-127 /;2~2s/^/0-31,64-95 /' | xargs -n 2 echo sudo -u usr1cv8 taskset -apc
Архитектурный сухой остаток
Главный практический шаг при выходе на 30 000 пользователей — вынос всех тяжелых аналитических отчетов на асинхронную реплику, оставив мастер только под оперативные OLTP-записи. Масштабирование enterprise-базы — это всегда комплекс: от патчей каталога СУБД до раскладки сетевых прерываний по процессорам.
