Дисковая подсистема остаётся одной из главных причин «мистических» просадок RPS и роста TTFB у веб‑сервисов. Даже при достаточном количестве CPU и RAM неправильные профили ввода‑вывода, неудачный планировщик или случайная фонова задача могут привести к лавинообразной деградации. В этой статье соберу рабочие приёмы диагностики и тестирования I/O на Linux с упором на веб‑нагрузку: покажу, как читать метрики iostat, как разоблачить «пожирателей диска» с помощью iotop, как воспроизводимо моделировать нагрузку через fio, и как осознанно выбрать I/O‑планировщик (scheduler) для разных типов носителей — HDD, SATA SSD, NVMe и виртуальных дисков.
Что именно мерить в I/O: латентность, IOPS, пропускная способность и глубина очереди
Для веб‑сервисов критичнее всего латентность операций записи/чтения и стабильность задержек под нагрузкой. Ширина канала (MB/s) важна для бэкапов и больших скачиваний, но в типичном API/динамическом сайте основная боль — это случайный доступ маленькими блоками (метаданные, журналы БД, сессии, кэш), где решают IOPS и хвостовые задержки (p95/p99). Дополняют картину глубина очереди (queue depth), процент загрузки диска (%util) и средний размер запроса. В связке они отвечают на базовый вопрос: «Мы упираемся в диск или причина не здесь?»
iostat: быстрый срез состояния диска и понимание контекста
iostat из пакета sysstat — удобный инструмент для периодического сэмплинга дисковых метрик. Веб‑админам полезен расширенный вывод с показателями по устройствам и разделам.
iostat -dxm 1
На что смотреть в первую очередь:
%util— приблизительная загруженность устройства. Длительное нахождение около 100% означает насыщение очереди.r/s,w/sиrkB/s,wkB/s— интенсивность и характер нагрузки (чтение/запись, случайная/последовательная).await(или раздельноr_await/w_awaitв новых версиях) — средняя задержка операции с учётом очереди. Для NVMe под смешанной нагрузкой десятки миллисекунд — уже тревожный сигнал.avgqu-szилиaqu-sz— средний размер очереди. Стабильно высокий на фоне выросших задержек намекает на перегруз диска.rareq-sz/wareq-sz— средний размер запросов. Полезно для понимания, насколько «мелкими» блоками работает приложение.
Полезные варианты запуска:
# Показать логические тома (LVM) и их вклад
iostat -N -dx 1
# Аггрегировать по всем устройствам, если важно общее состояние узла
iostat -dxy 1
Интерпретация: если %util прыгает под 100%, await растёт, а CPU простаивает — вероятно, «узкое горлышко» именно диск. Если же %util низкий, но сайт тормозит — ищите блокировки в БД, сетевые проблемы или «застрявшие» воркеры PHP‑FPM (смотрите наше руководство по slowlog PHP‑FPM).
Правило большого пальца: для NVMe в веб‑нагрузке держите p95 задержек операций на уровне единиц миллисекунд. Всё, что заметно выше десятков миллисекунд под стедитейт‑нагрузкой — повод копать.

iotop: кто прямо сейчас жрёт диск
iotop показывает, какие процессы создают I/O и каков их вклад. На продакшене чаще всего полезен режим вывода только активных операций, с накоплением статистики по процессам/CGroups.
# Только активные процессы с суммированием и PID/Command
iotop -oPa
# Неболтливый batch-режим для логирования (5 выборок с интервалом 2с)
iotop -b -qqq -n 5 -d 2
Колонки DISK READ/DISK WRITE показывают текущий и суммарный объём, IO — долю времени процесса в ожидании I/O. Если видите неожиданные акторы (архиватор, индексатор поиска, тяжёлый бэкап, find по всему диску) — изолируйте их в расписании и/или ограничьте полосу/вес I/O через cgroups.
fio: воспроизводимые тесты диска под профиль веб‑нагрузки
Диагностика «вживую» важна, но для инженерных выводов нужно уметь воспроизводимо тестировать. fio — стандарт де‑факто. Идея проста: моделируем профили, похожие на ваше приложение (размер блока, доля чтения/записи, глубина очереди, число потоков), и снимаем метрики латентности/IOPS/MB/s.
Быстрые пресеты
# Последовательное чтение большими блоками (бэкапы, медиаконтент)
fio --name=seqread --filename=/data/fio.test --size=8G --bs=1M --rw=read --iodepth=32 --direct=1 --ioengine=io_uring --runtime=60 --time_based=1 --group_reporting=1
# Случайное чтение мелкими блоками (каталоги, индексы БД)
fio --name=randread --filename=/data/fio.test --size=8G --bs=4k --rw=randread --iodepth=64 --numjobs=4 --direct=1 --ioengine=io_uring --runtime=60 --time_based=1 --group_reporting=1
# Смешанная R/W как у типичной БД или кэша
fio --name=mixed --filename=/data/fio.test --size=8G --bs=4k --rw=randrw --rwmixread=70 --iodepth=64 --numjobs=4 --direct=1 --ioengine=io_uring --runtime=60 --time_based=1 --group_reporting=1
Пояснения: --direct=1 выключает файловый кэш ОС для чистого профиля устройства. --ioengine=io_uring обычно даёт лучшие задержки на новых ядрах. --numjobs и --iodepth подбирайте ближе к реальности вашего сервиса (число воркеров, параллелизм в БД, пул потоков).
Job‑файл для повторения тестов
[global]
ioengine=io_uring
runtime=90
time_based=1
group_reporting=1
direct=1
filename=/data/fio.test
size=16G
[web-rand-reads]
rw=randread
bs=4k
numjobs=4
iodepth=64
[web-mixed]
rw=randrw
rwmixread=70
bs=4k
numjobs=4
iodepth=64
[seq-read]
rw=read
bs=1M
numjobs=1
iodepth=32
Как читать результат: смотрите на lat (avg, p95, p99), IOPS и стабильность в течение теста. Для веб‑нагрузки ключевое — низкая и предсказуемая латентность при разумной глубине очереди. Если рост iodepth почти не увеличивает IOPS, а только портит задержку — упираетесь в контроллер/планировщик/устройство.

Планировщики I/O: none, mq-deadline, bfq, kyber — что выбрать
Современные ядра Linux используют многоквартальную подсистему запросов blk‑mq. Для неё актуальны планировщики: none, mq-deadline, bfq, kyber. Коротко о поведении:
- none — минимум вмешательства. Хорош для быстрых NVMe, где собственный контроллер диска отлично планирует запросы.
- mq-deadline — балансирует пропускную и задержку, предотвращает «голодание». Универсальный выбор для SATA SSD и виртуальных дисков.
- bfq — ориентирован на fairness и интерактивность. Уместен на HDD или десктоп‑сценариях, где важна отзывчивость фоновых задач, но может проигрывать по пиковым IOPS на SSD.
- kyber — низкие задержки для датацентровых профилей, но встречается реже и не всегда доступен в сборках дистрибутивов.
Практические рекомендации для веб‑сервисов:
- NVMe: чаще всего none. Если наблюдаете хвосты задержек под смешанной нагрузкой — попробуйте mq-deadline.
- SATA SSD: стартуйте с mq-deadline. Он сглаживает пики и чаще даёт предсказуемую латентность.
- HDD: для случайного доступа в проде — спорный выбор. Если важна отзывчивость панели/логов — можно тестово bfq, но для бэкапов и последовательных задач — подойдёт mq-deadline.
- Виртуальные диски (virtio‑blk/virtio‑scsi, облака): часто всё уже зашедулено на гипервизоре. Практичный выбор — mq-deadline; иногда доступен только none. Проверяйте, что реально поддерживается.
Если вы работаете на виртуальном хостинге, переключение планировщика обычно недоступно на уровне клиента. На VDS вы полностью контролируете ядро и блочные настройки, поэтому тесты и тюнинг дадут прямой эффект.
Как проверить и переключить scheduler
# Список доступных и активный планировщик для устройства
cat /sys/block/sda/queue/scheduler
# Переключить планировщик на mq-deadline (через tee, чтобы не требовался ">")
echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler
# Для NVMe-устройств
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler
Чтобы сохранить настройку после перезагрузки — используйте udev‑правило или systemd‑юнит, применяющий значение на старте. Не меняйте планировщик на бою без тестов под реалистичной нагрузкой — измерьте до/после через fio и метрики приложения.
Очереди, readahead и прочие регуляторы
Помимо scheduler, влияют параметры очередей и предварительного чтения.
- Глубина очереди на уровне приложения:
iodepthвfio, число воркеров БД/кэша, параллелизм запросов. Слишком маленькая — недоиспользование устройства; слишком большая — растут хвосты задержек. nr_requestsи прочие лимиты очереди в/sys/block/<dev>/queue/: тонкая настройка, без нужды лучше не крутить.- Readahead: важно для последовательного чтения (медиа, бэкапы). Для случайных чтений большого эффекта нет.
# Посмотреть и изменить readahead (страницы по 512 байт)
sudo blockdev --getra /dev/sda
sudo blockdev --setra 4096 /dev/sda
Файловые системы и монтирование: ext4 и XFS, опции под веб‑нагрузку
И ext4, и XFS отлично подходят для веб‑нагрузки. Больше решают параметры монтирования и дисциплина обслуживания SSD, чем сам выбор FS. Базовые рекомендации:
- relatime/noatime: отключает лишние записи времени доступа.
relatimeдавно дефолт и обычно достаточно. Для высоких RPS можно использоватьnoatime. - discard: для SSD/TRIM лучше периодический
fstrimпо таймеру, чем постоянныйdiscardна каждом удалении. - Журналирование: оставляйте по умолчанию. Экзотические режимы ради «скорости» часто бьют по надёжности.
# Пример /etc/fstab с ext4
device-uuid /data ext4 defaults,noatime,commit=120 0 2
# Включить еженедельный TRIM для всех поддерживаемых устройств
systemctl enable fstrim.timer
systemctl start fstrim.timer
# Разово
fstrim -av
Ограничение и изоляция I/O: cgroups и systemd
Чтобы фоновая задача не убивала прод, задайте лимиты I/O. В мире systemd это делается свойствами юнитов.
# Снизить вес I/O для сервисов бэкапа
sudo systemctl set-property backup.service IOWeight=50
# Ограничить полосу чтения/записи на конкретном устройстве
sudo systemctl set-property backup.service IOReadBandwidthMax=/dev/sda 50M
sudo systemctl set-property backup.service IOWriteBandwidthMax=/dev/sda 20M
# Для контейнеров systemd-nspawn/podman под управлением systemd — аналогично
На старых системах с cgroup v1 используются blkio.* параметры. Суть одна: критичные веб‑процессы получают высокий приоритет, фоновым — полосу и вес поменьше, чтобы не раздувать латентности.
Связь с приложением: где рождается I/O в веб‑стеке
Даже идеальный диск не спасёт, если само приложение генерирует избыточный I/O. Посмотрите на путь запроса:
- Логи веб‑сервера: чрезмерная детализация и синхронная запись на busy‑сайте создают лавины мелких записей. Используйте буферизацию логов и ротацию с компрессией вне «прайм‑тайма».
- PHP‑FPM/Интерпретаторы: кешируйте автозагрузчики, отключайте лишний stat файлов. OpCache для PHP резко снижает I/O по коду.
- База данных: следите за fsync частотой, журналированием и размером буферов. Неправильные checkpoints или недонастроенный кэш приводят к всплескам записи.
- Кэши и сессии: переводите на in‑memory (Redis/Memcached) где это уместно, чтобы убрать «мелочь» с диска.
Полезная практика — параллельно запускать iostat и профайлер приложения/БД, чтобы видеть, какие операции в коде бьют по диску.
Типичные сценарии и что делать
1) Высокий %util, растущий await, iotop показывает активные бэкапы
Перенесите окна бэкапов, ограничьте им полосу через systemd, увеличьте размер блока и последовательность операций. На SSD отдайте приоритет планировщику mq-deadline для лучшей предсказуемости.
2) NVMe с неплохим MB/s, но латентность скачет под мелким randrw
Проверьте, не завышена ли глубина очереди на уровне пула воркеров; попробуйте mq-deadline вместо none, если хвосты задержек слишком велики. Убедитесь, что файл под fio на том же томе, где лежат данные сервиса.
3) Виртуальный диск в облаке, iostat стабильно 100% util при умеренной нагрузке
Скорее всего, достигли лимитов тарифа или noisy neighbor. Ограничьте фоновый I/O, проверьте, нет ли перегретого логгинга, и измерьте fio базовые профили. Если потолок низкий и подтверждается синтетикой — рассматривайте перенос на более быстрый класс хранилища или горизонтальное разделение. Подбор ресурсов поможет материал как выбрать конфигурацию VDS.
4) HDD и веб‑сайт «подлагивает» при одновременной индексации
Для интерактивности попробуйте bfq и жёстко ограничьте полосу индексатору. Переведите горячие каталоги (сессии, кэши) на SSD или tmpfs при достаточной RAM.
Минимальный план наведения порядка
- Зафиксируйте базовую линию:
iostat -dxm 1под реальной нагрузкой, снимите p95/p99 отклика приложения. - Найдите «шумных» соседей по диску через
iotop -oPa; ограничьте полосу и приоритеты фоновым задачам. - Проведите
fioс профилями, близкими к продовой нагрузке: randread/randrw 4k, seqread 1M. Запомните IOPS и латентность. - Выберите подходящий scheduler (NVMe: none или mq-deadline; SATA SSD: mq-deadline; HDD: по ситуации). Измерьте до/после.
- Проверьте опции монтирования (
noatime,fstrim.timer), readahead для последовательного доступа. - Оптимизируйте слой приложения: логгирование, кэш, параметры БД. Сравните метрики снова.
Замечания по методике и безопасности
Не запускайте тяжёлый fio по рабочему LUN в прайм‑тайм — синтетика может «выдавить» продовый I/O. Лучше отдельный том или окно низкой нагрузки. Если тестируете на многопользовательском узле, скоррелируйте действия с остальными сервисами. Любые изменения scheduler/монтирования фиксируйте и возвращайте при откате.
Итоги
Поставить I/O «под контроль» — это сочетание быстрых наблюдений (iostat, iotop), воспроизводимых тестов (fio) и аккуратной настройки окружения (scheduler, очереди, опции FS, cgroups). Для веб‑сервисов цель номер один — стабильная низкая латентность под реалистичной параллельной нагрузкой. Поддерживайте дисциплину бэкапов и индексаторов, выбирайте планировщик по типу носителя, держите в порядке логи и кэш — и ваш TTFB скажет «спасибо».


