Почему «всё тормозит», но CPU почти свободен
Типичная картина инцидента: load average растёт, SSH отвечает рывками, веб/БД замирают, а в top видно много wa (iowait) и десятки процессов в состоянии D (uninterruptible sleep). Важно понимать: это не «процесс завис», а «процесс ждёт I/O так, что ядро не может его прервать обычными сигналами». При большом количестве таких ожиданий система выглядит живой (таймеры тикают, сеть иногда работает), но полезная работа почти не выполняется.
Ниже — рабочая методика от быстрой диагностики до глубокого разбора: iostat/pidstat/iotop, разбор hung task и стеков ожидания, диагностика NVMe, и при необходимости трассировка блокового слоя через blktrace. В конце — контрольные замеры латентности с fio и типовые меры стабилизации.
Что такое iowait и почему он часто вводит в заблуждение
iowait (в top это wa) — доля времени, когда CPU простаивает, потому что в системе есть задачи, ожидающие завершения дискового I/O. Ключевой момент: iowait не означает «диск на 100%» и не всегда означает «узкое место — диск». Он означает: «есть хотя бы один поток, который ждёт I/O, и ядру нечем занять этот CPU прямо сейчас».
Когда wa высокий, проблема может быть не только в локальном диске:
- ожидание I/O идёт на сетевое хранилище (NFS/iSCSI/распределённые СХД), а локальные устройства выглядят спокойно;
- узкое место на уровне гипервизора или лимитов по IOPS/MBps, и очереди растут «выше» гостевой ОС;
- D-state вызван непрерываемыми ожиданиями в драйверах, а не классическим блочным I/O.
Поэтому iowait — полезный симптом, но плохой диагноз. Дальше задача — привязать симптом к конкретным устройствам, процессам и типам операций.
Если вы хотите параллельно освежить команды и метрики, держите под рукой материал iostat/iotop/fio и базовые принципы дисковой диагностики.

Состояние D (uninterruptible sleep): что это и чем опасно
Состояние D означает, что поток находится в «непрерываемом сне»: он ждёт завершения операции ядра (чаще всего I/O), и стандартные сигналы не могут его «разбудить». Поэтому kill -9 обычно не помогает: сигнал будет доставлен, но обработается только когда ожидание завершится.
Практическая опасность D-state в том, что такие потоки могут удерживать ресурсы: блокировки, дескрипторы, транзакции. Например, один поток БД застрял в D на fsync() — и следом встают остальные, растут очереди запросов и таймауты на уровне приложения.
Если вы видите массовый D-state, цель — не «убить процесс», а выяснить, что именно он ждёт: какой том/шару, какая операция (read/write/flush), и почему ожидание стало долгим.
Быстрый чек-лист (10 минут): понять «где болит»
1) Убедиться, что проблема именно в I/O
Соберите минимальный снимок состояния — он пригодится и для ретроспективы:
uptime
vmstat 1 5
mpstat -P ALL 1 5
free -m
В vmstat смотрите на рост wa, очередь b (blocked), наличие активного swap и поведение bo/bi. Если b растёт вместе с wa — это почти всегда задержки I/O (локального или сетевого).
2) Посмотреть устройства через iostat
iostat быстро показывает, на каком устройстве растут задержки и очереди:
iostat -x 1 10
Типовые сигналы проблем:
- высокий
%utilвместе с ростомaqu-sz(илиavgqu-sz) — устройство не успевает обслуживать очередь; - большие
r_await/w_await(или общийawait) — операции выполняются долго; - низкие
r/sиw/s, но огромныйawait— похоже на «редкие, но очень долгие» операции: flush/fsync, деградация носителя или проблемы нижнего уровня (SAN/сеть/виртуализация).
Для NVMe держите в голове, что «nvme latency» может идти волнами из-за внутренней сборки мусора, температурного троттлинга, прошивки, либо внешних лимитов (в виртуализации — по IOPS/throughput).
3) Найти процессы, которые генерируют I/O: pidstat и iotop
pidstat даёт пер-процессные цифры по I/O:
pidstat -d 1 10
pidstat -d -p ALL 1 10
Ищите лидеров по kB_rd/s, kB_wr/s и по времени ожидания диска (названия полей зависят от версии пакета sysstat).
iotop показывает, кто прямо сейчас грузит диск:
iotop -oPa
Если I/O почти нет, а await огромный — это важный сигнал: система может ждать не «поток данных», а завершение синхронных операций (flush/fsync) или ответ нижнего уровня хранения.
4) Проверить hung task и стек ожидания
Когда ядро видит, что поток слишком долго в D-state, оно пишет предупреждение о hung task в логи. Это часто готовая подсказка: в сообщении может быть видно, на чём именно «спит» поток.
dmesg -T | tail -n 200
journalctl -k -n 200
Точечный приём: посмотреть wchan и стек конкретного процесса:
ps -eo pid,stat,wchan:32,comm | awk '$2 ~ /D/ {print}' | head
cat /proc/PID/stack
Если в стеке видны функции файловой системы, блокового слоя или сетевой ФС — направление верное. Если стек пустой/нечитаемый, всё равно часто помогает wchan и корреляция по времени с iostat.
Частые причины iowait и D-state на серверах
Диск «не успевает»: очередь, %util и латентность
Частый сценарий: нагрузка (или фоновые задачи) создают больше операций, чем устройство может переварить. На HDD это проявляется сразу, на SSD/NVMe — часто как рост латентности при росте очереди. В iostat это выглядит как рост await и aqu-sz, иногда при %util около 100%.
Синхронные записи: fsync/flush, журналы и базы данных
PostgreSQL, MySQL/InnoDB, очереди и многие журнальные механизмы активно используют fsync(). Если устройство или сторедж не держит стабильную latency на flush, вы увидите «редкие, но убийственные» пики: много потоков уходит в D, а приложение ловит таймауты и деградацию по p99.
Проблемы сетевого или виртуализированного хранилища
Виртуальные диски с лимитами IOPS/MBps, перегруженные ноды, деградация пула, NFS с залипшими запросами — всё это легко приводит к D-state. Особенность: в гостевой ОС вы видите симптомы, но «железо» как будто ни при чём. В таких случаях критично смотреть метрики платформы/хранилища и коррелировать по времени.
Если вы администрируете проекты на VDS, заранее закладывайте мониторинг дисковых задержек и квот/лимитов, чтобы отличать «нам не хватает IOPS» от «у нас баг/регресс в приложении».
Память и writeback: когда «проблема диска» начинается с RAM
При нехватке памяти и активном writeback процессы могут блокироваться на записи не потому, что они пишут много, а потому что ядро «дросселирует» writers, пока не сбросит грязные страницы на диск. Это иногда выглядит как D-state у процессов, которые «вроде бы не про диск».
Глубже: что происходит на блоковом уровне
blktrace/btt: когда iostat уже не хватает
Если вы видите рост задержек на конкретном устройстве, но непонятно, кто и какими запросами создаёт очередь, пригодится blktrace. Он снимает события блокового слоя и помогает оценить, где именно тратится время: в очереди, в драйвере, на устройстве.
Трассировка может быть тяжёлой, включайте её точечно и на короткие интервалы, особенно на продакшене.
lsblk
blktrace -d /dev/nvme0n1 -o - | blkparse -i -
Для агрегирования и отчётов часто используют btt (если доступен):
blktrace -d /dev/nvme0n1 -o /tmp/trace
btt -i /tmp/trace
Что вы хотите понять: где растёт очередь, какая часть задержки — «до устройства», а какая — «на устройстве». Это помогает отличить «приложение генерирует слишком много мелких sync-write» от «устройство/сторедж отвечает рывками».
Проверка NVMe: SMART/health и признаки деградации
Для NVMe обязательно проверьте здоровье накопителя и ошибки:
nvme list
nvme smart-log /dev/nvme0
dmesg -T | grep -i nvme | tail -n 50
Обращайте внимание на media/CRC ошибки, warnings по температуре, а также резкие изменения метрик по времени. Даже если диск «ещё работает», нестабильная latency может убивать сервис сильнее, чем падение throughput.

fio: как быстро померить, «тянет ли диск» и где предел
fio полезен в двух случаях: (1) вы хотите получить базовые цифры latency/IOPS на конкретном томе; (2) вы подозреваете, что реальная производительность сильно ниже ожидаемой (например, после миграции).
Осторожно: тесты записи могут повредить данные, если выбрать неверный путь или запускать на боевом каталоге. На продакшене используйте тестовый файл в отдельной директории и контролируйте размер, чтобы не забить диск.
Пример безопасного «файлового» теста случайного чтения (без записи):
fio --name=randread --directory=/var/tmp --filename=fio.test --size=2G --rw=randread --bs=4k --iodepth=32 --numjobs=1 --time_based=1 --runtime=30 --direct=1 --group_reporting
Пример теста случайной записи (даёт заметную нагрузку и может ухудшить latency соседних сервисов):
fio --name=randwrite --directory=/var/tmp --filename=fio.test --size=2G --rw=randwrite --bs=4k --iodepth=32 --numjobs=1 --time_based=1 --runtime=30 --direct=1 --group_reporting
Смотрите не только IOPS, но и распределение задержек (percentiles). Именно хвосты (p95/p99) чаще всего коррелируют с D-state и таймаутами приложений.
Как связать D-state с конкретным диском или файловой системой
Практический путь: от процесса в D-state к файловому дескриптору и mount.
- Найдите PID в
Dи посмотрите открытые файлы. - Определите, на каком mount это лежит (локальный диск, LVM, NFS).
- Сопоставьте mount с устройством (
lsblk,findmnt). - Проверьте по
iostat -xименно это устройство.
ps -eo pid,stat,comm | awk '$2 ~ /D/ {print}' | head
ls -l /proc/PID/fd | head
findmnt -T /path/to/file
lsblk -f
Если это NFS — D-state может быть следствием сетевой задержки/таймаутов/подвисшего сервера. Если это LVM/RAID — иногда проблема ниже (деградация массива, ребилд, ошибки дисков). Если это виртуальный диск — возможны лимиты или влияние «соседей» на ноде.
Что делать после диагностики: типовые меры
Снизить давление на диск
- Остановить или перенести тяжёлые фоновые задачи (бэкапы, пересборка индексов, массовые архивирования).
- Ограничить «шумных» потребителей через
ionice(если уместно) и планировщик задач. - Проверить, не идёт ли внезапная ротация логов, компрессия или большой
flushбуферов.
Стабилизировать latency
- Для БД — убедиться, что журналы и данные на подходящем носителе, а
fsync()не упирается в «медленный flush». - Для NVMe — проверить health/температуры/ошибки и сравнить latency с ожидаемой по
fio. - Для сетевых ФС — проверить потери/RTT, таймауты и состояние сервера хранения.
Разобраться с hung task
Если hung task повторяется, копайте глубже: собирайте стеки, проверяйте блокировки, обновляйте ядро/драйверы в рамках вашей политики обновлений, проверяйте сторедж и носители. Такие эпизоды редко «исчезают навсегда» — обычно они возвращаются в самый неудобный момент.
Мини-шпаргалка команд (на случай аварии)
top
ps -eo pid,stat,wchan:32,comm | awk '$2 ~ /D/ {print}' | head
vmstat 1 5
iostat -x 1 10
pidstat -d 1 10
iotop -oPa
dmesg -T | tail -n 200
journalctl -k -n 200
lsblk -f
findmnt
nvme smart-log /dev/nvme0
Итог: как мыслить про iowait и D-state
iowait — индикатор, что система простаивает из-за ожидания I/O. D — подтверждение, что конкретные потоки застряли в непрерываемом ожидании (часто диска/хранилища). Дальше успех зависит от правильной развилки:
- Какие устройства? —
iostatи сопоставление mount с дисками. - Какие процессы? —
pidstat/iotopи анализ открытых файлов. - Какая природа задержки? — latency, логи ядра (hung task), при необходимости
blktrace. - Насколько плохо? —
fioкак контрольная линейка производительности и хвостов задержек.
Держите этот алгоритм под рукой — и большинство «непонятных тормозов» с D-state перестают быть магией и превращаются в понятную инженерную задачу: найти источник очереди, измерить задержку и устранить причину.


