Настройка Linux Strict Memory Overcommit для предотвращения сбоев PostgreSQL
Операционные системы семейства Linux по умолчанию используют оптимистичный подход к управлению виртуальной памятью — оверкоммит (overcommit). Ядро исходит из предположения, что приложению редким образом требуется весь объем памяти, запрошенный при вызове функции выделения (например, malloc). До тех пор пока процесс не начнет фактически записывать данные на выделенные страницы, физическая оперативная память им не выделяется.
Для СУБД PostgreSQL такое поведение операционной системы представляет постоянную угрозу. В момент, когда суммарный объем физической памяти хоста исчерпывается, ядро вынуждено принудительно завершить один из процессов с помощью механизма OOM Killer (Out-Of-Memory Killer). Когда жертвой становится процесс-обработчик PostgreSQL, вся система сталкивается с аварийной перезагрузкой инстанса и длительным простоем.
Эвристический оверкоммит и уязвимость PostgreSQL к OOM Killer
Операционная система Linux поддерживает три режима учетной политики выделения оперативной памяти (vm.overcommit_memory):
- Режим 0 (Heuristic Overcommit): Поведение по умолчанию. Ядро анализирует запросы на выделение памяти с помощью эвристики, разрешая разумные превышения и отклоняя очевидно невыполнимые запросы.
- Режим 1 (Always Overcommit): Ядро разрешает любые запросы приложений на выделение виртуальной памяти, игнорируя реальный объем физической памяти и файла подкачки.
- Режим 2 (Strict Memory Overcommit): Ядро строжайшим образом ограничивает суммарный объем выделенной виртуальной памяти установленным лимитом. Запросы, превышающие этот лимит, отклоняются на этапе вызова
malloc.
В режиме по умолчанию (режим 0) ядро позволяет процессам резервировать адресное пространство, превышающее физические возможности оборудования. Если несколько параллельных процессов PostgreSQL начинают заполнять зарезервированную память, в системе возникает дефицит.
Архитектура PostgreSQL устроена так, что при принудительном завершении любого из рабочих процессов (backend) по сигналу SIGKILL управляющий процесс postmaster не может гарантировать целостность разделяемой памяти (shared_buffers). Чтобы предотвратить повреждение данных, postmaster завершает все оставшиеся клиентские соединения, инициирует процедуру восстановления после сбоя (crash recovery) и заново проигрывает WAL-логи. В результате OOM-сбой приводит к полному разрыву всех подключений и недоступности СУБД.
Сравнительный эксперимент: поведение СУБД при дефолтной и строгой политиках
Для демонстрации разницы между эвристическим и строгим режимами был проведен эксперимент на сервере AWS EC2 (конфигурация m7i.2xlarge: 8 vCPU, 30.8 ГБ ОЗУ, PostgreSQL 16 с тестовой базой pgbench объемом 3 ГБ). Резервирование памяти под shared_buffers составляло 6.8 ГБ (с Huge Pages 2 МБ). На хосте удерживались 20 простаивающих сессий, а отдельный монитор каждые 0.3 секунды проверял возможность установления нового соединения.
Нагрузочный сценарий имитировал тяжелые аналитические запросы с построением массивов в памяти через array_agg(repeat('x', 1000)) по генератору рядов.
Результаты эксперимента:
- Режим по умолчанию (
vm.overcommit_memory = 0): Объем выделенной виртуальной памяти (Committed_ASв/proc/meminfo) достиг 23.6 ГБ при лимите 11.6 ГБ. Спустя 24 секунды, когда свободная физическая память снизилась до 1.3 ГБ, ядро Linux вызвало OOM Killer и завершило рабочий процесс Postgres, занимавший 2.4 ГБ RSS. Процессpostmasterсбросил все 20 сторонних сессий и ушел в перезагрузку. Недоступность базы данных составила 30.2 секунды. - Строгий режим (
vm.overcommit_memory = 2): Лимит памяти был жестко ограничен значением 20.5 ГБ (vm.overcommit_kbytes). По достижении объема 19.8 ГБ десятый клиентский запрос получил от системы отказ. PostgreSQL отменил транзакцию с ошибкой приложения:ERROR: out of memory.
При этом остальные 9 параллельных процессов продолжили работу, все 20 простаивающих сессий не потеряли соединение, а в логе ядра не появилось записей OOM Killer. На хосте сохранилось 3.8 ГБ свободной памяти, что позволило СУБД обработать отказ транзакции без общего простоя.
Математика памяти Linux: режимы ядра, ratio и kbytes
В строгом режиме (vm.overcommit_memory = 2) суммарная доступная для выделения память (Commit Limit) рассчитывается ядром по формуле:
$$\text{CommitLimit} = (\text{RAM} - \text{HugePages}) \times \frac{\text{overcommit_ratio}}{100} + \text{Swap}$$
Параметры настройки ядра:
vm.overcommit_ratio: Процент доступной оперативной памяти (по умолчанию 50%).vm.overcommit_kbytes: Абсолютное значение допустимой памяти в килобайтах.
Важно: Параметры overcommit_ratio и overcommit_kbytes взаимно исключают друг друга. Установка одного из них автоматически обнуляет другой. Если задан vm.overcommit_kbytes, ядро игнорирует overcommit_ratio.
Текущее состояние системы отслеживается через файл /proc/meminfo:
CommitLimit: Максимальный объем виртуальной памяти, который ядро согласится выдать суммарно всем процессам.Committed_AS: Текущий объем виртуальной памяти, зарезервированный процессами.
В строгом режиме ядро никогда не позволит значению Committed_AS превысить CommitLimit.
Учет Huge Pages, Swap и расчет лимита
При расчете лимита памяти для серверов баз данных учитываются следующие факторы:
- Исключение Huge Pages: Страницы памяти HugeTLB, зарезервированные под
shared_buffersPostgreSQL, выделяются заранее и исключаются из математики overcommit accounting. Лимит рассчитывается только на оставшийся объем «обычной» памяти. - Учет файла подкачки (Swap): Подкачка увеличивает значение
CommitLimit. Однако использовать swap для активных рабочих процессов СУБД недопустимо из-за деградации производительности. Swap в данном случае служит буфером безопасности. - Запас под операционную систему: Часть оперативной памяти должна оставаться свободной под нужды ядра Linux, кэш файловой системы (page cache) и системные службы.
Руководство по настройке sysctl и регламент отката
Внедрение строгого режима overcommit требует выполнения следующих шагов:
- Аудит текущего состояния:
Снять текущие показатели системы:
sysctl vm.overcommit_memory vm.overcommit_ratio vm.overcommit_kbytes grep -E 'CommitLimit|Committed_AS' /proc/meminfo
Проверить параметры PostgreSQL:shared_buffers,work_mem,max_connectionsи PgBouncer. - Создание конфигурационного файла:
Создать файл
/etc/sysctl.d/99-postgresql-memory.conf:vm.overcommit_memory = 2 vm.overcommit_ratio = 80
Применить настройки:sysctl --system - Проверка и тестирование:
Убедиться в изменении показателей в
/proc/meminfo. В тестовом окружении выполнить прогон тяжелых запросов, имитируя нехватку памяти. Убедиться, что при превышении лимита сессия получает ошибкуout of memory, а основной процесс СУБД продолжает работу. - Регламент отката (Rollback Plan):
Если приложения начинают массово получать ошибки недостатка памяти, восстановить дефолтный режим можно командой:
sysctl -w vm.overcommit_memory=0
После отката необходимо пересмотреть значенияwork_memи ограничение максимального числа подключений в PostgreSQL.
Перевод Linux в режим vm.overcommit_memory = 2 переносит точки отказа с непредсказуемого OOM Killer на управляемые ошибки уровня приложения, обеспечивая высокую доступность инфраструктуры баз данных.

