Тонкая настройка планировщика ввода-вывода
Диск в сервере не обрабатывает запросы в том порядке, в котором их отправило приложение — между файловой системой и физическим накопителем стоит планировщик ввода-вывода (I/O scheduler), который решает, что отправить первым, а что можно объединить. Для механического HDD правильный выбор экономит реальные миллисекунды на каждой операции, а для SSD и NVMe та же логика иногда просто тратит CPU впустую. Разберёмся, как это устроено и что стоит поменять на вашем сервере.
Содержание
Что делает планировщик ввода-вывода
Когда процесс читает или пишет данные, запрос не летит на диск мгновенно. Он попадает в очередь блочного слоя ядра (block layer), и уже оттуда планировщик решает:
- в каком порядке отдать накопившиеся запросы устройству;
- какие соседние по адресам запросы объединить (merge) в один, чтобы сократить число операций;
- как балансировать между чтением и записью, чтобы ни один тип запросов не голодал бесконечно.
Современные ядра Linux (начиная примерно с 4.x, окончательно — после отказа от старого однопоточного блочного слоя в пользу multi-queue block layer, blk-mq) работают именно через blk-mq. У каждого устройства свой набор доступных планировщиков, и увидеть их можно прямо из /sys:
cat /sys/block/sda/queue/scheduler
[mq-deadline] kyber bfq none
Планировщик в квадратных скобках — активный, остальные — просто доступны для переключения.
Важно понимать: планировщик — это прослойка ядра. Она ничего не знает про реальную физику накопителя, кроме того, что ей сообщил драйвер устройства (в частности, флаг rotational — вращается диск или нет). И вся её работа имеет смысл ровно настолько, насколько дорого стоит физическое перемещение внутри устройства.
Почему для HDD переупорядочивание — это реальная экономия
У механического жёсткого диска есть головка чтения-записи, которая физически перемещается над вращающимися пластинами. Чтобы прочитать данные с другой дорожки, головке нужно физически туда переместиться (seek time) и дождаться, пока нужный сектор подъедет под неё (rotational latency). Это не абстракция — это миллисекунды, и на фоне скорости самой передачи данных они огромны.
Отсюда классическая идея планировщиков эпохи HDD (в старых ядрах — CFQ, deadline; сейчас их наследник — mq-deadline): сортировать и группировать запросы по номерам секторов (LBA), а не по времени поступления. Это называют elevator-алгоритмом по аналогии с лифтом — он не бегает туда-сюда за каждым пассажиром, а едет в одну сторону, забирая всех попутных.
Что конкретно это даёт на HDD:
- запросы к близко расположенным на диске данным обрабатываются вместе — головка делает один проход вместо нескольких;
- при интенсивной нагрузке снижается суммарное время seek;
- deadline-компонент следит, чтобы старые запросы (особенно чтение) не откладывались бесконечно ради оптимальной сортировки — у каждого запроса есть предельный срок.
Для дисковой нагрузки на вращающихся носителях этот механизм даёт измеримый выигрыш, особенно при случайном доступе многих процессов одновременно — типичная картина на файловом сервере или базе данных на HDD.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПочему на SSD и NVMe та же логика теряет смысл
У флеш-памяти нет головки и нет механического перемещения. Произвольный доступ к любому адресу стоит примерно одинаково — что соседний блок читать, что противоположный конец накопителя. Контроллер SSD и так параллельно работает с десятками флеш-чипов, и у него собственная логика распределения запросов (wear leveling, внутреннее переупорядочивание на уровне FTL — flash translation layer), которая уже учитывает реальную физику устройства лучше, чем планировщик ядра.
Из этого следует не слишком очевидный, но важный вывод: сортировка запросов ради минимизации несуществующего механического перемещения — это работа, которая ничего не даёт устройству, но стоит CPU-времени планировщику. В лучшем случае — нейтрально. В худшем — на очень быстрых NVMe с задержками в единицы-десятки микросекунд, лишняя обработка в планировщике (взять блокировку очереди, отсортировать, смержить) может занять сопоставимое или большее время, чем сама операция на устройстве заняла бы без всякой сортировки.
NVMe усугубляет ситуацию архитектурно: протокол изначально спроектирован для параллелизма — до 65535 очередей команд по 65535 команд в каждой, с прямым доступом без единой точки конкуренции. Многоуровневая программная сортировка запросов поверх этого — это борьба с проблемой, которой на таком устройстве уже почти не существует.
Практическая рекомендация: none/noop против mq-deadline
Отсюда рабочее правило, которое стоит проверять, а не принимать слепо:
| Тип накопителя | Разумный планировщик по умолчанию | Почему |
|---|---|---|
| HDD (вращающийся, rotational=1) | mq-deadline (или bfq, если важна честность между процессами) | Реальная физическая экономия на seek |
| SATA/SAS SSD | mq-deadline или none — зависит от контроллера и очереди | Механики нет, но однопоточная очередь у части устройств иногда всё же выигрывает от минимальной сортировки |
| NVMe | none | Параллелизм и низкие задержки устройства делают программную сортировку избыточной |
| Диск внутри VPS (virtio-blk/virtio-scsi) | none в госте | Реальное планирование уже произошло на хосте; повторять его в гостевой ОС бессмысленно |
none (в старых ядрах — noop) — это минимальный планировщик: он делает базовое слияние соседних запросов и отдаёт их устройству практически в порядке поступления, без сложной сортировки по LBA и без дедлайнов. Это не «отсутствие планировщика» в буквальном смысле, а самый дешёвый по CPU вариант — прямая передача с минимумом накладных расходов.
Стоит отметить честно: многие современные дистрибутивы уже сами выбирают разумный планировщик по умолчанию — ядро смотрит на глубину аппаратной очереди устройства и часто ставит none для NVMe и mq-deadline для однопоточных SATA-устройств автоматически, ещё на этапе загрузки. Поэтому первый шаг — не менять, а проверить, что реально стоит сейчас.
Как проверить текущий планировщик
Определите тип накопителя — вращающийся он или нет:
cat /sys/block/sda/queue/rotational
1 — механический HDD, 0 — SSD или NVMe.
Посмотрите доступные и активный планировщик для конкретного устройства:
cat /sys/block/sda/queue/scheduler
cat /sys/block/nvme0n1/queue/scheduler
Вывод вида [mq-deadline] kyber none означает, что активен mq-deadline, а kyber и none доступны для переключения. На части систем bfq тоже присутствует в списке — это планировщик с упором на честность между процессами (полезен на десктопах и multi-tenant нагрузках, но дороже по CPU, чем none).
Как сменить планировщик — временно и навсегда
Временная смена (до перезагрузки) — просто запишите имя нужного планировщика в тот же файл:
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler
echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler
Это изменение живёт только до перезагрузки — при рестарте вернётся значение по умолчанию, выбранное ядром или дистрибутивом. Для постоянной настройки есть два пути.
Через udev-правило (предпочтительный современный способ, реагирует на конкретное устройство по его характеристикам, а не по имени, которое может меняться):
# /etc/udev/rules.d/60-io-scheduler.rules
ACTION=="add|change", KERNEL=="sd[a-z]|nvme[0-9]n[0-9]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="none"
ACTION=="add|change", KERNEL=="sd[a-z]|nvme[0-9]n[0-9]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="mq-deadline"
После создания файла примените правило без перезагрузки:
sudo udevadm control --reload-rules
sudo udevadm trigger
Через параметр загрузки ядра (более грубый способ — задаёт планировщик глобально, без учёта разных типов устройств в одной системе). В /etc/default/grub добавьте в GRUB_CMDLINE_LINUX:
elevator=none
и обновите загрузчик (sudo update-grub на Debian/Ubuntu или sudo grub2-mkconfig -o /boot/grub2/grub.cfg на RHEL-подобных). Стоит знать, что параметр elevator= в современных ядрах частично устарел и на некоторых версиях игнорируется в пользу udev-правил — поэтому если у вас смешанная инфраструктура с HDD и SSD на одном хосте, udev-правило по флагу rotational — надёжнее и гибче.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Нужно ли менять планировщик, если я не заметил проблем с диском?
Нет. Если производительность и так устраивает, трогать планировщик «на всякий случай» не стоит — эффект от смены обычно заметен только под конкретной нагрузкой (много параллельных случайных запросов, чувствительность к хвостовым задержкам), а не всегда и не везде.
Как понять, что смена планировщика реально помогла, а не просто «звучит логично»?
Замерьте вашу реальную нагрузку до и после — например, утилитой fio с параметрами, близкими к вашему профилю (случайное чтение/запись, размер блока, глубина очереди), а не абстрактным синтетическим тестом. Если вы уже проверяли статус медленного диска на VPS, это тот же принцип: сначала измерение, потом вывод.
На VPS вообще есть смысл трогать планировщик внутри гостевой ОС?
Обычно нет. Реальное планирование операций к физическому накопителю происходит на хосте, а гостевая ОС видит виртуальный блочный диск (virtio-blk или virtio-scsi). Дополнительная сортировка запросов внутри гостя — лишний слой поверх уже отработавшего слоя хоста; none в госте, как правило, разумный выбор по умолчанию.
Чем none отличается от noop из старых ядер?
По сути это преемник: noop был планировщиком старого однопоточного блочного слоя, none — его аналог в multi-queue block layer (blk-mq). Функционально оба делают минимум: базовое слияние соседних запросов и передачу устройству в порядке поступления, без сортировки по LBA и без дедлайнов.
Планировщик влияет на IOPS, которые заявляет провайдер?
Косвенно и слабо — он не создаёт и не ограничивает производительность устройства, а лишь меняет порядок доставки уже существующих запросов. Если хотите разобраться, почему заявленные цифры IOPS часто недостижимы на практике, это отдельная тема — см. что такое IOPS на самом деле.
А есть разница между SATA SSD и NVMe в этом вопросе?
Да. У NVMe изначально протокол спроектирован под параллелизм и минимальные задержки, поэтому эффект от none там обычно нагляднее. На SATA SSD аппаратная очередь скромнее, и разница между none и mq-deadline часто в пределах погрешности измерения — сравнение стоит смотреть в статье NVMe против SATA SSD на сервере.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →