Выберите продукт

NVMe в Linux: scheduler, read_ahead, discard и mount options для минимальной latency и максимальных IOPS

Разбираем настройки, которые реально влияют на NVMe в Linux: scheduler (none и mq-deadline), read_ahead, TRIM через discard или fstrim, а также mount options для ext4 и xfs. Дадим команды проверки, безопасные значения и подход для тюнинга по измерениям.
NVMe в Linux: scheduler, read_ahead, discard и mount options для минимальной latency и максимальных IOPS

NVMe-диски обычно «быстрые из коробки», но на реальной серверной нагрузке (БД, очереди, логи, кэш, контейнеры) упираются не в «скорость чтения в МБ/с», а в latency (задержки) и стабильность IOPS. В Linux на это чаще всего влияют три группы настроек:

  • планировщик ввода-вывода (scheduler): none, mq-deadline и др.;
  • read-ahead: как агрессивно ядро читает наперёд;
  • discard/TRIM: онлайн через mount option discard или пакетно через fstrim.

Ниже — практичный чек-лист: как посмотреть текущие значения, как менять безопасно и какие комбинации обычно дают лучший баланс для NVMe в продакшене.

0) Сначала определите, что у вас за NVMe и где узкое место

На VDS NVMe часто «видится» как /dev/nvme0n1, но фактическая производительность может ограничиваться слоем виртуализации, политиками I/O на узле или соседями. Перед тюнингом полезно зафиксировать базовую линию: какие сейчас задержки и как ведёт себя очередь.

Проверяем устройство, ФС и параметры очереди

lsblk -o NAME,TYPE,SIZE,ROTA,DISC-GRAN,DISC-MAX,MOUNTPOINT,FSTYPE
nvme list 2>/dev/null
cat /sys/block/nvme0n1/queue/scheduler
cat /sys/block/nvme0n1/queue/nr_requests
cat /sys/block/nvme0n1/queue/read_ahead_kb

На что смотреть в первую очередь:

  • ROTA должен быть 0 (не HDD).
  • DISC-GRAN/DISC-MAX</code показывают, поддерживается ли discard/TRIM на уровне блочного устройства.</li> <li>Текущий scheduler отмечается в квадратных скобках, например: <code>none [mq-deadline].

Смотрим latency на живой системе

iostat -x 1

В выводе интереснее всего:

  • r_await, w_await — средние задержки чтения/записи;
  • aqu-sz — средняя глубина очереди;
  • %util — признак «упора» (в виртуализации интерпретируйте осторожно).

Если цель — «быстрее отклик» веба и БД, оптимизируйте не MB/s, а хвосты задержек. NVMe может показывать высокий throughput, но при неудачных настройках scheduler/read-ahead получить «пилу» latency.

1) Scheduler для NVMe: none или mq-deadline

Для NVMe в современных ядрах чаще всего выбор сводится к двум адекватным вариантам:

  • none — минимум накладных расходов, ядро почти не «рулит» очередями, отдавая это устройству/драйверу;
  • mq-deadline — добавляет дедлайны и упорядочивание, часто улучшает предсказуемость latency при смешанной нагрузке.

Практическая логика выбора

Выбирайте none, если:

  • нагрузка в основном случайная и параллельная (БД, key-value, очереди);
  • важны максимальные IOPS и минимальная средняя latency;
  • ниже по стеку нет «хитрых» ограничений, требующих сглаживания.

Выбирайте mq-deadline, если:

  • есть смешанный профиль (и последовательные, и случайные операции);
  • наблюдаете скачки задержек под нагрузкой (особенно на записи);
  • нужна более стабильная latency, пусть иногда ценой небольшого падения пиковых IOPS.

Переключить scheduler на лету (до перезагрузки)

cat /sys/block/nvme0n1/queue/scheduler
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler
cat /sys/block/nvme0n1/queue/scheduler

Или так:

echo mq-deadline | sudo tee /sys/block/nvme0n1/queue/scheduler
cat /sys/block/nvme0n1/queue/scheduler

Сделать scheduler постоянным (udev правило)

Один из самых переносимых вариантов — через udev, чтобы применялось именно к NVMe:

sudo tee /etc/udev/rules.d/60-nvme-scheduler.rules >/dev/null <<'EOF'
ACTION=="add|change", KERNEL=="nvme*n*", ATTR{queue/scheduler}="none"
EOF
sudo udevadm control --reload-rules
sudo udevadm trigger --type=devices --action=change

Если хотите глубже разобраться в диагностике дисковой подсистемы и верификации тюнинга, держите под рукой статью про практику замеров и инструменты: iostat/iotop/fio и выбор scheduler.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Если вы подбираете сервер под БД или высокие IOPS, удобнее всего тестировать такие настройки на отдельной машине с гарантированными ресурсами. Для этого подойдёт VDS, где можно быстро менять ядро/дистрибутив, разносить роли по дискам и валидировать результаты под своей нагрузкой.

Проверка iostat и параметров очереди NVMe в Linux

2) read_ahead для NVMe: когда снижать, когда повышать

read_ahead задаёт, сколько килобайт ядро будет читать «на опережение» при последовательном чтении. На NVMe цена лишнего чтения ниже, чем на HDD, но в продакшене завышенный read-ahead всё равно может:

  • раздувать page cache и вытеснять полезные данные;
  • ухудшать latency на случайном чтении при конкуренции потоков;
  • создавать лишний фон I/O и влиять на хвосты задержек.

Посмотреть текущий read-ahead

cat /sys/block/nvme0n1/queue/read_ahead_kb
lsblk -o NAME,RA,MOUNTPOINT

Рекомендации по значениям (стартовые точки)

  • БД/очереди/образы VM (случайный доступ): часто лучше 128–256 KB.
  • стриминг/бэкапы/большие файлы (последовательное): уместно 512–2048 KB.
  • универсальный старт для NVMe на сервере: 256 KB.

Установить read-ahead на лету

sudo blockdev --setra 512 /dev/nvme0n1
sudo blockdev --getra /dev/nvme0n1
cat /sys/block/nvme0n1/queue/read_ahead_kb

Важно: blockdev --setra задаётся в секторах по 512 байт. То есть 512 = 256 KB. Проверяйте себя через read_ahead_kb в sysfs.

3) Discard/TRIM: mount option discard vs fstrim

На SSD/NVMe полезно возвращать неиспользуемые блоки устройству (TRIM), чтобы контроллеру было проще с garbage collection и чтобы запись держала стабильные IOPS. В Linux есть два подхода:

  • онлайн discard — mount option discard;
  • пакетный TRIM — периодический fstrim (systemd timer или cron).

Почему discard нередко ухудшает latency

С включённым discard освобождение блоков может превращаться в дополнительные операции прямо во время работы (удаление файлов, освобождение extents). На части стэков хранения это добавляет задержки на запись, особенно при частых delete/overwrite (логирование, временные файлы, контейнерные слои).

Поэтому для серверов чаще выбирают практичную схему: не держать discard включённым постоянно, а использовать fstrim по расписанию.

Проверить поддержку discard на уровне блока

lsblk -D

Если DISC-GRAN и DISC-MAX не нули — TRIM поддерживается. Если нули — слой ниже, вероятно, его не пропускает, и ждать эффекта от discard/fstrim не стоит.

Запустить TRIM вручную

sudo fstrim -av

Флаг -v покажет, сколько было «обрезано». Если значения стабильно 0, обычно это означает либо регулярный TRIM уже работает, либо нижний слой discard игнорирует.

Включить регулярный fstrim (systemd)

systemctl status fstrim.timer
sudo systemctl enable --now fstrim.timer
systemctl list-timers | grep fstrim

На многих дистрибутивах таймер уже настроен на еженедельный запуск — для большинства сценариев этого достаточно. По теме TRIM, mount options и типичных ошибок можно дополнительно свериться со статьёй: fstrim vs discard и опции монтирования SSD.

4) Mount options для ext4 и xfs на NVMe: что реально влияет

Опции монтирования — тонкая настройка поведения файловой системы. Она может улучшить предсказуемость и снизить лишние записи, но может и навредить, если включить «ускоряющие» параметры без понимания рисков. Ниже — безопасные и практичные рекомендации для ext4 и xfs с фокусом на latency/IOPS.

Общие безопасные опции

  • relatime обычно уже стоит по умолчанию и часто достаточно.
  • noatime уменьшает лишние записи метаданных при чтении и полезен там, где точно не нужен atime.
  • Не включайте discard «по привычке», если ваша цель — минимальная latency на записи.

Проверить текущие опции монтирования:

findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS

ext4: рекомендации и нюансы

Для ext4 на NVMe чаще всего достаточно дефолтов плюс аккуратные изменения:

  • noatime (или оставить relatime, если сомневаетесь);
  • не включать data=writeback ради скорости: это компромисс по целостности;
  • с опцией commit= не торопитесь: она меняет частоту коммитов журнала и требует понимания допустимой потери последних секунд данных при аварии питания/краше.

Пример строки в /etc/fstab (как ориентир):

UUID=xxxx-xxxx  /data  ext4  defaults,noatime  0  2

xfs: рекомендации и нюансы

XFS часто выбирают за предсказуемость на больших объёмах и хорошее поведение при параллельной нагрузке. Для NVMe:

  • noatime — по тем же причинам;
  • параметры вроде logbsize= и другие — не трогайте без причины и измерений;
  • discard — с теми же оговорками, что и для ext4.

Пример для /etc/fstab:

UUID=yyyy-yyyy  /data  xfs  defaults,noatime  0  2

Опции монтирования ext4 и xfs для NVMe: пример fstab и findmnt

5) Мини-методика тюнинга: меняем по одному параметру и меряем

Чтобы не получить «кажется стало лучше», действуйте итеративно: одно изменение — замер — вывод.

Шаг 1: зафиксируйте базовую линию

iostat -x 1

Если есть возможность, прогоняйте тестовую нагрузку на отдельном разделе/файле и в тихое окно. Синтетика на проде легко мешает живым сервисам, особенно если вы тестируете запись.

Шаг 2: выберите scheduler

Начните с none. Если видите хвосты задержек и «пилу» на записи — попробуйте mq-deadline и сравните не только средние await, но и «ровность» работы приложения.

Шаг 3: настройте read-ahead под профиль

Для большинства серверных NVMe-сценариев рабочая стартовая точка — 256 KB. Для чистого sequential (бэкапы, раздача больших файлов) повышайте. Для случайного доступа и конкуренции потоков — понижайте.

Шаг 4: TRIM делайте пакетно

Оставьте discard выключенным и включите fstrim.timer. Исключение — только если вы измерениями подтвердили, что онлайн discard не добавляет задержек на вашем стеке хранения.

6) Частые ошибки, из-за которых NVMe «внезапно медленный»

  • Тюнинг без измерений: поменяли scheduler/read-ahead и не зафиксировали latency до/после.
  • Включили discard везде и получили микрофризы на записи при delete/rotate логов.
  • Слишком высокий read-ahead на случайной нагрузке: лишний I/O и вытеснение page cache.
  • Смешали уровни оптимизаций (ФС, LVM, RAID, виртуализация) и потеряли понимание, где именно теряются IOPS.

7) Короткие рецепты по ролям нагрузки

Веб + PHP/Node + небольшая БД на одном NVMe

  • scheduler: none (если хвосты — mq-deadline);
  • read-ahead: 256 KB;
  • TRIM: fstrim.timer, без discard;
  • mount options: noatime (или оставить relatime).

PostgreSQL/MySQL (latency и IOPS важнее throughput)

  • scheduler: чаще none, но при нестабильной записи пробуйте mq-deadline;
  • read-ahead: 128–256 KB;
  • TRIM: пакетно через fstrim.

Бэкапы/архивы/статический контент (последовательное чтение)

  • scheduler: none;
  • read-ahead: 1024–2048 KB (проверяйте эффект замерами);
  • TRIM: fstrim.

Итог

Для NVMe в Linux чаще всего «выстреливает» простой набор: scheduler none или mq-deadline по результатам измерений, read_ahead около 256 KB под смешанную серверную нагрузку и отказ от постоянного discard в пользу регулярного fstrim. Это помогает сделать latency ровнее, а IOPS — стабильнее.

Если хотите довести тюнинг до «идеала», следующий шаг — профилировать именно вашу нагрузку (что читает/пишет, какими блоками, как выглядит очередь), и только после этого аккуратно править параметры, не смешивая сразу десяток изменений.

Поделиться статьей

Вам будет интересно

Linux: apt/yum/dnf зависают — проверяем DNS, IPv6 и MTU (практический чеклист) OpenAI Статья написана AI (GPT 5)

Linux: apt/yum/dnf зависают — проверяем DNS, IPv6 и MTU (практический чеклист)

Если apt update висит на Connecting, yum/dnf уходят в таймаут, а веб «вроде работает», чаще всего виноваты DNS (включая systemd-re ...
IPVS в Docker Swarm: как работает VIP/ingress и что ломается на проде OpenAI Статья написана AI (GPT 5)

IPVS в Docker Swarm: как работает VIP/ingress и что ломается на проде

Docker Swarm по умолчанию балансирует сервисы через VIP и ingress-сеть, опираясь на IPVS и netfilter. Разберём путь пакета для pub ...
Linux traffic shaping: tc и nftables для ingress/egress лимитов OpenAI Статья написана AI (GPT 5)

Linux traffic shaping: tc и nftables для ingress/egress лимитов

Разберём практический контроль полосы на Linux: egress shaping через tc, ingress через IFB, очереди fq_codel/CAKE, HTB для деления ...