Производительность ZFS в 2026: настройка пулов и тюнинг под нагрузку | AdminWiki

Производительность ZFS в 2026: настройка пулов и тюнинг под нагрузку

22 сентября 2026 14 мин. чтения
Содержание статьи

С чего начать оптимизацию ZFS: порядок действий

Оптимизацию ZFS начинают с измерений, а не с правки параметров ядра. Рабочий порядок такой: снять baseline, определиться с топологией vdev, подобрать размер блока, настроить ARC и L2ARC, поставить SLOG под синхронные записи, включить компрессию и только потом оценивать необходимость дедупликации. Каждый шаг затрагивает более крупный фактор, чем следующий за ним, поэтому время расходуется по убывающей отдаче.

Топология vdev и размер блока дают основную часть прироста скорости. Тюнинг параметров вида zfs_arc_max или zfs_txg_timeout помогает на узком наборе сценариев и редко меняет картину целиком. Дедупликация при нехватке RAM работает в минус: запись замедляется, а таблица блоков вытесняет полезный кэш.

Руководство ориентировано на TrueNAS SCALE и CORE, Proxmox VE и любой Linux или FreeBSD с OpenZFS 2.2 и новее. Принципы одинаковы для всех платформ, различаются только способы задать параметры: в TrueNAS часть значений выставляется через UI, в Proxmox и чистом Debian или Ubuntu - через командную строку и modprobe. Команды проверяйте на тестовом пуле, а перед работой на продакшене снимайте снапшот.

Правило «сначала измерь, потом меняй»

Без baseline вы не отличите ускорение от естественных колебаний нагрузки. Снимите три среза подряд в одинаковых условиях (та же нагрузка, тот же час суток), запишите цифры и только после этого что-то меняйте.

zpool iostat -v 5
arcstat 5
iostat -x 5

В выводе zpool iostat -v важны две колонки: пропускная способность и задержка каждого vdev по отдельности. iostat -x показывает await (среднее время ожидания операции, мс) и %util (загрузка диска в процентах). arcstat 5 даёт hit ratio и текущий размер ARC.

Дальше зафиксируйте точку возврата:

zfs snapshot pool/dataset@before-tuning

Для датасетов это защищает файлы. Для zvol и параметров пула снапшот не поможет: volblocksize и топологию vdev после создания не изменить, поэтому такие вещи планируют заранее. Меняйте один параметр за раз и повторяйте замер после каждого изменения. Иначе причина ускорения или деградации останется неизвестной.

Что реально влияет на скорость, а что - миф

ФакторВлияниеКомментарий
Топология vdevВысокоеОпределяет IOPS и отказоустойчивость, меняется только пересозданием пула
recordsize / volblocksizeВысокоеВлияет на амплификацию записи и объём трафика при чтении
Объём RAM под ARCВысокоеХит-рейт кэша напрямую зависит от размера рабочего набора
SLOGСреднееПомогает только там, где есть синхронные записи
КомпрессияСреднееlz4 почти бесплатен по CPU и экономит полосу диска
L2ARCНизкое-среднееЭффект заметен только при рабочем наборе больше RAM
ДедупликацияНизкое, часто отрицательноеDDT расходует RAM и замедляет запись
sysctl zfs_*ТочечноеОправдан на конкретных профилях нагрузки, не как универсальное средство

Практический вывод из таблицы: сначала железо и геометрия пула, затем размер блока, затем кэши. Если после этого скорости всё ещё не хватает, смотрите на sysctl.

Топология vdev: как выбрать пул под нагрузку

Пул ZFS собирается из vdev, и потолок производительности задают их количество и тип. Запомните формулу: IOPS пула примерно равны IOPS одного диска, умноженным на число vdev. Ёмкость складывается независимо от этого.

Пример на восьми NVMe. Четыре зеркала по два диска дают около четырёх IOPS одного накопителя. Один raidz2 из восьми дисков даёт примерно один IOPS одного накопителя, зато шесть дисков из восьми работают на полезную ёмкость вместо четырёх при зеркалах. Отсюда развилка: IOPS нужны базам данных и виртуальным машинам, ёмкость нужна архивам и файловым шарам.

Mirror vs RAIDZ: что важнее - IOPS или ёмкость

Тип нагрузкиРекомендуемая топологияПочему
PostgreSQL, MySQLMirrorСлучайные операции 8K-16K, нужен параллелизм нескольких vdev
Виртуальные машины (Proxmox VE)MirrorСмешанная случайная нагрузка, критичен IOPS
Файловое хранилище, резервные копииraidz2Последовательный доступ, приоритет ёмкости и отказоустойчивости
Медиа-архивraidz2 или raidz3Большие файлы, редкая запись, длительный срок хранения

raidz1 на дисках объёмом больше 2 ТБ к 2026 году потерял смысл: при замене диска resilver читает весь vdev и на нагруженной системе может идти сутками, а второй отказ за это время обрушит пул. raidz2 закрывает этот риск ценой одного дополнительного диска.

Мелочи, которые ломают схему чаще, чем ошибочный выбор уровня RAID:

  • ashift=12 при создании пула. Со значением 9 на диске с 4K-секторами каждая запись читает-изменяет-пишет больше, чем нужно.
  • Одинаковые диски в одном vdev. Смешивание моделей и объёмов ухудшает предсказуемость задержек, а raidz работает по самому медленному участнику.
  • Добавить диск в существующий raidz нельзя. Расширение идёт только новым vdev целиком, и это снова умножает IOPS, а не размазывает ёмкость одного.

Подробный разбор конфигураций и ошибок проектирования с примерами собран в статье про архитектуру vdev в пулах ZFS.

Специальные vdev: special, log, cache, spare

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

  • special хранит метаданные и, при настройке special_small_blocks, мелкие блоки на быстрых NVMe. Ускоряет нагрузки с миллионами мелких файлов и диски виртуальных машин. Потеря special vdev означает потерю всего пула, поэтому минимум mirror из двух устройств, а лучше из трёх.
  • log (SLOG) принимает синхронные записи и существует только для них.
  • cache (L2ARC) работает на чтение и при выходе из строя ничего не ломает.
  • spare это горячий резерв, который ZFS автоматически подставит вместо отказавшего диска.

Добавляют их командами вида zpool add pool special mirror nvme0n1 nvme1n1. Запомните отдельно: special vdev без зеркала превращает быстрый пул в пул с единой точкой отказа по метаданным.

recordsize и volblocksize: главный рычаг под тип нагрузки

Разделяйте два параметра. recordsize задаёт максимальный размер блока для датасета с файлами. volblocksize задаёт размер блока для zvol, то есть для блочного устройства, которое используют как диск виртуальной машины или LUN для iSCSI.

Изменение recordsize применяется к новым записям, старые блоки остаются прежнего размера. volblocksize фиксируется в момент создания zvol и после этого не меняется. Единственный способ исправить ошибку - создать новое блочное устройство и перенести содержимое.

НагрузкаПараметрЗначение
PostgreSQL, MySQL на датасетеrecordsize16K, для отдельных сценариев 8K
Общие файлыrecordsize1M (значение по умолчанию)
Смешанная нагрузка с мелкими файламиrecordsize128K
Диск VM Windows или Linuxvolblocksize16K
БД внутри виртуальной машиныvolblocksize8K
Медиатекаrecordsize1M

Почему 1M по умолчанию - не всегда хорошо

Блок 1M хорош для последовательного чтения и записи больших файлов. Для базы данных с 8K-страницами он превращает каждую мелкую запись в цикл чтение-изменение-запись: ZFS поднимает блок 1M, меняет в нём 8K и записывает блок целиком. Диск получает трафик, которого могло не быть, а latency растёт пропорционально.

Для PostgreSQL учитывайте full_page_writes и размер страницы. Совпадение recordsize с 16K снижает амплификацию записи. У MySQL с InnoDB страница тоже 16K, поэтому цифра совпадает с настройками самого движка. Ставить recordsize меньше 8K не стоит: растёт число блоков, объём метаданных и накладные расходы на их обработку.

volblocksize для zvol: почему 16K - безопасный выбор

16K это компромисс для дисков виртуальных машин: значение подходит Windows и Linux, а также смешанной нагрузке без выраженного профиля. Если внутри VM работает база данных с 8K-страницами, ставьте 8K. Меньше 8K ведёт к росту накладных расходов и фрагментации, больше 16K даёт ту же амплификацию, что и 1M recordsize у баз данных.

В Proxmox VE volblocksize задаётся при создании ZFS-хранилища в разделе Datacenter, Storage, тип ZFS, параметр Blocksize. Для уже существующего хранилища значение на лету не меняется.

zfs set recordsize=16K pool/pgdata
zfs set compression=lz4 pool/pgdata
zfs create -V 100G -o volblocksize=16K pool/vm-100-disk-0

Проверить текущие значения можно через zfs get recordsize pool/dataset и zfs get volblocksize pool/zvol.

ARC и L2ARC: как не отдать всю RAM и получить кэш-хиты

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

Как выставить zfs_arc_max без вреда для ОС

Правило: оставьте операционной системе и приложениям 25-30% RAM. Пример для машины со 128 ГБ, где база данных просит 40 ГБ: держите ARC в диапазоне 64-80 ГБ.

options zfs zfs_arc_max=68719476736

Строку размещают в /etc/modprobe.d/zfs.conf, после чего обновляют initramfs (update-initramfs -u в Debian и Ubuntu, dracut -f в RHEL-подобных системах) и перезагружаются или перезапускают модуль. Значение указывается в байтах: 64 ГиБ это 68719476736.

Временная проверка без перезагрузки:

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

В TrueNAS параметр задают через System, Tunables: тип sysctl или loader в зависимости от версии, переменная zfs_arc_max. Для CORE настройки модуля применяются через loader.conf. Значение zfs_arc_min задавать не обязательно: оно задаёт нижнюю границу, ниже которой ARC не сжимается, и по умолчанию работает корректно.

L2ARC: когда NVMe-кэш реально ускоряет

L2ARC расширяет кэш на быстрый накопитель и работает только на чтение. Три условия, при которых он оправдан: рабочий набор больше доступной RAM, нагрузка преимущественно на чтение, hit ratio ARC стабильно ниже 90%. Если рабочий набор помещается в память, L2ARC ничего не добавит, а при низком хит-рейте он вытесняет полезные блоки из ARC и вредит.

zpool add pool cache nvme0n1
zfs set secondarycache=all pool

Потеря NVMe под L2ARC пул не разрушит: устройство не входит в состав данных. Параметр l2arc_headroom управляет тем, сколько данных ARC отдаёт в L2ARC; при недостатке RAM увеличивать его нет смысла. В OpenZFS 2.x поддерживается persistent L2ARC: содержимое кэша переживает перезагрузку, поэтому прогрев после рестарта занимает меньше времени.

Мониторинг кэша:

arcstat 5
arc_summary
cat /proc/spl/kstat/zfs/arcstats | grep -E "hits|misses"

Практические детали настройки кэшей, включая сравнение SLOG на Intel Optane и L2ARC на NVMe, разобраны в статье про кэширование в системах хранения.

SLOG и ZIL: ускоряем синхронные записи

ZIL это журнал намерений, куда ZFS пишет синхронные операции до их попадания в основной пул. SLOG это отдельное быстрое устройство, которое берёт журнал на себя. Для асинхронных записей ни ZIL, ни SLOG в горячем пути не участвуют, поэтому покупать NVMe под SLOG «на всякий случай» бессмысленно.

sync=standard vs sync=always: когда переключать

sync=standard оставляет решение за ZFS: она уважает флаг O_SYNC от приложения, а остальные записи проходит как асинхронные. Для базы данных с собственным WAL этого достаточно: СУБД сама управляет durability через fsync.

sync=always заставляет каждую запись ждать подтверждения от ZIL. Режим нужен там, где приложение рассчитывает на гарантию записи, а флага не выставляет: NFS, iSCSI, отдельные сценарии с виртуальными дисками. Без быстрого SLOG режим sync=always резко снижает IOPS, потому что каждая операция ждёт физической записи журнала.

zpool add pool log mirror nvme0n1 nvme1n1
zfs set sync=always pool/dataset

Эффект проверяют тем же zpool iostat -v 5: смотрите на задержки log-устройства и на то, изменилась ли картина по vdev с данными.

Почему обычный SSD без PLP - плохой SLOG

Смысл SLOG в том, что запись считается завершённой после попадания в энергозависимый кэш устройства. Если у накопителя нет защиты от потери питания (power-loss protection, PLP), содержимое кэша пропадёт при отключении питания, и гарантия, за которую вы платите режимом sync=always, исчезнет. Consumer NVMe в этой роли не подходит.

Подходящие варианты - накопители с PLP: Intel Optane, серверные NVMe уровня Samsung PM9A3, Kingston DC1500M и аналоги. Для продакшена стандарт это зеркало из двух SLOG-устройств, потому что выход из строя единственного SLOG при sync=always означает потерю последних записей.

Компрессия и дедупликация: что включать, а что нет

Компрессию включайте почти всегда, дедупликацию почти никогда. Это короткая версия ответа, дальше критерии.

lz4 vs zstd: когда zstd оправдан

lz4 это значение по умолчанию в OpenZFS: минимальная нагрузка на CPU и быстрая распаковка. Для баз данных lz4 обычно выгоднее всего, потому что экономит полосу диска и почти не занимает процессор.

zstd сжимает плотнее и требует больше CPU. Уровни 1-3 безопасны на серверном железе, выше имеет смысл подниматься только при свободных ядрах и явной выгоде по месту. Логи, JSON, текстовые выгрузки, резервные копии и виртуальные машины с большим объёмом повторяющихся блоков сжимаются zstd заметно лучше.

zfs set compression=lz4 pool/data
zfs set compression=zstd pool/logs
zfs get compressratio pool/data

Прирост от компрессии двойной: место и скорость, потому что с диска читается меньше блоков.

Дедупликация: почему в 2026 её всё ещё не стоит включать

Дедупликация ведёт таблицу DDT, в которой хранятся контрольные суммы и ссылки на блоки. Таблица живёт в оперативной памяти, а при нехватке уходит на диск, и тогда каждая запись требует дополнительного чтения и записи по DDT. Именно поэтому включение dedup без запаса RAM превращает быстрый пул в медленный.

Ориентир по памяти: порядок 5 ГБ RAM на каждый терабайт дедуплицированных блоков, точное значение зависит от размера блока, числа уникальных блоков и длины контрольной суммы. Считать нужно от реального объёма после дедупликации, а не от размера пула.

Дедупликация оправдана в узких случаях: например, десятки однотипных виртуальных машин VDI, где большая часть блоков совпадает, а RAM достаточно, чтобы DDT целиком держалась в ARC. Для логов, файловых шар и большинства баз данных ту же экономию дают компрессия, снапшоты и клоны, и обходится это дешевле.

Отключается дедупликация только через пересоздание датасета: параметр необратим без переноса данных. Алгоритм расчёта памяти и сравнение с компрессией приведены в статье про дедупликацию ZFS и её влияние на IOPS.

Готовые профили: БД, виртуализация, файловое хранилище

Три набора параметров закрывают большинство рабочих задач. Создавайте датасеты под каждый сценарий отдельно: настройки применяются на уровне датасета, а не пула.

Профиль для PostgreSQL и MySQL

zfs create -o recordsize=16K -o compression=lz4 -o atime=off -o xattr=sa -o logbias=throughput pool/pgdata
zfs set sync=standard pool/pgdata

Здесь recordsize=16K совпадает со страницей InnoDB и снижает амплификацию записи, logbias=throughput снимает часть нагрузки с ZIL для асинхронных операций, atime=off убирает лишние обновления метаданных при чтении, xattr=sa ускоряет работу с расширенными атрибутами. Размер блока ниже 8K не используйте.

Профиль для Proxmox VE и дисков VM

zfs create -V 100G -o volblocksize=16K -o compression=lz4 -o atime=off pool/vm-100-disk-0

volblocksize=16K подходит универсальным дискам. Если внутри VM работает база данных, берите 8K. При создании пула под виртуализацию фиксируйте ashift=12, а для нагрузок с синхронными записями добавляйте SLOG с PLP. Помните, что volblocksize не меняется после создания zvol, поэтому параметр выбирают до развёртывания.

Профиль для NFS/SMB файлового хранилища

zfs create -o recordsize=1M -o compression=lz4 -o atime=off -o xattr=sa -o acltype=posixacl pool/share

1M оптимален для больших файлов и последовательного доступа. Для смешанной нагрузки с обилием мелких файлов ставьте 128K. acltype=posixacl нужен для корректных прав в SMB-шарах. Если блоки хорошо сжимаются, замените lz4 на zstd уровня 1-3. В TrueNAS SCALE большинство этих параметров выставляется в свойствах датасета через UI.

Пошаговое создание пула, томов и настройка параметров под разные нагрузки описаны в статье про создание пула ZFS и оптимизацию.

Мониторинг и проверка результата

Изменения без проверки остаются предположениями. Ниже метрики, по которым видно, помогло ли вмешательство, и команды для регулярного съёма.

Ключевые метрики: ARC hit ratio и latency дисков

МетрикаОриентирЧто делать при отклонении
ARC hit ratioвыше 90% для рабочей нагрузкиНиже 70% означает нехватку RAM или рабочий набор больше кэша
await на SSDменьше 1 мсРост говорит о насыщении устройства или амплификации записи
await на HDDменьше 20 мсВыше 20 мс проверьте очереди и топологию vdev
%utilниже 80% в среднемСтабильно выше 80% указывает на перегрузку диска
zpool iostat -v 5
arcstat 5
arc_summary
iostat -x 5
zpool status

zpool status показывает ошибки чтения и записи, а также состояние resilver. Ошибки в колонках READ и WRITE это повод проверять кабели и накопители раньше, чем настраивать производительность.

fio: как измерить прирост до и после тюнинга

Для профиля базы данных используйте случайные операции 8K, для файлового хранилища - последовательные 1M. Ключевой флаг direct=1: он обходит кэш операционной системы, иначе замер покажет скорость RAM, а не дисков.

fio --name=randwrite --ioengine=libaio --rw=randwrite --bs=8k --numjobs=4 --size=10G --runtime=60 --group_reporting --direct=1
fio --name=seqread --ioengine=libaio --rw=seqread --bs=1m --numjobs=1 --size=20G --runtime=60 --group_reporting --direct=1

Запускайте тесты на отдельном датасете и в окно, когда продакшен не испытывает пиковой нагрузки. fio создаёт файлы и сам же их удаляет, поэтому места на пуле нужно с запасом. Замеры до и после изменения должны идти на одном и том же датасете с одинаковыми параметрами.

План отката: снапшот перед изменениями и zfs rollback при проблемах для датасетов; для параметров модуля возврат прежнего значения в /etc/modprobe.d/zfs.conf и повторное обновление initramfs. Для настроек пула и zvol откат означает перенос данных на пересозданные объекты, поэтому такие решения принимают до ввода системы в работу.

Типичные ошибки и как их избежать

  • raidz1 на дисках больше 2 ТБ. Долгий resilver и риск второго отказа во время восстановления. Решение: raidz2 минимум, для крупных массивов raidz3.
  • Дедупликация без запаса RAM. DDT уходит на диск и замедляет запись. Решение: компрессия, снапшоты и клоны вместо dedup.
  • SLOG на consumer SSD без PLP. Гарантия записи исчезает вместе с кэшем накопителя при отключении питания. Решение: серверный NVMe с PLP в зеркале.
  • recordsize=1M для базы данных. Каждая мелкая запись превращается в чтение-изменение-запись блока целиком. Решение: 16K или 8K для датасета СУБД.
  • L2ARC при небольшом рабочем наборе. Кэш ничего не добавляет, а при низком хит-рейте вытесняет блоки из ARC. Решение: сначала измерьте hit ratio и объём рабочего набора.
  • Попытка изменить volblocksize после создания zvol. Параметр фиксирован. Решение: планировать значение заранее, при ошибке переносить данные в новый zvol.
  • Тюнинг без baseline. Невозможно понять, что дало эффект. Решение: замер до, один параметр за раз, замер после.
  • Правка sysctl без понимания профиля нагрузки. Параметры вроде zfs_txg_timeout и zfs_prefetch_disable меняют поведение точечно. Решение: трогать их только после того, как исчерпаны топология, размер блока и кэши.

Чек-лист перед изменениями в продакшене: снимите baseline теми же командами, которыми будете мерить результат; сделайте снапшот датасетов; убедитесь, что знаете точку отката для каждого параметра; меняйте по одному параметру; держите под рукой zpool status и arcstat во время и после правок. Отдельно проверьте поведение системы при отключении питания и при имитации отказа диска: принцип Copy-on-Write и его практические последствия объясняют, почему снапшоты достаются почти бесплатно, а изменения геометрии пула почти всегда требуют пересоздания.

Поделиться:
Сохранить гайд? В закладки браузера