Почему хранилище тормозит: четыре источника ограничений
Скорость файлового хранилища ограничивает тот слой, который первым упёрся в предел: диски, контроллер и шина PCIe, сеть, файловая система с её параметрами. В конкретный момент узкое место одно. Апгрейд любого другого слоя не даёт прироста и съедает бюджет.
Проверка, которая занимает десять минут. Снимите локальную скорость прямо на сервере хранения. Если массив отдаёт 500 МБ/с локально, а NFS-клиент получает 80 МБ/с, ограничение сидит в сети или в настройках протокола. Обратная картина: канал держит гигабит, но iowait выше 30%, а await растёт под нагрузкой, значит упёрлись диски.
Порядок работы такой: измеряем и фиксируем baseline, локализуем слой, который первым достиг предела, и только после этого ускоряем. Менять диски, когда причина в MTU или recordsize, - самая дорогая ошибка в этой теме.
Как отличить дисковое узкое место от сетевого
Разделяйте слои прямыми замерами. Локальный тест с обходом кэша показывает предел накопителя, сетевой тест показывает предел канала, а замер на клиенте показывает результат их совместной работы. Таблица ниже помогает сузить круг за минуту.
| Симптом | Вероятный слой | Что проверить первым |
|---|---|---|
| Локально быстро, по сети медленно | Сеть, протокол | ethtool, iperf3, параметры монтирования |
| Высокий iowait, await растёт под нагрузкой | Диски, массив | iostat -x, состояние пула, SMART |
| Скорость вдвое ниже паспортной | Шина, контроллер, выравнивание | PCIe lanes, режим кэша контроллера, разметка |
| Приложение пишет медленно, диск свободен | Файловая система, sync-запись | recordsize, ZIL/SLOG, режим sync |
| Скорость падает со временем | Деградация носителей | SMART, результат scrub, тип дисков |
Пример из практики: NFS-клиент показывает 80 МБ/с на записи, а локальный тест на том же массиве даёт 500 МБ/с последовательной записи. Массив тут не при чём. Сначала проверьте линк командой ethtool eth0: скорость и duplex. Затем запустите iperf3 между сервером и клиентом. Если канал выдаёт 9,4 Гбит/с, а NFS всё равно 80 МБ/с, копайте rsize/wsize, nconnect и размер TCP-окна.
Когда картина размыта и непонятно, на каком слое искать, помогает методика разбора bottleneck по метрикам: она позволяет подтвердить причину деградации, а не угадать её. Такой подход описан в материале как найти узкое место в системе и повысить производительность без лишних затрат.
Типичные ошибки при поиске узкого места
- Замер на прогретом кэше. Свободная оперативная память и page cache отдают данные быстрее любого накопителя. Перед тестом сбросьте кэш: echo 3 > /proc/sys/vm/drop_caches. Для ZFS дополнительно используйте direct I/O в fio, иначе ARC отдаст данные из памяти и результат ничего не покажет.
- Один поток вместо реальной нагрузки. Синхронный dd с глубиной очереди 1 показывает максимум для одной операции, а не для базы данных с 64 одновременными запросами. Тестируйте с iodepth 32-128 и несколькими задачами (numjobs).
- Игнорирование queue depth. У SATA SSD очередь 32 команды, у NVMe - тысячи. Один и тот же диск на глубине 1 и на глубине 64 выдаёт разные цифры, и сравнивать результаты с разной глубиной нельзя.
- Сравнение разного типа нагрузки. Sequential и random на HDD расходятся в десятки раз. Для архивов важна последовательная скорость, для баз данных и виртуалок - 4K random.
После каждого изменения снимайте новый замер и записывайте рядом с baseline. История цифр за месяц объясняет деградацию быстрее, чем интуиция.
Как измерить реальную производительность хранилища
Набор инструментов минимален: fio для нагрузки, ioping для задержки, iostat -x для наблюдения, iperf3 и sar -n DEV для сети. Всё есть в стандартных репозиториях и не требует остановки сервисов.
- Baseline диска. Случайное чтение 4K при глубине 32: fio --name=randread --ioengine=libaio --direct=1 --rw=randread --bs=4k --iodepth=32 --numjobs=4 --size=4G --runtime=60 --group_reporting. Далее то же для randwrite, затем последовательные 1M: --rw=read --bs=1M. Смотрите три числа: IOPS, МБ/с и среднюю латентность.
- Задержка отклика. ioping -c 20 -i 1 /mnt/data показывает latency без нагрузки. Полезно сравнить с latency под нагрузкой: рост в 5-10 раз говорит о перегрузке очереди.
- Текущая нагрузка. iostat -x 1 в течение 60 секунд под рабочим трафиком. Средние значения по всем дискам важнее пиковых.
- Сеть. iperf3 -s на сервере, iperf3 -c 10.0.0.10 -t 30 -P 4 на клиенте, повторите с ключом -R для обратного направления. Пропускную способность интерфейса проверьте через sar -n DEV 1.
Ориентиры, с которыми стоит сравнивать свои цифры. HDD 7200 rpm: 80-120 IOPS на 4K random, 150-220 МБ/с последовательно, latency 8-15 мс. SATA SSD: 50-90 тыс. IOPS, 500-550 МБ/с, latency 0,1-0,3 мс. Потребительский NVMe: 200-500 тыс. IOPS, 2-3,5 ГБ/с. Серверный NVMe с PCIe 4.0: от 1 млн IOPS, 6-7 ГБ/с, latency ниже 0,1 мс. Реальный массив из нескольких дисков показывает сумму с поправкой на накладные расходы RAID.
Измеряйте под нагрузкой, близкой к рабочей. Синтетический тест на пустом хранилище и тест на массиве, занятом на 80%, дадут разную картину, особенно на ZFS и на SSD без TRIM.
Метрики производительности дисков: что смотреть в iostat
Утилита iostat -x 1 выводит десятки полей, но решения принимаются по пяти: r/s и w/s (число операций), rkB/s и wkB/s (объём), await (средняя задержка одной операции с учётом очереди), %util (доля времени, когда устройство занято).
| Device | r/s | w/s | rkB/s | wkB/s | await, мс | %util |
|---|---|---|---|---|---|---|
| sda (HDD) | 12 | 340 | 96 | 13600 | 18,4 | 99,2 |
| nvme0n1 | 8200 | 0 | 32800 | 0 | 0,08 | 12,4 |
Читается так: HDD загружен полностью (%util 99), очередь накопилась, await 18 мс при потоке 340 записей в секунду. Диск работает на пределе, дальше расти некуда. NVMe при 8200 операций чтения имеет await 0,08 мс и загрузку 12%, у него большой запас.
Пороги для быстрой реакции: await выше 20 мс для SSD и выше 50 мс для NVMe - сигнал тревоги, %util выше 90% на всех дисках массива означает насыщение. Поле svctm встречается в выводе, но его значения недостоверны на современных устройствах с параллельной обработкой очереди, опирайтесь на await. Для NVMe и многодисковых массивов %util тоже теряет смысл: устройство обрабатывает команды параллельно, и 100% загрузки не означают предела. Ориентируйтесь на latency и глубину очереди.
Тестирование сети: iperf3 и проверка пропускной способности
Запуск: на сервере хранения iperf3 -s, на клиенте iperf3 -c 10.0.0.10 -t 30 -P 4. Несколько потоков нужны, потому что одно TCP-соединение часто упирается в возможности одного ядра. Затем замерьте в обратную сторону: iperf3 -c 10.0.0.10 -R -t 30.
Ориентиры по каналам в одну сторону: 1 GbE даёт около 110-117 МБ/с, 10 GbE около 1,1-1,18 ГБ/с, 25 GbE около 2,8 ГБ/с, 100 GbE около 11 ГБ/с. Если iperf3 показывает 940 Мбит/с на десятигигабитном линке, проблема в драйвере, кабеле, коммутаторе или в настройках offload.
Проверка MTU выполняется так: ping -M do -s 8972 10.0.0.10. Для кадра 9000 байт полезная нагрузка ICMP составляет 8972 байта. Прошло без фрагментации - jumbo frames работают на всём пути. Получили ошибку вида "message too long" - где-то в цепочке MTU остался 1500.
Если iperf3 даёт норму, а NFS медленный, узкое место в настройках протокола, в синхронной записи или в дисках. Сеть тут уже не при чём.
Кэширование в системах хранения: что реально ускоряет
Уровней кэша несколько, и каждый ускоряет свой тип операций. page cache Linux держит недавно прочитанные данные в свободной RAM и управляется параметрами vfs_cache_pressure, dirty_ratio, dirty_background_ratio и размером readahead (blockdev --setra в секторах). В ZFS ту же работу делает ARC. Отдельно существуют L2ARC для чтения с SSD и SLOG для синхронной записи. Кэш контроллера RAID работает на уровне блоков и тоже влияет на результат.
Разумные значения для Linux-сервера хранения с большим объёмом RAM: vfs_cache_pressure от 50 до 80 (чем выше, тем агрессивнее ядро освобождает кэш inode и dentry), vm.dirty_ratio 10-20, vm.dirty_background_ratio 5-10. Большой dirty_ratio на медленных дисках приводит к долгим всплескам записи и росту latency.
Настройка ARC и L2ARC в ZFS
ARC по умолчанию забирает до 50% оперативной памяти. На выделенном хранилище этот лимит поднимают до 75-80% через параметр zfs_arc_max. На Linux его задают в файле /etc/modprobe.d/zfs.conf строкой options zfs zfs_arc_max=<значение в байтах>, на FreeBSD через sysctl, в TrueNAS через раздел tunables. Пример: 64 ГБ RAM и рабочий набор 500 ГБ - под ARC отдаём 48 ГБ, остальное оставляем ОС и сервисам.
L2ARC помогает, когда рабочий набор данных превышает доступную RAM. Кэш ставится на быстрый SSD или NVMe, а в датасете включается использование второго уровня: zfs set secondarycache=all pool/data. Плата за это - оперативная память под индексацию: ориентировочно 1 ГБ RAM на каждые 100 ГБ L2ARC. На слабой машине с 16 ГБ памяти L2ARC только навредит, потому что отнимет память у основного ARC.
L2ARC ускоряет чтение и не влияет на запись. Если нагрузка на 90% состоит из записи, кэш второго уровня бесполезен. Практические сценарии выбора SSD под кэш и оценки его эффективности по hit ratio разобраны в руководстве настройка кэширования в TrueNAS.
SLOG и ZIL: когда синхронная запись тормозит
ZFS пишет синхронно, когда этого требует вышестоящее приложение: NFS с опцией sync, базы данных с fsync, виртуалки с включённым кэшированием записи. Каждая такая операция должна дойти до стабильного носителя, и задержка определяет итоговую скорость. SLOG (Separate Log Device) переносит журнал ZIL с медленных дисков пула на быстрый SSD.
Добавление: zpool add pool log nvme0n1. Для защиты от потери данных SLOG ставят в зеркало (zpool add pool log mirror nvme0n1 nvme1n1) либо берут диск с конденсаторами и защитой от отключения питания. Дешёвый потребительский SSD без power-loss protection приведёт к потере подтверждённых записей при сбое питания.
SLOG не кэширует данные, он ускоряет фиксацию транзакций и освобождает пул от случайной записи по журналу. Без синхронных операций (sync=standard при обычной записи или sync=disabled) устройство простаивает. Ставить SLOG под нагрузкой из одних последовательных копирований смысла нет.
Кэш контроллера RAID работает по своим правилам. Режим write-back с батареей даёт кратный прирост на случайной записи, режим write-through без батареи тормозит всё. Об этом ниже, в разделе про ловушки конфигурации.
Уровни хранения SSD и HDD: как правильно разделить данные
Смысл разделения простой: горячие данные живут на быстрых носителях, холодные на дешёвых. Реализаций три. Первая - ручное разделение по датасетам: база данных на NVMe, архивы и бэкапы на HDD. Вторая - special vdev в ZFS для метаданных и мелких блоков. Третья - блочный кэш bcache или dm-cache, который ставит SSD перед медленным устройством.
Special vdev ускоряет операции с метаданными и мелкими блоками, перенося их на SSD. Задаётся при создании: zpool add pool special mirror nvme0n1 nvme1n1. Ориентир по объёму: 1-2% ёмкости пула обычно достаточно, но всё зависит от среднего размера файлов. Важное предупреждение: special vdev без зеркала превращается в точку отказа. Его потеря означает потерю всего пула, а не отдельного диска.
bcache и dm-cache работают на уровне блочных устройств и не зависят от файловой системы. Включают их там, где нужен SSD-кэш перед ext4 или XFS, а ZFS не используется. Настройка сложнее, чем кажется: при неверном режиме (writethrough вместо writeback) прирост исчезает.
Разделение уровней добавляет эксплуатационную сложность. Если горячий набор уже целиком помещается в ARC и L2ARC, отдельный tiering не нужен. Для расчёта требований по IOPS, латентности и запасу на рост пригодится методика из статьи как выбрать систему хранения данных: она помогает посчитать реальную нагрузку до закупки железа, а не после.
Часть холодных данных иногда дешевле держать вне собственного массива. Облачный провайдер закрывает эту задачу без закупки дисков и контроллеров: Timeweb Cloud предоставляет серверы с NVMe, блочное хранилище и готовую инфраструктуру под бэкапы, вынос архивов или тестовый стенд для замеров.
Настройка NFS и SMB: параметры, которые влияют на скорость
Для NFS на клиенте Linux 5.3 и новее ключевой параметр - nconnect. Одно TCP-соединение ограничено размером окна, задержкой и возможностями одного ядра, поэтому несколько каналов дают кратный прирост. Пример строки монтирования: mount -t nfs -o vers=4.2,rsize=1048576,wsize=1048576,nconnect=8,noatime 10.0.0.10:/pool/data /mnt/data. На 10 GbE связка vers=4.2 плюс nconnect=8 превращает 300-400 МБ/с в 900-1100 МБ/с на одном клиенте.
Дополнительно проверьте число потоков сервера nfsd: на сервере с 32 ядрами значение RPCNFSDCOUNT в пределах 64-128 покрывает нагрузку. На клиентах с ядром старше 5.3 вместо nconnect увеличивают sunrpc.tcp_slot_table_entries до 128.
Для SMB (Samba) важны версия протокола, multichannel и socket options. Минимум, который стоит выставить в smb.conf: server min protocol = SMB3, server multi channel support = yes, socket options = TCP_NODELAY IPTOS_LOWDELAY. Для клиентов macOS добавляют vfs objects = fruit и catia, иначе метаданные и права обрабатываются медленно.
iSCSI и NVMe-oF чувствительны к задержке сильнее, чем к пропускной способности. Для них имеет смысл RDMA (iSER) и отдельная сеть без общей нагрузки. Jumbo frames (MTU 9000) включайте только тогда, когда все устройства на пути поддерживают этот размер: сервер, коммутатор, клиент, а также хранилище. Ошибка в одном звене даёт фрагментацию, рост задержки и падение скорости ниже исходной.
Тюнинг ZFS под конкретную нагрузку
Набор параметров ZFS невелик, но каждый влияет на результат заметно. recordsize определяет размер блока и лучшее совпадение с профилем нагрузки. ashift фиксирует размер сектора и задаётся один раз при создании пула. compression=lz4 сжимает почти бесплатно и часто ускоряет запись за счёт меньшего объёма данных на диск. atime=off убирает лишние операции записи. sync выбирает режим подтверждения.
| Нагрузка | recordsize / volblocksize | sync | Дополнительно |
|---|---|---|---|
| PostgreSQL, MySQL | 16K | standard | logbias=throughput при SLOG, atime=off |
| Виртуальные машины | 64K | standard | xattr=sa, compression=lz4 |
| Медиа, бэкапы | 1M | standard | compression=lz4, atime=off |
| Общий SMB/NFS | 128K | standard | atime=off, secondarycache=all |
ashift=12 выставляйте всегда для дисков с сектором 4K, включая те, что отдают 512e. Ошибка ashift=9 на 4K-диске режет производительность записи и увеличивает износ. Исправить ashift после создания пула нельзя, только пересоздать пул и перелить данные.
Изменение recordsize применяется только к новым данным. Существующие файлы и блоки сохраняют прежний размер, пока запись не перезапишет их. Для смены профиля помогает копирование датасета: zfs send и zfs receive на новый датасет с нужными параметрами.
Не отключайте scrub ради производительности: регулярная проверка целостности выявляет ошибки чтения раньше, чем они превратятся в потерю данных. Ставьте scrub на ночные часы и следите за нагрузкой во время его работы.
Когда апгрейд железа не помогает: конфигурационные ловушки
Замена дисков не даст прироста, если ограничение лежит в другом месте. Ниже список причин, которые встречаются чаще всего.
- SMR-диски в массиве. Черепичная запись даёт провалы скорости в десятки раз при переполнении внутреннего кэша и сильно замедляет resilver в ZFS. Проверяйте модель диска перед покупкой, а не после.
- Невыровненные разделы. Раздел, начинающийся не на границе 1 МиБ, заставляет каждый блок записи читаться и переписываться заново. Проверка: parted -l показывает выравнивание разделов.
- Неверный размер страйпа RAID. Страйп 64K под 4K-запись даёт лишние операции. Для баз данных страйп подбирают под размер блока приложения.
- Контроллер в режиме write-through. Без кэша записи и без батареи контроллер подтверждает каждую запись физически. Переключение в write-back с BBU даёт кратный прирост на случайной записи.
- Нехватка линий PCIe. NVMe в слоте x4 при физическом подключении через чипсет с общей линией теряет половину скорости. Проверьте lspci -vv: скорость и ширина линка указаны в поле LnkSta.
- Неподходящий recordsize. База данных на датасете с recordsize 1M пишет в разы медленнее, чем на 16K.
- Сетевой потолок. Классика: массив из восьми NVMe работает, а клиенты подключены через 1 GbE и получают 110 МБ/с. Никакие настройки не превысят физическую пропускную способность линка.
Чек-лист перед апгрейдом. Проверьте загрузку линка и MTU. Снимите iostat -x под реальной нагрузкой. Убедитесь, что кэш контроллера включён и работает от батареи. Сверьте выравнивание разделов и ashift. Посчитайте, какой слой упирается первым, и только потом выбирайте, что менять.
Мониторинг производительности хранилища и предотвращение деградации
Разовая диагностика решает текущую проблему, но деградация накапливается незаметно. Дешёвый и надёжный стек: node_exporter для метрик системы, zfs_exporter или встроенные средства ZFS для пулов, smartctl_exporter для SMART, Prometheus как хранилище и Grafana как дашборды. Графики по iostat, hit ratio ARC, latency и свободному месту показывают динамику, которую не видно в отдельный момент времени.
Пороговые значения для алертов, которые хорошо работают на практике:
- await выше 50 мс (или выше 20 мс для SSD) в течение 5 минут подряд.
- ARC hit ratio ниже 90% на постоянной основе - рабочий набор не помещается в память.
- Свободное место ниже 20% на пуле: ZFS теряет возможность эффективно размещать блоки, SSD теряют резерв под перезапись.
- iowait выше 20% и растущая длина очереди запросов.
- SMART: рост Reallocated_Sector_Ct, Pending_Sector, ошибки CRC на интерфейсе.
- Ошибки и предупреждения ZED, а также ошибки проверки scrub.
ZED следит за событиями ZFS и сообщает о деградации пула, ошибках записи и провале проверок целостности. Утилита smartctl -a по расписанию даёт историю состояния дисков. После каждого изменения настроек фиксируйте новый baseline: без него вы не отличите замедление после правки от фонового роста нагрузки.
Пошаговые сценарии настройки сбора метрик, дашбордов и алертов собраны в руководствах мониторинг систем хранения: ключевые метрики и система мониторинга и проактивного обслуживания дисковых массивов.
Начните с одного действия прямо сейчас: снимите baseline на рабочем хранилище и запишите цифры в отдельный файл с датой. Когда через месяц приложение начнёт жаловаться на медленную запись, у вас будет с чем сравнивать, и вы найдёте узкое место за минуты, а не за дни.