MAATRIX / Блог / Как устроен ARC в ZFS и почему память «съедена»

Как устроен ARC в ZFS и почему память «съедена»

MAATRIX

Открываете free -h или htop на сервере с ZFS и видите, что почти вся оперативная память занята — а вы вроде бы ничего тяжёлого не запускали. Первая мысль у многих новичков: где-то утечка памяти, ZFS сломан, надо срочно что-то чинить. На самом деле это почти всегда ARC — адаптивный кеш ZFS, который работает ровно так, как задуман: занимает свободную память, потому что простаивающая RAM — это ресурс, который жалко не использовать. Разберём, как ARC устроен на самом деле, чем он принципиально отличается от утечки и как настроить его размер под свой сервер.

Что такое ARC и зачем ZFS вообще нужен свой кеш

ARC — Adaptive Replacement Cache — это слой кеширования данных и метаданных, который ZFS держит в оперативной памяти между приложением и физическим диском. Когда вы читаете файл с ZFS-датасета, прочитанные блоки не выбрасываются сразу — они оседают в ARC. Если тот же блок понадобится снова, ZFS отдаёт его из памяти, а не идёт на диск заново.

Разница в скорости между чтением из RAM и чтением с диска — на порядки, даже на быстром NVMe: обращение к оперативной памяти измеряется в наносекундах, обращение к диску — в микросекундах или десятках микросекунд, а для вращающихся HDD счёт может идти на миллисекунды. Именно поэтому кеш в памяти вообще имеет смысл: чем больше «горячих» данных удаётся держать в ARC, тем меньше запросов доходит до диска, и тем выше отзывчивость системы на повторных чтениях.

ZFS не полагается на стандартный page cache операционной системы, а реализует собственный механизм — отчасти потому, что ARC умеет учитывать не только частоту, но и давность обращения к блоку (это и заложено в слове Adaptive: кеш адаптируется к паттерну доступа, балансируя между «часто используемые» и «недавно использованные» данные), а отчасти потому что ARC интегрирован с внутренней логикой ZFS — контрольными суммами, сжатием, дедупликацией. Базовые понятия пула, датасета и снапшота, на которых стоит вся эта конструкция, мы разбирали в статье ZFS за 15 минут — если ARC для вас новая тема, стоит сначала закрыть этот пробел.

Важно: ARC кеширует не «весь диск», а рабочий набор данных, который реально запрашивается. Если у вас 10TB пула, но активно читается только 200GB из них, то именно эти 200GB (или их часть, ограниченная размером ARC) будут постепенно попадать в кеш — а не случайный срез всего пула.

Почему ARC занимает так много памяти — это осознанное решение, а не баг

Ключевая идея, которую упускают новички: свободная память, которая просто лежит незанятой, — это упущенная выгода. Она не приносит никакой пользы, пока простаивает. Разработчики ZFS исходили именно из этого: если RAM свободна, лучше использовать её под кеш, который ускорит будущие операции чтения, чем оставить пустовать «на всякий случай».

Поэтому по умолчанию ARC агрессивно расширяется и способен занимать значительную долю доступной оперативной памяти сервера — точные цифры зависят от версии OpenZFS, платформы и общего объёма RAM, поэтому не буду называть конкретный процент: стоит свериться с документацией именно для вашей версии (modinfo zfs, changelog дистрибутива или официальный репозиторий OpenZFS). Общая логика такая: чем больше свободной памяти доступно системе, тем больше ARC готов забрать под кеш, оставляя разумный запас для остальных процессов.

Отсюда и картина в мониторинге, которая пугает новичков: сервер только что поднялся, ARC ещё не успел ничего накешировать — памяти свободно много. Через день работы под нагрузкой чтения ARC разросся и занял большую часть RAM — в free -h это выглядит как «память кончилась», хотя фактически система в порядке: ей просто не нужно было отдавать эту память никому другому, и она пошла в дело.

Отличить «ARC занял память с пользой» от «что-то реально не так» помогает простая проверка:

# Linux (OpenZFS) — сводка по ARC
cat /proc/spl/kstat/zfs/arcstats | grep -E "^size|^c_max|^c \b"

# альтернативный вывод, если установлен zfs-utils с этим скриптом
arc_summary

Поле size — сколько ARC реально занимает сейчас, c_max — текущий верхний лимит, до которого ARC может расти. Если size растёт к c_max по мере активности чтения — это штатное поведение, а не проблема.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Ключевое отличие ARC от утечки памяти: динамическое освобождение

Настоящая утечка памяти — это когда процесс занимает RAM и никогда её не отдаёт, даже когда система испытывает острую нехватку памяти под другие нужды. Со временем такой процесс копит всё больше занятой, но фактически бесполезной памяти, пока не доведёт систему до OOM (out-of-memory) и принудительного убийства процессов.

ARC устроен принципиально иначе. У него есть механизм вытеснения (eviction): когда на сервере запускается новый процесс, которому реально нужна память — например, поднимается тяжёлое приложение, база данных увеличивает буферы, или ядру нужна память под собственные структуры — ZFS обязан отреагировать и уменьшить размер ARC, освобождая занятые страницы для этого нового потребителя.

Это принципиальное отличие можно сформулировать так:

ХарактеристикаУтечка памятиARC ZFS
Занимает свободную RAMДа, постепенноДа, целенаправленно
Отдаёт память при реальной потребности других процессовНет, никогдаДа, динамически (eviction)
Растёт бесконечноДа, до OOMНет, ограничен c_max
Видна причина ростаОбычно нет (баг в коде)Да, коррелирует с объёмом чтений
Что показывает мониторинг в покоеПостоянный рост занятой памятиСтабильное плато у лимита

Иначе говоря: высокое потребление памяти ARC в состоянии покоя — это не тревожный сигнал сам по себе. Тревожным сигналом было бы, если бы память, занятая под ARC, не освобождалась в момент, когда другому процессу реально не хватает RAM и он падает по OOM, хотя формально в системе «занята» память под кеш. Такое поведение возможно, но обычно указывает либо на слишком агрессивный лимит ARC относительно реальных потребностей сервера, либо на редкий баг, а не на нормальную работу механизма.

Практический нюанс: когда стоит явно ограничить ARC

Динамическое вытеснение работает, но у него есть практическая цена: оно происходит реактивно, в момент, когда памяти уже не хватает. Если новое приложение резко запрашивает большой объём RAM, а ARC в этот момент занимает почти всю свободную память, освобождение места может занять заметное время и создать кратковременные задержки — как для самого нового процесса, ожидающего память, так и для операций чтения ZFS, чей кеш резко ужимается.

Для сервера, где ZFS — единственная существенная нагрузка (выделенный файловый сервер, NAS, бекап-хранилище), полагаться на автоматическое поведение ARC обычно разумно: там нет других тяжёлых потребителей памяти, с которыми нужно делить RAM. А вот если ZFS работает на том же сервере, что и другие требовательные к памяти приложения — базы данных, кеши уровня приложения (Redis, Memcached), виртуальные машины, контейнеры с собственными лимитами, — есть смысл явно ограничить максимальный размер ARC заранее, а не полагаться исключительно на вытеснение по требованию.

Логика простая: вы заранее резервируете гарантированный объём памяти под остальные процессы, вместо того чтобы каждый раз надеяться на быстрое и безболезненное вытеснение ARC в момент пиковой нагрузки.

Как настроить лимит ARC через zfs_arc_max

Управление верхней границей ARC делается через параметр модуля ядра ZFS zfs_arc_max. Общий подход такой (конкретный синтаксис и путь стоит сверить с документацией вашего дистрибутива и версии OpenZFS — детали параметров ядра между релизами могут отличаться):

Проверить текущее значение лимита:

cat /sys/module/zfs/parameters/zfs_arc_max

Если там 0 — используется значение по умолчанию, вычисляемое ZFS автоматически исходя из объёма RAM.

Задать лимит «на лету» (применяется сразу, но не переживёт перезагрузку):

echo 8589934592 > /sys/module/zfs/parameters/zfs_arc_max

Значение указывается в байтах — в примере это 8GB (8 × 1024³). Пересчитайте под нужный вам объём.

Закрепить лимит на постоянной основе через модульные параметры (типичный путь для систем с modprobe, но убедитесь, что он актуален для вашего дистрибутива):

echo "options zfs zfs_arc_max=8589934592" > /etc/modprobe.d/zfs.conf
update-initramfs -u   # Debian/Ubuntu
# или
dracut -f             # RHEL/Fedora-based

После этого потребуется перезагрузка, чтобы модуль ZFS подхватил параметр при загрузке.

Конкретное значение лимита — это не универсальная константа, а результат расчёта под ваш сервер: сколько RAM в системе, сколько гарантированно нужно оставить другим приложениям, и насколько критична для вас скорость чтения ZFS. Практический совет — не берите число «из интернета», а протестируйте выбранный лимит под реальной нагрузкой: запустите свой типичный рабочий сценарий (те же запросы к базе, тот же профиль чтения файлов), понаблюдайте за arc_summary и поведением остальных приложений в течение нескольких часов или дней, и скорректируйте значение по факту, а не по теории.

Также стоит учитывать нижнюю границу zfs_arc_min — если задать её слишком высокой, ARC не будет уменьшаться ниже этого порога даже при острой нехватке памяти у других процессов, что фактически превращает часть кеша в жёстко зарезервированную память. Обычно нижний лимит трогают реже, чем верхний, но для серверов с плотным сожительством разных нагрузок стоит знать, что этот параметр тоже существует.

Мониторинг: hit ratio важнее занятого объёма памяти

Если вы хотите понимать, действительно ли ARC приносит пользу, а не просто занимает память впустую, смотрите не на абсолютный размер занятой RAM, а на коэффициент попаданий кеша (hit ratio) — долю запросов чтения, которые ARC смог обслужить из памяти, не обращаясь к диску.

arc_summary | grep -A 5 "ARC size"
arc_summary | grep -i "hit"

Или разбор по счётчикам напрямую:

cat /proc/spl/kstat/zfs/arcstats | grep -E "^hits|^misses"

Hit ratio считается как hits / (hits + misses). Высокий и стабильный hit ratio (условно — заметно выше половины запросов, точный «хороший» порог зависит от профиля нагрузки) говорит о том, что ARC реально ускоряет вашу рабочую нагрузку: большая часть запросов чтения находит нужные данные в памяти. Низкий hit ratio при этом большом занятом объёме памяти — сигнал, что рабочий набор данных просто больше, чем помещается в текущий ARC, либо паттерн доступа настолько случайный, что повторные чтения одних и тех же блоков редки и кеш здесь мало что решает.

Занятый объём памяти сам по себе — плохая метрика эффективности: он почти всегда будет большим, потому что так задуман механизм. А вот падение hit ratio на графике во времени — куда более осмысленный сигнал: он показывает, что либо рабочий набор данных вырос, либо лимит ARC стал недостаточным для текущей нагрузки, и стоит пересмотреть zfs_arc_max или добавить памяти на сервер.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

ARC — это то же самое, что кеш чтения на уровне контроллера диска или SSD-кеш?

Нет, это разные уровни. ARC — программный кеш в оперативной памяти на уровне файловой системы ZFS. Аппаратный кеш контроллера и SSD-кеш (например, L2ARC как расширение ARC на быстром SSD) — отдельные механизмы, которые могут работать вместе с ARC, но не заменяют его.

Можно ли полностью отключить ARC?

Формально можно установить zfs_arc_max в минимально допустимое значение, но это резко снизит производительность чтения, потому что ZFS лишится основного механизма ускорения повторных операций. Обычно смысла в этом нет — правильнее подобрать разумный лимит, а не отключать кеш целиком.

После настройки zfs_arc_max память сразу освободится?

Уменьшение лимита ограничивает рост ARC вперёд, но уже занятая память освобождается постепенно, по мере работы механизма вытеснения, а не мгновенно в момент применения параметра.

Почему ARC растёт заново после перезагрузки сервера?

Потому что кеш живёт только в оперативной памяти и не переживает перезагрузку — это ожидаемо. После рестарта ARC снова начинает с нуля и постепенно накапливает горячие данные по мере чтений, точно так же, как в первый раз.

Если у меня мало RAM, стоит ли вообще беспокоиться про ARC?

Если система в целом укладывается по памяти без ARC, то само по себе кеширование только помогает. О явном лимите стоит задуматься, когда на том же сервере есть другие приложения, которым нужна гарантированная память — подробнее о балансе RAM и ZFS мы разбирали в статье про сколько памяти нужно ZFS на самом деле.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →