При переводе тяжелых конфигураций 1С (таких как 1С:ЗУП, 1С:ЗКГУ или 1С:ERP) с СУБД MS SQL Server на PostgreSQL технические специалисты часто сталкиваются с неожиданным поведением системы. Обычные операции, которые на MS SQL выполнялись мгновенно, на PostgreSQL могут приводить к стопроцентной нагрузке на одно ядро процессора, лавинообразному росту потребления оперативной памяти, уходу сервера в swap и, как следствие, полной парализации работы пользователей.
Ярким примером такой ситуации является работа с регистрами расчета и связанными с ними таблицами перерасчетов зарплаты. В этой статье мы подробно проанализируем, почему возникает эта проблема, разберем физический смысл ключевых параметров конфигурационного файла postgresql.conf и выработаем пошаговую инструкцию по оптимизации сервера под нужды 1С.
Для начала проанализируем поведение системы, описанное в обращении. При попытке прочитать или удалить записи из регистра «Перерасчеты зарплаты» в ЗКГУ одно ядро процессора загружается на 100% в течение 10–15 минут, а объем памяти, запрашиваемый процессами СУБД, вырастает с базовых значений до 15.5 Гб физической и 22 Гб виртуальной памяти. При этом объем всей оперативной памяти на сервере Centos составляет всего 16 Гб.
Многие ошибочно полагают, что в этот момент PostgreSQL выполняет тяжелый запрос: считывает терабайты данных с диска или производит сложные математические расчеты. Однако это не так. В этот момент СУБД даже не приступала к чтению данных — она зависла на этапе планирования запроса (Query Planning).
Давайте разберем этот механизм подробнее. Запросы, генерируемые платформой 1С при обращении к виртуальным таблицам и регистрам расчета, представляют собой огромные SQL-конструкции. Они содержат множество вложенных подзапросов, обращений к временным таблицам и десятки соединений LEFT JOIN. Для построения эффективного плана выполнения планировщик PostgreSQL должен решить, в каком порядке соединять эти таблицы.
За эту логику отвечают два критически важных параметра в postgresql.conf:
from_collapse_limit — определяет количество элементов в предложении FROM, до достижения которого планировщик будет пытаться «схлопнуть» (объединить) вложенные запросы в один плоский список для поиска оптимального порядка соединений.join_collapse_limit — аналогично определяет предел, до которого конструкции явных перекрестных соединений (JOIN) будут объединяться в плоский список.Поиск оптимального порядка соединения таблиц — это задача с факториальной сложностью $O(N!)$. Если эти параметры установлены в значение 16 (как это часто рекомендуется в некоторых устаревших гайдах по оптимизации 1С), и в запросе присутствует 15–16 соединяемых таблиц, планировщик пытается проанализировать все возможные комбинации их связей.
Давайте посчитаем:
Для обсчета этого гигантского дерева вариантов процессу PostgreSQL требуется колоссальный объем памяти. Поскольку планированием занимается один конкретный поток СУБД, обслуживающий данное клиентское соединение, мы наблюдаем картину: одно ядро процессора загружено на 100%, память лавинообразно утекает в виртуальный swap, а СУБД полностью зависает на 15 минут, пытаясь найти идеальный план для запроса, который сам по себе мог бы выполниться за секунды.
Комментирование этих строк в конфигурационном файле возвращает их к дефолтным значениям (обычно это 8). В результате планировщик перестает заниматься перебором триллионов вариантов, мгновенно строит эвристический план, и запрос выполняется с приемлемой скоростью.
Рассмотрим вторую группу параметров, изменение которых помогло стабилизировать систему. Автор уменьшил значения work_mem и temp_buffers со 128 МБ до 64 МБ, а также снизил maintenance_work_mem.
Разберем физический смысл этих настроек:
1. Параметр work_mem
Это объем памяти, выделяемый для внутренних операций сортировки (например, ORDER BY, DISTINCT) и хэш-соединений (Hash Join) перед тем, как СУБД начнет сбрасывать временные данные на диск в файлы подкачки.
Важно понимать критическую ошибку многих администраторов: этот объем памяти выделяется не на весь запрос и не на одного пользователя! Он выделяется под каждую отдельную операцию сортировки или хэширования внутри одного запроса. Сложный аналитический запрос 1С при расчете зарплаты может содержать до 10–15 таких операций одновременно.
Если у вас установлено work_mem = 128MB, один тяжелый запрос от одного пользователя может потребовать:
128 МБ * 15 операций = 1.92 ГБ оперативной памяти
Если параллельно такие отчеты запустят 5–6 пользователей, сервер с 16 ГБ оперативной памяти моментально исчерпает физический объем ОЗУ. После этого операционная система Centos активирует механизм защиты OOM-killer (Out of Memory Killer) и принудительно аварийно завершит процесс PostgreSQL, либо сервер уйдет в глубокий swap, заблокировав всю работу.
Снижение work_mem до 64 МБ (а для баз среднего размера рекомендуется начинать вообще с 32 МБ или даже 16 МБ) гарантирует безопасность. Если операции сортировки не поместятся в этот объем, PostgreSQL сбросит их во временные файлы на диск. Да, скорость выполнения этого конкретного запроса может незначительно снизиться, но вся система продолжит стабильную работу без падений.
2. Параметр temp_buffers
Этот параметр задает максимальный объем памяти, выделяемый под временные таблицы для каждой сессии. Платформа 1С активно использует временные таблицы во всех своих механизмах. Значение 256 МБ на сессию при большом количестве активных пользователей также способно быстро истощить оперативную память сервера. Снижение этого параметра до 128 МБ является разумным компромиссом для конфигураций типа ЗКГУ и БГУ.
В сообщениях темы упоминается, что лимит max_connections = 1000 установлен по «рекомендациям 1С». Давайте проанализируем, почему слепое следование этой рекомендации на сервере с 16 ГБ оперативной памяти является критической ошибкой администрирования.
PostgreSQL использует архитектурную модель процесса process-per-connection (модель fork). Это означает, что под каждое новое подключение операционная система выделяет отдельный независимый процесс. Каждый такой процесс, даже находясь в состоянии простоя (idle), потребляет порядка 10–15 МБ оперативной памяти на поддержание собственного контекста и служебных структур данных.
Если на сервере действительно откроется 1000 подключений, то картина будет следующей:
1000 подключений * 15 МБ = 15 ГБ ОЗУ
Вся оперативная память сервера (16 ГБ) будет сожрана процессами СУБД просто за факт своего существования! При этом не останется ресурсов ни под дисковый кэш операционной системы, ни под разделяемый буфер СУБД (shared_buffers), ни под выполнение самих запросов.
Правило настройки: Для серверов с объемом оперативной памяти до 32 ГБ значение max_connections ни в коем случае не должно превышать 150–200 соединений. Если конфигурация вашей системы 1С требует большего числа одновременных сессий, необходимо:
Автор изменил параметр effective_io_concurrency с 8 на 2. Этот параметр сообщает планировщику PostgreSQL, сколько одновременных операций ввода-вывода дисковая подсистема может обрабатывать параллельно без деградации производительности.
Для современных локальных SSD-накопителей это значение может составлять от 100 до 200. Однако в конфигурации автора сервер является виртуальным и расположен в ЦОДе с использованием сетевого хранилища данных (SAN/NAS).
Сетевой ввод-вывод характеризуется высокой задержкой передачи пакетов (network latency). Если установить высокое значение effective_io_concurrency, PostgreSQL начнет отправлять запросы на упреждающее чтение слишком агрессивно. Сетевой стек и контроллер удаленного хранилища не будут успевать обрабатывать эти запросы, возникнет огромная очередь на чтение, что приведет к резкому падению скорости дисковых операций всей виртуальной машины. Ограничение этого параметра значением 2 (или даже 0 для медленных дисков) позволяет снизить сетевой оверхед и стабилизировать дисковую подсистему.
Проанализируем оптимальный набор параметров для сервера с конфигурацией 6 ядер процессора и 16 Гб оперативной памяти, на котором крутятся конфигурации БГУ и ЗКГУ.
Рекомендуем внести следующие изменения в конфигурационный файл postgresql.conf:
# Память для кэширования таблиц СУБД (рекомендуется 25% от общей RAM)
shared_buffers = 4GB
# Память для временных таблиц одной сессии 1С
temp_buffers = 128MB
# Память под операции сортировки и хэширования в рамках одного узла запроса
work_mem = 32MB
# Память под служебные операции (индексация, вакуум, очистка)
maintenance_work_mem = 512MB
# Максимальное количество подключений к базе данных
max_connections = 150
# Ограничение планировщика на схлопывание таблиц (защита от зависания на тяжелых LEFT JOIN)
from_collapse_limit = 8
join_collapse_limit = 8
# Параметр оценки дискового кэша ОС (рекомендуется около 50-75% от всей RAM)
effective_cache_size = 10GB
# Эффективный параллелизм ввода-вывода для сетевого хранилища
effective_io_concurrency = 2
После внесения изменений в файл конфигурации необходимо перезапустить службу PostgreSQL для их вступления в силу. На операционной системе Centos 7 это выполняется командой:
systemctl restart postgresql-11
Важное примечание: При оптимизации серверов баз данных всегда оставляйте не менее 2-3 Гб оперативной памяти свободной под нужды самой операционной системы Linux и дискового кэша ОС. Полное распределение всей физической памяти под параметры PostgreSQL приведет к падению производительности дисковых операций, так как СУБД во многом опирается на кэш страниц операционной системы (Page Cache).