PG_EXPECTO: правда о vm.swappiness и производительности PostgreSQL под нагрузкой
Взято с основного технического канала Postgres DBA (возможны правки в исходной статье).
Продолжение статьи :
Стандартные рекомендации по влиянию параметра vm.swappiness на производительность СУБД PostgreSQL
Для OLAP-нагрузок в PostgreSQL рекомендуется установить vm.swappiness=1, чтобы минимизировать обращения к медленному диску и обеспечить стабильную производительность при обработке больших данных. Это заставит ядро использовать своп только в крайнем случае, сохраняя рабочие данные в оперативной памяти.
1. Текущая конфигурация памяти:
RAM: 8 GB
shared_buffers: 4 GB (50% от RAM - правильно настроено)
work_mem: 32 MB (адекватно для 22 сессий)
Остаётся для ОС: ~3.5-4 GB (учитывая кэш файловой системы)
2. Рекомендация: swappiness = 1 (лучший выбор)
Почему именно 1:
PostgreSQL интенсивно использует файловый кэш - для SELECT-нагрузки это критично
shared_buffers уже содержит "горячие" данные в управляемой памяти
Низкое значение предотвращает вытеснение кэша страниц ОС в своп
При нехватке памяти PostgreSQL сам управляет своими буферами через shared_buffers
Экспериментальная проверка влияния vm.swappiness=1 на производительность СУБД
Операционная скорость
Среднее уменьшение операционной скорости в Эксперименте-2 (vm.swappiness = 1) составило 14.37%⚠️
Анализ причин снижения производительности СУБД PostgreSQL после применения vm.swappiness = 1
Рекомендация vm.swappiness=1 для PostgreSQL, которую часто дают нейросети и многие руководства, основана на общей логике: «меньше свопа — меньше обращений к диску — выше производительность». Однако эксперимент, описанный в статье «PG_EXPECTO 5.1: Влияние vm.swappiness=1 на производительность PostgreSQL», показывает, что в конкретных условиях эта настройка не дала ожидаемого эффекта. Вот ключевые причины, почему это произошло.
🔍 Суть эксперимента
Конфигурация: PostgreSQL 17, 8 ядер CPU, 8 ГБ RAM, shared_buffers=4 ГБ, нагрузка OLAP (аналитические запросы с большим объёмом чтения).
Сравнение: Производительность при vm.swappiness=10 и vm.swappiness=1.
Ожидание: Снижение своппинга должно уменьшить дисковый ввод-вывод и ускорить работу.
📊 Результаты
Диск - «узкое место»
Нагрузка на диск (/data) достигала 100% в обоих экспериментах.
Высокая очередь запросов (aqu_sz) и время ожидания CPU для IO (cpu_wa >50%) указывали на то, что производительность упиралась в диск, а не в память.
Система избегала своппинга
Даже при swappiness=10 ядро Linux предпочитало вытеснять файловый кэш, а не отправлять данные в своп. Поэтому изменение параметра почти не повлияло на поведение подсистемы памяти.
Минимальное влияние на метрики vmstat
Разница между swappiness=10 и swappiness=1 оказалась статистически незначимой: не было существенных изменений в IOPS или пропускной способности.
Небольшие улучшения (например, снижение w_await и глубины очереди) не решили основную проблему — перегруженность диска.
Общий результат
Проведенные эксперименты не подтвердили ожидаемого положительного влияния снижения параметра vm.swappiness на производительность PostgreSQL при интенсивной нагрузке с дефицитом оперативной памяти. Ключевым узким местом в обоих экспериментах стала производительность дисковой подсистемы.
🤔 Почему рекомендация нейросетей не подтвердилась?
Обобщённый характер рекомендаций
Нейросети (и многие руководства) опираются на общие принципы: «для OLAP-нагрузок снизьте swappiness, чтобы удержать данные в RAM». Однако они не учитывают конкретную конфигурацию и узкие места системы.
Условия эксперимента
Дисковая подсистема была перегружена (100% utilization), поэтому даже полное отключение свопа не могло улучшить общую производительность.
Нехватка RAM (8 ГБ при shared_buffers=4 ГБ) приводила к вытеснению файлового кэша, а не к активному своппингу. Из-за этого изменение swappiness практически не меняло картину.
Контекстная зависимость
Эффект от vm.swappiness проявляется только когда система активно использует своп. Если же система избегает своппинга (как в эксперименте) или узким местом является диск, то настройка не даёт заметного выигрыша.
💡 Практические выводы
Не доверяйте слепо общим рекомендациям (в том числе от нейросетей)
Проверяйте их в своей среде.
Диагностируйте узкие места перед тонкой настройкой
Мониторьте iostat, vmstat, pg_stat_activity.
Смотрите на %util, await, cpu_wa, aqu_sz.
Если диск перегружен, сначала оптимизируйте его
Используйте более быстрые диски (SSD/NVMe).
Необходима настройка effective_io_concurrency, random_page_cost.
Рассмотрите увеличение RAM для уменьшения нагрузки на диск.
vm.swappiness стоит снижать только когда
Наблюдается активный своппинг (si, so в vmstat).
Дисковая подсистема не является узким местом.
PostgreSQL работает с большим объёмом данных, которые должны оставаться в RAM.
Краткий сравнительный анализ производительности и ожиданий СУБД
Операционная скорость (SPEED)
Наклон линии регрессии скорости отрицательный в обоих случаях, но в Эксперименте-2 он круче (-26.99 против -23.65), что указывает на более быстрое снижение производительности.
Ожидания СУБД (WAITINGS)
Общий объем ожиданий и их рост во времени практически идентичны в обоих экспериментах (угол наклона ~43.6).
В обоих случаях ожидания почти полностью (коэффициент корреляции ~1.0) состоят из ожиданий ввода-вывода (IO).
Ключевое отличие: в Эксперименте-2 появилась умеренная корреляция ожиданий с IPC (0.86) и LWLock (0.86), которые в первом эксперименте были незначимы.
Типы ожиданий (Wait Events):
Основное событие ожидания в обоих случаях — DataFileRead (более 90% всех ожиданий типа IO).
В Эксперименте-2 дополнительно зафиксированы ожидания типов IPC (BufferIo) и LWLock (XidGen, BufferContent), что отсутствовало в первом эксперименте.
Подробный сравнительный анализ метрик vmstat и выводы по метрикам
procs_r (процессы в run queue)
Одинаково низкие значения (1-3) в обоих экспериментах. Очередь на выполнение не является проблемой.
procs_b (процессы в uninterruptible sleep)
Явная проблема в обоих случаях. Количество процессов, заблокированных в ожидании IO, стабильно растет на протяжении всего теста.
В Эксперименте-1 значение выросло с 5 до 16.
В Эксперименте-2 значение выросло с 5 до 14.
Более 40% наблюдений в обоих тестах — количество заблокированных процессов превышает число ядер CPU (8), что подтверждает серьезные проблемы с подсистемой IO.
memory_swpd (используемый объем свопа)
Эксперимент-1: Незначительный рост с ~220 до ~224 МБ.
Эксперимент-2: Заметный рост с ~205 до ~217 МБ. Хотя объем мал, тенденция к увеличению при swappiness=1 присутствует.
swap_si / swap_so (свопин/аут)
В обоих экспериментах равен 0. Активный свопинг не наблюдался.
memory_free (свободная RAM)
Критически низкий показатель в обоих экспериментах (~130 МБ из 7.5 ГБ, что составляет менее 2%).
100% наблюдений — свободной памяти менее 5%. Система работает на пределе доступной оперативной памяти.
memory_buff / memory_cache (буферы и кеш)
Стабильно высокие значения (кеш ~7 ГБ) в обоих тестах. ОС активно использует свободную память для кеширования дисковых операций, что является нормальным поведением.
io_bi / io_bo (блоки ввода/вывода)
Чрезвычайно высокие значения в обоих экспериментах (bi: с ~26k до ~32k; bo: с ~17k до ~20k).
Наблюдается очень сильная корреляция (>0.87) этих показателей с ожиданиями IO в СУБД. Это подтверждает, что СУБД является основным источником нагрузки на дисковую подсистему.
system_in / system_cs (прерывания / переключения контекста)
Значения стабильны и сопоставимы в обоих тестах. Существенных аномалий не выявлено.
cpu_us / cpu_sy / cpu_id / cpu_wa (распределение времени CPU)
cpu_wa (время ожидания IO): Основная проблема. В обоих экспериментах занимает 50-70% времени CPU, что указывает на то, что процессор простаивает в ожидании операций дискового ввода-вывода.
cpu_id (время простоя): Снижается с ~33% в начале до ~10-13% в конце тестов, так как процессор все больше времени ждет IO.
cpu_us / cpu_sy (пользовательское/системное время): Остаются стабильно низкими (11-12% / 4-5%), что означает отсутствие нагрузки на вычислительные ресурсы со стороны приложения или ядра ОС. CPU не является узким местом.
Ключевые отличия метрик vmstat, влияющих на итоговую производительность СУБД
Динамика memory_swpd
При vm.swappiness=1 наблюдается более выраженный рост использования области свопа (с 205 до 217 МБ), в то время как при значении 10 рост был минимальным (с 220 до 224 МБ). Это указывает на то, что даже при низком значении параметра система под давлением нехватки памяти была вынуждена начать использовать своп.
Уровень procs_b
В конце теста при swappiness=10 было зафиксировано 16 заблокированных процессов, а при swappiness=1 — 14. Хотя разница невелика, в сочетании с другими метриками это указывает на схожую, но несколько менее выраженную пиковую нагрузку на IO во втором эксперименте.
Анализ причин снижения производительности при vm.swappiness=1
Основная (общая) причина
В обоих экспериментах основная причина снижения производительности — критическая нехватка оперативной памяти для рабочего набора данных СУБД. Это приводит к:
Чрезмерной нагрузке на дисковую подсистему (очень высокие io_bi/io_bo, cpu_wa >50%).
Росту числа процессов, заблокированных на операциях ввода-вывода (procs_b).
Прямой зависимости скорости работы СУБД от скорости чтения с диска (корреляция speed и shared_blks_read >0.97).
Специфическое влияние vm.swappiness=1
Установка параметра в 1 должна минимизировать использование свопа. Однако при тотальной нехватке памяти система все равно была вынуждена начать использовать swpd, хотя и менее активно, чем при значении 10.
Более важное следствие: Жесткая политика удержания данных в RAM при ее катастрофической нехватке привела к усилению конкуренции за ресурсы памяти между процессами СУБД. Это выразилось в появлении в Эксперименте-2 дополнительных ожиданий, связанных с легковесными блокировками (LWLock) и межпроцессным взаимодействием (IPC), которые отсутствовали в первом тесте.
Данные блокировки являются внутренними для PostgreSQL и возникают при конкуренции за доступ к разделам общей памяти. Их появление свидетельствует о том, что нехватка памяти начала негативно влиять не только на IO, но и на внутреннюю координацию процессов СУБД.
Итог
При vm.swappiness=1 на фоне той же фундаментальной проблемы (нехватка RAM → лавина IO) дополнительно возникла внутренняя contention (конкуренция) в СУБД из-за борьбы за scarce (дефицитную) оперативную память. Это привело к более быстрому и глубокому падению операционной скорости (SPEED) по сравнению с Экспериментом-1, где система могла немного "разгрузить" RAM в своп, потенциально смягчая пиковое давление на shared buffers и внутренние структуры PostgreSQL.
⚖️ Почему swappiness=10 работал лучше
Более сбалансированный подход
При swappiness=10 система могла немного своппингуть неактивные фоновые процессы
Это сохраняло больше файлового кэша для активных операций PostgreSQL
Оптимизация под конкретную нагрузку
OLAP-запросы (70% SELECT) требуют много последовательных чтений
Файловый кэш ОС критически важен для повторных чтений одних и тех же данных
Сохранение этого кэша дало больше преимуществ, чем экономия на своппинге
📈 Основной итог экспериментов:
Нет универсальных настроек
То, что работает для OLTP, может вредить OLAP
Важен контекст bottlenecks:
Если узкое место - диск (как в эксперименте), сохраняйте кэш любой ценой
Если узкое место - память, можно снижать swappiness
Мониторинг обязателен
Без метрик vmstat и iostat нельзя понять реальный эффект
Послесловие
Главный вывод этого исследования прост: не существует универсальных настроек. Производительность PostgreSQL зависит от конкретной конфигурации, нагрузки и узких мест системы. Прежде чем применять рекомендации, проведите тесты в своей среде и опирайтесь на метрики, а не на советы из чатов.






















