На сервере PostgreSQL почти исчезает свободная память. Панель краснеет, закупка новых модулей кажется неизбежной, но база отвечает с прежней задержкой. На другом узле RAM занята так же, однако запросы замедляются и контейнер получает события нехватки памяти. Одинаковый used скрывает разные состояния. Linux не стремится сохранять большой пустой запас: незанятую процессами память система использует для ускорения работы. Поэтому для бюджета важнее не сама занятость, а цена освобождения RAM и потерянное СУБД время.

Free показывает пустое место, а не качество сервиса

В /proc/meminfo поле MemFree означает RAM, которая в этот момент не используется. Это буквальный свободный остаток, но не полный резерв системы. Если Linux сохранил недавно прочитанные части файлов в памяти, MemFree уменьшается, хотя эти страницы при необходимости могут быть вытеснены. MemAvailable отвечает на более практический вопрос. Ядро оценивает, сколько памяти можно предоставить новым приложениям без перехода к swap, учитывая свободную RAM, часть файлового кэша, часть освобождаемых структур и защитные границы. Это оценка, а не договорённость: не весь кэш можно отдать без цены и не все структуры освобождаются мгновенно.

Порог вроде «free меньше 10%» объявляет аварией и здоровый прогретый сервер. Самодельный расчёт used как total минус free записывает полезный кэш в безусловно занятую память. Утилита free отдельно показывает available и кэш, но определения панелей различаются. Низкий MemFree при стабильном сервисе может означать эффективное использование RAM, а заметный MemAvailable не гарантирует запас конкретному контейнеру.

Page cache экономит чтения, но освобождается не бесплатно

Page cache, или файловый кэш, хранит в RAM недавно использованные части файлов. Повторному обращению часто не нужно ждать накопитель, поэтому для СУБД это часть производительного пути. PostgreSQL использует собственный буфер shared_buffers, но официальная документация отдельно учитывает и кэш операционной системы. Блоки таблиц и индексов проходят через файловый путь, поэтому уменьшение RAM может сократить полезный кэш даже тогда, когда настройка shared_buffers не менялась. По графику одного процесса эту потерю легко не заметить.

Не вся кэшированная память освобождается с одинаковой ценой. Чистую страницу можно отбросить и прочитать заново. Изменённая, или dirty, страница сначала должна попасть в writeback — фоновую запись, конкурирующую за накопитель. Если чистая страница скоро снова нужна, reclaim, то есть освобождение памяти ядром, превращается в повторное чтение. Рабочая граница и refault разобраны в предыдущем материале; здесь важен итог: кэш полезен, пока вытеснение не отнимает время у обязательной работы.

Три сервера могут показывать одинаковый used

Первый сервер давно работает под повторяющейся нагрузкой. MemFree низок, большая часть доступной RAM занята файловым кэшем, но MemAvailable остаётся устойчивым, заметного давления нет, а задержка PostgreSQL и объём выполненной работы не меняются. Покупать память только ради увеличения пустой области означало бы оплатить кэш дважды: сначала как полезную RAM, затем как «проблему» на панели. На втором сервере хост имеет запас, однако Redis или PostgreSQL работает в ограниченной группе процессов. Контейнер достигает своей границы, внутри него усиливается reclaim, хотя общесистемный MemAvailable выглядит благополучно. При таком сценарии добавление RAM узлу может ничего не изменить: доступ приложения определяет лимит группы, а не вся физическая ёмкость сервера.

Третий сервер действительно испытывает общее давление. Несколько процессов расширяют активную память, ядро чаще освобождает страницы, возвращает ранее вытесненные данные, использует swap или ждёт записи на накопитель. Одновременно растёт PSI памяти — доля времени, когда задачи не могли продвигаться из-за нехватки ресурса, — и ухудшаются p99 либо пропускная способность СУБД. Здесь высокий used уже часть причинной цепочки, но не самостоятельное доказательство.

Эти состояния нельзя различить одним снимком: разница проявляется в фазе нагрузки, ожидании и результате для пользователей. Swap тоже не даёт простого ответа. Активная подкачка показывает цену возврата страниц, но наличие когда-то выгруженных данных не всегда означает инцидент. И наоборот, система без настроенного swap может испытывать reclaim и stalls, а затем приблизиться к OOM.

Хост и контейнер отвечают на разные вопросы

Поля /proc/meminfo описывают память всего хоста. Они не знают, сколько RAM разрешено отдельному сервису. В cgroup v2, на которой строятся ограничения многих контейнеров, memory.current показывает текущее потребление группы и её потомков. Это другая область учёта, поэтому сравнивать её напрямую с MemFree без контекста нельзя. memory.stat раскладывает потребление, в частности, на anon и file. Anon обычно связан с анонимной памятью процессов, file — с файловыми страницами и кэшем. Это не карта продуктов: PostgreSQL использует оба класса, у Redis тоже есть не только набор данных. Поля не знают бизнес-смысла SQL или ключа.

memory.events добавляет события границ. high означает пересечение мягкой границы и усиленное освобождение памяти; max относится к жёсткому пределу, oom и oom_kill — к неудачным выделениям и завершениям. События видят локальный конфликт при свободном хосте, но накопительные значения без связи с фазой мало что говорят. PSI доступен для системы и группы. Показатель some отражает время, когда из-за ресурса ждала часть задач; full — когда одновременно не продвигались все непростаивающие задачи области. Это ближе к потерянной работе, чем used, но не называет процесс, запрос или страницу и не требует автоматически покупать DRAM.

Давление становится бизнес-проблемой через потерянное время

Для владельца сервиса нехватка памяти начинается не в момент заполнения шкалы, а когда управление памятью мешает обязательной работе. У PostgreSQL это может проявиться ростом хвоста задержки, снижением числа завершённых транзакций или конкуренцией повторных чтений с журналом и фоновыми записями. У Redis короткие операции могут получить паузы, хотя объём логических данных не изменился. p99 — задержка, быстрее которой завершается подавляющее большинство операций, — связывает системную цену с пользователем. Но хвост растёт также из-за CPU, блокировок, сети и накопителя. Причинная связь убедительнее, когда в одной фазе снижается доступность, усиливается reclaim, появляется memory PSI или I/O-очередь и одновременно падает качество сервиса.

Краткий всплеск reclaim при смене отчёта может не нарушить договорённое качество. Устойчивое вытеснение полезных страниц с их быстрым возвратом уже означает, что система тратит ресурсы на перемещение вместо работы. Покупка памяти оправдана не красным free, а стоимостью этой потери и невозможностью устранить её более точной причиной: лимитом контейнера, плохим запросом, конкуренцией соседей или расписанием фоновой работы. MemAvailable в одиночку пропускает сервис, зажатый cgroup; PSI не объясняет причину; p99 способен расти из-за блокировок. Каждая величина отвечает на свой вопрос и не заменяет причинный рассказ.

Бюджет RAM определяет обязательная фаза, а не пустота

Ёмкость следует из обязательных фаз: обычного пика, отчёта, обслуживания, прогрева или восстановления. Запас нужен не ради красивого MemFree, а ради требуемой задержки и пропускной способности при росте и соседней нагрузке. Иногда достаточно исправить предел cgroup: физическая RAM уже есть, но СУБД её не видит. Иногда полезнее развести I/O или расписание. Когда же горячий набор действительно не помещается, дополнительная DRAM возвращает производительность. Эти решения имеют разную цену при одинаковом графике used.

Прозрачный страничный уровень класса gigaRAM может обсуждаться, когда у нагрузки действительно есть менее активные страницы: горячая часть остаётся в DRAM, а подходящая менее активная обслуживается через NVMe с дополнительной задержкой. Низкий free не доказывает такую структуру и не позволяет вычислить экономию ни из used, ни из MemAvailable. Если вытесняемый кэш быстро нужен снова или I/O уже насыщен, более медленный уровень способен ухудшить результат.

Главный вывод прост: свободная память измеряет пустоту, а дефицит — потерянную полезную работу. Тёплый кэш при хорошем SLO является активом. Локальное ограничение контейнера требует работы с его границами. Рост PSI, повторного I/O и p99 в обязательной фазе превращает управление памятью в бизнес-проблему. Бюджет следует за этим различием, а не за универсальным процентом free.

Источники. Linux 6.12: /proc и MemAvailable определяет поля памяти хоста и расчёт доступности;

Linux 6.12 cgroup v2 — memory.current, memory.stat и memory.events;

Linux PSI — some, full и потерянное время.

free(1) поясняет представление free, cache и available.

PostgreSQL 17 Resource Consumption подтверждает роль кэша ОС рядом с shared_buffers.

Meta TMO используется для общей механики страничного переноса, продуктовая страница — только для позиционирования класса решения.