Ваше сховище «виглядає нормально». Панель показує 2 GB/s читання, пул зелений, але база даних задихається, наче щойно пробігла марафон у вовняному пальті.
Усі все одно сперечаються. Хтось вказує на «пропускну здатність диска» і оголошує перемогу. Тим часом ваш графік p95 латентності тихо підпалює все навколо.
Проблеми продуктивності ZFS рідко починаються з зламаного диска. Вони починаються з неправильної ментальної моделі: читання не того показника для реального навантаження.
Це різниця між «ми обмежені IOPS» і «ми обмежені пропускною здатністю», і вона вирішує, чи купувати більше шпинделів, змінювати recordsize, додавати спеціальний vdev або просто припинити обманювати себе тестами.
Неспроможність метрики: чому 2 GB/s все ще може бути повільно
Пропускна здатність привабливо проста. Це велике число. Воно гарно виглядає в презентації. Це також найпростіша метрика для неправильного використання.
Система може переносити гігабайти за секунду й при цьому забезпечувати жахливий користувацький досвід, якщо вона виконує це у великих послідовних потоках, а ваше реальне навантаження — маленькі випадкові читання з жорсткими бюджетами затримки.
Основний зв’язок такий:
Пропускна здатність = IOPS × розмір IO.
Це не лозунг; це математика за більшістю дискусій про сховище.
Якщо ваш розмір IO — 4 KiB і ви можете робити 20 000 IOPS, це близько 80 MiB/s. Не вражає, але може бути саме тим, що потрібне базі даних.
Якщо ваш розмір IO — 1 MiB і ви можете робити 2 000 IOPS, це 2 GiB/s. Вражає, але зовсім не стосується тієї самої бази даних.
У світі ZFS ця невідповідність ускладнюється, бо стек чесний, але складний:
ZFS має компресію, контрольні суми, copy-on-write, великий ARC-кеш, prefetch і transaction groups.
Ці функції не «повільні». Вони просто специфічні. Вони винагороджують деякі шаблони IO і карають інші.
Якщо ви запам’ятаєте тільки одне, то нехай це буде так:
Припиніть використовувати єдиний графік «MB/s» для оцінки здоров’я сховища.
Коли затримка — видимий користувачем симптом, спочатку виміряйте затримку. Потім зіставте цю затримку з IOPS vs пропускною здатністю і з реальним вузьким місцем (CPU, черги vdev, синхронний шлях, фрагментація, метадані, мережа або додаток).
Жарт №1: Якщо ви протестували пул одним послідовним тестом читання і оголосили його «швидким», вітаю — ви виміряли здатність читати файл тесту.
IOPS, пропускна здатність, затримка: три числа, що мають значення (і як вони пов’язані)
IOPS
IOPS — це «операцій вводу/виводу за секунду». Він рахує завершені запити IO, а не байти.
Він найважливіший, коли додаток виконує багато маленьких читань/записів: бази даних, диски VM, навантаження з великою кількістю метаданих, поштові сховища, кеші CI з малими файлами.
IOPS не безкоштовний. Кожен IO має накладні витрати: syscall, файлові облікові дії, контрольна сума, алокація, планування vdev, доступ до середовища, завершення.
Ваш пул може мати високу пропускну здатність і низькі IOPS, бо він хороший для великих послідовних передач, але поганий для багатьох маленьких випадкових операцій.
Пропускна здатність
Пропускна здатність (MB/s або GB/s) — це загальна кількість байтів, переміщених за секунду. Вона найважливіша для потокових задач: бекапи, обробка медіа, великі ETL, реплікація об’єктного сховища.
Для послідовного IO одна черга з достатньою глибиною запитів може наситити пропускну здатність з дивовижно невеликою кількістю IOPS.
Затримка
Затримка — це час на одну операцію IO (середнє, p95, p99). Зазвичай це метрика, яку відчувають люди.
Базі даних, яка чекає 15 ms на читання, все одно, що ваш пул може досягати 3 GB/s, якщо вона просила 8 KiB і отримувала його пізно.
У практичних термінах: затримка показує, чи у вас проблеми; IOPS/пропускна здатність пояснюють чому.
Взаємозв’язок, який ви постійно ігноруєте
Якщо розмір IO малий, вам потрібні високі IOPS для пристойної пропускної здатності.
Якщо розмір IO великий, ви можете отримати високу пропускну здатність при помірних IOPS.
ZFS ускладнює «розмір IO», бо розмір IO додатка, recordsize ZFS і фізичний розмір сектору не завжди співпадають.
Також навантаження може бути одночасно обмеженим і пропускною здатністю, і IOPS у різний час.
Приклад: хост VM може мати випадкові 8 KiB читання в стабільному стані (чутливі до IOPS/затримки), а потім робити великі послідовні записи під час вікон бекапу (чутливі до пропускної здатності).
Один пул, два вузьких місця, один on-call.
Де ZFS витрачає час: шлях продуктивності простими словами
Діагностика продуктивності ZFS стає простішою, коли думати шарами. Не «ZFS повільний». Скоріше: «ця IO чекає на який етап?»
Шлях читання (спрощено)
- ARC hit? Якщо так — ви переважно в RAM і CPU. Затримка від мікросекунд до низьких мілісекунд залежно від навантаження системи.
- ARC miss → vdev read. Тепер ви залежите від латентності диска, глибині черги та планування.
- Контрольні суми й декомпресія. Вартість CPU, іноді значна при швидких NVMe або важкій компресії.
- Prefetch може допомогти при послідовних читаннях, може зашкодити випадковим, якщо забруднює ARC.
Шлях запису (спрощено, і тут люди часто звільняють)
- Async writes потрапляють у пам’ять і фіксуються на диск у transaction groups (TXG). Це загалом швидко… допоки пам’ять не заповниться або пул не зможе достатньо швидко скинути дані.
- Sync writes потрібно зафіксувати безпечно до підтвердження. Без виділеного журналу (SLOG) це означає запис на основні vdev з низькими гарантіями латентності.
- Copy-on-write змінює історію «перезапису». ZFS виділяє нові блоки та оновлює метадані. Фрагментація з часом може стати податком на випадкові навантаження.
Vdev — одиниця паралелізму
ZFS рівномірно розподіляє по vdev, а не по окремих дисках, як дехто помилково уявляє.
Один RAIDZ vdev має обмежений профіль IOPS (особливо для випадкових записів) у порівнянні з багатьма mirror vdev.
Якщо вам потрібно більше випадкових IOPS, зазвичай додають більше vdev, а не «більші диски».
Черги — куди йде ваша затримка помирати
Більшість інцидентів продуктивності — це інциденти чергування.
Диски не стільки «повільні», скільки «завантажені», а завантаження означає, що запити чекають.
Це очікування проявляється як затримка. Пул може й далі показувати гідну пропускну здатність, бо байти продовжують рухатися, але кожен окремий IO чекає в черзі.
Відбитки навантажень: як виглядають «IOPS-bound» і «throughput-bound»
IOPS-bound (малі випадкові IO)
Відбитки:
- Висока затримка, навіть коли MB/s помірна.
- Розмір IO малий (типово 4–16 KiB).
- Глибина черги зростає під навантаженням; диски показують високий
awaitабо симптоми на кшталтsvctmзалежно від інструмента. - CPU часто не насичений; ви чекаєте на сховище.
- ZFS datasets з малим recordsize або zvol з малим volblocksize схильні посилювати це.
Типові винуватці:
хости VM, OLTP бази даних, файлові дерева з великою кількістю метаданих, контейнери з шаруватими файловими системами, що сильно навантажують малі файли.
Throughput-bound (великі послідовні IO)
Відбитки:
- Високий MB/s і стабільна затримка до моменту насичення.
- Великий розмір IO (128 KiB до кількох MiB).
- CPU може стати вузьким місцем, якщо компресія/контрольні суми сильно завантажують процесор.
- Мережа часто стає стелею (10/25/40/100GbE), особливо з NFS/SMB клієнтами.
Типові винуватці:
потоки бекапів, медіапайплайни, великі аналітичні скани, реплікація і операції resilver.
Пастка «змішаного навантаження»
Ваш пул може виглядати блискуче вночі (графіки пропускної здатності бекапів) і жахливо опівдні (інтерактивна затримка).
Якщо ви проєктуєте тільки для пропускної здатності, ви отримаєте систему, яка тане під випадковим IO.
Якщо проєктуєте тільки для IOPS, можете купити дорогі SSD, коли насправді потрібні мережа або краща послідовна розкладка.
Цікаві факти та історичний контекст (які насправді допомагають)
- ZFS був розроблений в Sun з пріоритетом цілісності даних: наскрізні контрольні суми і copy-on-write були ключовими рисами, а не доповненнями.
- RAIDZ існує, щоб уникнути write hole RAID-5, жертвуючи деякою продуктивністю при малих записах заради сильніших гарантій.
- ARC — це не «просто кеш»; це самоналаштовуваний алгоритмічний кеш з обізнаністю про метадані, і він може радикально змінювати поведінку навантаження.
- Диски з сектором 4K змінили все: невідповідні ashift спричиняли реальні падіння продуктивності й непередбачуване write amplification.
- Prefetch створювався для потокових читань; при навантаженнях з великою часткою випадкових IO він може марнувати ARC і бюджет IO, якщо ви не розумієте, коли він спрацьовує.
- SLOG-пристрої стали популярні, бо sync-записи стали критичними, коли віртуалізація і бази даних почали часто використовувати fsync за замовчуванням.
- Компресія перетворилася з «податку на CPU» на «функцію продуктивності», коли CPU стали швидшими, а сховище — відносно повільнішим; менше байтів часто означає менше IO.
- Спеціальні vdev — це сучасна відповідь на стару проблему: метадані і малі блоки чутливі до затримки, тоді як масивні дані можуть бути повільнішими.
- Маркетинг IOPS прийшов з ери HDD, де випадковий доступ був жорстоко повільний і «seeks per second» фактично визначали класи продуктивності.
Швидкий план діагностики
Ось порядок дій, який я застосовую, коли дзвонять на пейджер. Він тяжіє до того, щоб правильно ідентифікувати вузьке місце до того, як чіпати налаштування.
Спочатку: доведіть, чи це затримка, IOPS, пропускна здатність, CPU або мережа
- Перевірте затримку і черги на сервері (не на клієнті):
iostat,zpool iostatі p95/p99 додатку. - Перевірте розмір і шаблон IO: випадкові проти послідовних, sync проти async, читання проти запису. Якщо ви не знаєте шаблону — ваш бенчмарк — художня вигадка.
- Перевірте влучення ARC: якщо ARC вас рятує, диски можуть бути невинні.
Друге: локалізуйте вузьке місце до vdev, dataset/zvol або шляху
- Який vdev гарячий? Mirrors і RAIDZ поводяться дуже по-різному при випадкових записах.
- Чи sync — проблема? Шукайте затримку sync записів і стан SLOG.
- Чи метадані — проблема? Навантаження з малими файлами часто вмирають на IOPS метаданих, а не на пропускній здатності даних.
Третє: вирішіть важіль
- Якщо це IOPS-bound: додайте vdev, перейдіть на mirror, використайте special vdev для метаданих/малих блоків, виправте recordsize/volblocksize, зменшіть sync-навантаження або додайте якісний SLOG.
- Якщо це throughput-bound: перевірте мережу, перевірте завантаження CPU через компресію/контрольні суми, розширте смуги (більше vdev), і використайте більший recordsize для потокових datasets.
- Якщо це запізнення від чергувань: зменште конкурентність, ізолюйте шумних сусідів, встановіть розумні значення
primarycache/secondarycache, і припиніть змішувати навантаження, які ненавидять одне одного.
Практичні завдання: команди, що означає вивід і яке рішення приймати
Це реальні «зроби це зараз» завдання. Кожне має команду, зразок виводу, що це означає, і рішення, яке вона провокує.
Виконуйте їх на хості сховища коли можливо. Метрики з клієнта корисні, але вони брешуть замовчуванням.
Завдання 1: Ідентифікуйте топологію пулу (бо розклад vdev — це доля)
cr0x@server:~$ sudo zpool status -v tank
pool: tank
state: ONLINE
scan: scrub repaired 0B in 0 days 02:11:09 with 0 errors on Sun Dec 22 03:10:12 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme0n1p2 ONLINE 0 0 0
nvme1n1p2 ONLINE 0 0 0
raidz1-1 ONLINE 0 0 0
sda2 ONLINE 0 0 0
sdb2 ONLINE 0 0 0
sdc2 ONLINE 0 0 0
errors: No known data errors
Значення: Цей пул поєднує mirror vdev і RAIDZ1 vdev. Це дозволено, але характеристики продуктивності дуже різняться; розподіл буде робитися по vdev і повільніша/перегріта частина може домінувати у затримці.
Рішення: Якщо вам важлива стабільна затримка, уникайте змішування типів vdev в одному пулі. Якщо це вже існує, розгляньте розділення навантажень по пулах.
Завдання 2: Спостерігайте IO пулу на високому рівні (IOPS vs MB/s)
cr0x@server:~$ sudo zpool iostat -v tank 1 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 3.21T 5.89T 3.21K 1.87K 110M 42.3M
mirror-0 920G 1.81T 2.95K 210 98.4M 3.21M
nvme0n1p2 - - 1.48K 105 49.2M 1.60M
nvme1n1p2 - - 1.47K 105 49.2M 1.61M
raidz1-1 2.29T 4.08T 260 1.66K 11.7M 39.1M
sda2 - - 90 560 4.10M 13.0M
sdb2 - - 86 550 3.96M 13.1M
sdc2 - - 84 545 3.64M 13.0M
Значення: RAIDZ vdev приймає більшість записів (1.66K ops), тоді як читання домінують на mirror. Це ймовірно означає, що навантаження є записо-важким і потрапляє туди, де затримка буде гіршою.
Рішення: Якщо затримка важлива, перемістіть датасети, чутливі до записів, у пул з mirror або SSD vdev; або переробіть vdev. Не намагайтеся «налаштувати» проблему топології.
Завдання 3: Перевірте затримку/черги на блочному рівні
cr0x@server:~$ iostat -x 1 3
Linux 6.8.0 (server) 12/25/2025 _x86_64_ (32 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.44 0.00 6.21 9.87 0.00 71.48
Device r/s w/s rMB/s wMB/s rrqm/s wrqm/s %util await r_await w_await
nvme0n1 480.0 42.0 62.0 1.9 0.0 8.0 38.2 1.20 1.10 2.30
nvme1n1 475.0 41.0 61.8 1.9 0.0 7.0 37.9 1.18 1.08 2.25
sda 22.0 180.0 1.2 20.5 0.0 15.0 96.8 24.40 9.20 26.10
sdb 21.0 178.0 1.2 20.2 0.0 14.0 95.9 25.10 9.50 26.70
sdc 20.0 175.0 1.1 19.9 0.0 13.0 95.1 26.00 10.10 27.20
Значення: HDD приблизно на ~95% завантажені з ~25 ms await. NVMe в нормі (~1.2 ms). Це класичне чергування: малі IO на HDD vdev будуть каратися.
Рішення: Якщо ваше навантаження потребує низької затримки, не надсилайте його на насичені HDD vdev. Зменшіть конкуренцію, перемістіть датасети або додайте vdev/SSD. Не фокусуйтеся на MB/s.
Завдання 4: Визначте, чи присутні sync-записи
cr0x@server:~$ sudo zfs get -o name,property,value -r sync tank/app
NAME PROPERTY VALUE
tank/app sync standard
Значення: standard означає, що додаток вирішує sync проти async (fsync/O_DSYNC). Багато баз даних і гіпервізорів навмисно змушують sync семантику.
Рішення: Якщо ви бачите високу латентність записів і додаток інтенсивно використовує fsync, дослідіть SLOG і шлях sync. Не «виправляйте» це, встановивши sync=disabled, якщо вам не до вподоби постмортем з втратою даних.
Завдання 5: Перевірте наявність і стан SLOG (окремий журнал)
cr0x@server:~$ sudo zpool status tank | sed -n '1,120p'
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme0n1p2 ONLINE 0 0 0
nvme1n1p2 ONLINE 0 0 0
raidz1-1 ONLINE 0 0 0
sda2 ONLINE 0 0 0
sdb2 ONLINE 0 0 0
sdc2 ONLINE 0 0 0
logs
mirror-2 ONLINE 0 0 0
nvme2n1p1 ONLINE 0 0 0
nvme3n1p1 ONLINE 0 0 0
errors: No known data errors
Значення: Існує mirrored SLOG. Добре: латентність sync-записів може бути обмежена цими пристроями, а не HDD RAIDZ.
Рішення: Переконайтеся, що SLOG-пристрої мають захист при відключенні живлення і не є побутовими NVMe, що видають себе за надійні. Якщо SLOG відсутній і sync-навантаження велике, розгляньте додавання — але тільки після підтвердження, що навантаження дійсно sync-bound.
Завдання 6: Перевірте recordsize датасету (через пропускну здатність проти випадкових читань)
cr0x@server:~$ sudo zfs get -o name,property,value recordsize tank/app
NAME PROPERTY VALUE
tank/app recordsize 128K
Значення: 128K — хороший стандарт для загальних файлів. Для баз даних з 8K сторінками це може спричинити read amplification (читання більше, ніж потрібно) і вплинути на затримку при промахах кешу.
Рішення: Для датасетів баз даних розгляньте recordsize=16K або 8K залежно від розміру сторінки БД та шаблону доступу. Для потокових датасетів залишайте більші значення (128K–1M).
Завдання 7: Перевірте volblocksize zvol (VM тут живуть і вмирають)
cr0x@server:~$ sudo zfs get -o name,property,value volblocksize tank/vmstore/vm-001
NAME PROPERTY VALUE
tank/vmstore/vm-001 volblocksize 8K
Значення: 8K відповідає багатьом випадковим IO шаблонам VM, але може зменшувати послідовну пропускну здатність і збільшувати накладні метадані. Занадто малий розмір також означає більше IO операцій для тих самих байтів.
Рішення: Вибирайте volblocksize для конкретного навантаження перед записом даних (воно фіксоване після створення у багатьох реалізаціях). Для змішаних VM навантажень 8K–16K типовий вибір; для масивних послідовних — більше може допомогти.
Завдання 8: Перевірте статистику ARC (чи ви залежать від диска чи кешу?)
cr0x@server:~$ arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:10:01 3200 420 13 180 5 210 7 30 1 96G 110G
12:10:02 3400 600 17 210 6 360 11 30 1 96G 110G
12:10:03 3100 380 12 160 5 190 6 30 1 96G 110G
Значення: Рівень промахів ~12–17%. Не жахливо, але під інцидентом затримки навіть «лише» 15% промахів можуть бути катастрофічними, якщо промахи йдуть на повільні vdev.
Рішення: Якщо промахи корелюють зі сплесками затримки — ви обмежені дисковою латентністю. Розгляньте більше RAM, special vdev або переміщення гарячого робочого набору на швидші vdev.
Завдання 9: Перевірте коефіцієнт стиснення (це може бути безкоштовною пропускною здатністю)
cr0x@server:~$ sudo zfs get -o name,property,value,source compression,compressratio tank/app
NAME PROPERTY VALUE SOURCE
tank/app compression lz4 local
tank/app compressratio 1.62x -
Значення: 1.62x означає, що ви пишете менше байтів, ніж думає додаток. Це часто покращує і пропускну здатність, і IOPS (менше фізичного IO), але коштує CPU.
Рішення: Якщо CPU не завантажений і дані добре стискаються, залиште компресію ввімкненою. Якщо CPU завантажений, а сховище простає — оцініть зміну рівня компресії або перенесення важкої компресії з гарячих шляхів.
Завдання 10: Підтвердіть ashift (вирівнювання), щоб уникнути тихого write amplification
cr0x@server:~$ sudo zdb -C tank | sed -n '/ashift/,+2p'
ashift: 12
asize: 7.99T
is_log: 0
Значення: ashift=12 означає 4K сектори. Хороша база. Якби у вас було ashift=9 на 4K дисках, ви ризикували read-modify-write штрафами й потворною поведінкою для маленьких записів.
Рішення: Якщо ashift неправильний, ви не «налаштовуєте» це. Ви перебудовуєте vdev правильно. Так, це дратує. Ні, це не опціонально.
Завдання 11: Перевірте прискорення метаданих/малих блоків (special vdev)
cr0x@server:~$ sudo zpool status tank | sed -n '/special/,+15p'
special
mirror-3 ONLINE 0 0 0
nvme4n1p1 ONLINE 0 0 0
nvme5n1p1 ONLINE 0 0 0
Значення: Існує special vdev. Якщо налаштовано з порогом для малих блоків, метадані й малі блоки можуть жити на швидких накопичувачах, підвищуючи IOPS/затримку для навантажень з малими файлами.
Рішення: Якщо ваше навантаження важке на метадані або випадкові IO 4–16K, special vdev може змінити правила гри. Але ставтеся до нього як до повноцінного vdev: відмовостійкість і моніторинг — обов’язкові.
Завдання 12: Виміряйте IO на рівні датасету (хто галасує?)
cr0x@server:~$ sudo zfs iostat -r -v tank 1 3
capacity operations bandwidth
pool alloc free read write read write
------------------------- ----- ----- ----- ----- ----- -----
tank 3.21T 5.89T 3.20K 1.86K 110M 42.0M
tank/app 820G 1.20T 1.90K 920 52.0M 18.0M
tank/vmstore 1.1T 2.20T 1.10K 910 58.0M 22.0M
tank/backups 1.2T 1.50T 200 30 0.8M 2.0M
------------------------- ----- ----- ----- ----- ----- -----
Значення: Датасети app і VM store домінують в IO. Бекапи наразі тихі.
Рішення: Якщо потрібна ізоляція, помістіть VMs і бази даних у різні пули або принаймні в різні класи vdev. Спроба «чесно ділити» затримку в одному пулі — шлях до ранніх сивих волосся.
Завдання 13: Визначте, чи ви обмежені CPU через контрольні суми/компресію
cr0x@server:~$ mpstat -P ALL 1 2
Linux 6.8.0 (server) 12/25/2025 _x86_64_ (32 CPU)
12:12:01 PM CPU %usr %nice %sys %iowait %irq %soft %steal %idle
12:12:02 PM all 62.0 0.0 25.0 1.0 0.0 1.0 0.0 11.0
12:12:02 PM 0 78.0 0.0 19.0 0.0 0.0 0.0 0.0 3.0
Значення: CPU сильно завантажений, тоді як iowait низький. Це натяк, що носій сховища може бути в порядку, а ви витрачаєте CPU на компресію, контрольні суми, шифрування або сам додаток.
Рішення: Перед купівлею дисків профілюйте CPU і перевіряйте витрати ZFS-функцій. Якщо ви обмежені CPU, швидші диски не допоможуть; потрібні більше CPU або інші вибори компресії/шифрування.
Завдання 14: Запустіть чесний fio тест для латентності випадкового читання
cr0x@server:~$ fio --name=randread4k --filename=/tank/app/fio-testfile --size=8G --rw=randread --bs=4k --iodepth=32 --numjobs=4 --direct=1 --runtime=30 --time_based --group_reporting
randread4k: (groupid=0, jobs=4): err= 0: pid=27144: Thu Dec 25 12:13:40 2025
read: IOPS=48210, BW=188MiB/s (197MB/s)(5640MiB/30001msec)
slat (nsec): min=1100, max=220000, avg=5200.4, stdev=3400.1
clat (usec): min=70, max=9200, avg=640.8, stdev=310.5
lat (usec): min=80, max=9300, avg=650.9, stdev=312.0
clat percentiles (usec):
| 1.00th=[ 160], 5.00th=[ 240], 10.00th=[ 290], 50.00th=[ 610]
| 90.00th=[ 1050], 95.00th=[ 1400], 99.00th=[ 2100], 99.90th=[ 4200]
Значення: Випадкові 4K читання: ~48K IOPS, ~188 MiB/s, p99 ~2.1 ms. Це історія про IOPS/затримку, а не про пропускну здатність.
Рішення: Порівняйте це з вимогами додатка. Якщо додатку потрібен p99 нижче мілісекунди, а ви цього не досягаєте — потрібні швидші vdev, більше паралельності vdev, кращий кеш або менше sync-навантаження — не «більше GB/s».
Завдання 15: Запустіть послідовний fio тест пропускної здатності (окремо від випадкового)
cr0x@server:~$ fio --name=seqread1m --filename=/tank/backups/fio-testfile --size=32G --rw=read --bs=1m --iodepth=16 --numjobs=2 --direct=1 --runtime=30 --time_based --group_reporting
seqread1m: (groupid=0, jobs=2): err= 0: pid=27201: Thu Dec 25 12:15:12 2025
read: IOPS=3020, BW=3020MiB/s (3168MB/s)(90615MiB/30005msec)
clat (usec): min=230, max=12000, avg=820.3, stdev=310.4
Значення: Ви можете робити ~3 GiB/s послідовного читання з IO 1 MiB. Чудово для бекапів/стрімів.
Рішення: Використовуйте це для розрахунку вікон реплікації і швидкості бекапів. Не використовуйте це, щоб стверджувати, що база даних буде швидкою.
Завдання 16: Перевірте тиск TXG (чи накопичуються записі?)
cr0x@server:~$ cat /proc/spl/kstat/zfs/txg | sed -n '1,80p'
13 1 0x01 7 336 4257451234 123456789
name type data
birth 4 1756114458
state 4 1
txg 4 231948
g__active 4 231949
g__opened 4 231948
g__quiescing 4 231947
g__syncing 4 231946
ndirty 4 1948723200
dirty_max 4 4294967296
delay 4 0
Значення: ndirty велике, але нижче dirty_max, і delay = 0. Якщо delay зросте або ndirty досягне max, додатки можуть бути загальмовані; затримки часто слідують за цим.
Рішення: Якщо TXG бореться, ви обмежені скиданням записів. Дивіться на латентність запису vdev, sync-навантаження і чи повільний RAIDZ не зупиняє конвеєр.
Три міні-історії з корпоративного життя (анонімні, правдоподібні і технічно дратівливі)
1) Інцидент через хибне припущення: «Але пул читає 5 GB/s»
Середня SaaS-компанія мігрувала платіжний сервіс зі старого SAN на блискучий новий ZFS-аплайанс.
Чекліст міграції мав звичні пункти: розклад scrub, знімки, реплікацію, алерти. Розділ продуктивності мав один рядок: «перевірено пропускну здатність за допомогою dd».
Вони запустили велике послідовне читання, отримали геройське число — і відправили в прод.
Через два тижні вони випустили фічу, яка збільшила обсяг транзакцій і додала кілька вторинних індексів.
База даних не впала. Вона просто стала повільною. Затримка поступово зростала, поки API не почав таймаутити.
Канал інцидентів наповнився графіками, що пул робив лише кількасот MB/s. «У нас є резерв», — сказав хтось, і всі кивнули, бо MB/s виглядало низьким.
Реальна проблема була в латентності випадкових читань. Робочий набір більше не поміщався в ARC, тому промахи кешу йшли на RAIDZ vdev з HDD.
Кожен запит потребував десятків 8–16 KiB читань, і ці читання ставали в чергу за іншими випадковими IO.
Пропускна здатність залишалася помірною, бо розмір IO був малий; IOPS були вузьким місцем, і чергування подвоювало біль.
Вони вирішили це, перемістивши датасет бази до пулу з mirror-only SSD і встановивши відповідний recordsize.
Графік пропускної здатності майже не змінився. p95 затримки впала драматично. Команда болісно навчилася: користувачі не платять вам гігабайтами в секунду.
2) Оптимізація, яка повернулась бумерангом: «Давайте примусимо більші записи для продуктивності»
Інша компанія працювала з флотом хостів VM на ZFS з zvol.
Хтось помітив, що послідовна пропускна здатність під час вікон бекапу не блищить, і вирішив «оптимізувати»: стандартизувати все на більші блоки.
Датасети отримали більші recordsize; деякі zvol були пересоздані з більшим volblocksize; запит на зміни гордо стверджував «менше IO».
Бекапи трохи прискорилися. Потім у черзі тікетів почали накопичуватися скарги: VMs відчули уповільнення, особливо під час патчів і хвиль логінів.
Графіки показували вищу затримку, але й вищу пропускну здатність у піках, що здавалося успіхом для тих, хто застряг у режимі «ширина каналу».
Проблема була у read amplification і неефективності кешу. Багато VMs робили 4–8 KiB випадкові читання та записи.
Більші блоки означали, що кожен малий доступ підвантажував більше даних, ніж потрібно, марнуючи ARC і генеруючи більше фізичного IO при промахах кешу.
Гірше — випадкові записи створювали більше роботи на операцію, і ефективна IOPS-ємність пулу падала.
Вони відкотили зміни для zvol VM: менший volblocksize, і відокремили бекап-потоки в інший датасет/пул.
Урок простий: ви не оптимізуєте сховище, роблячи все «великим». Ви оптимізуєте під відповідний блок-поведінку для навантаження.
3) Нудна, але правильна практика, що врятувала ситуацію: «Ми зберігали sync чесним»
Фінансова компанія використовувала NFS-сховище для кількох критичних систем, включаючи кластер баз даних і чергу повідомлень.
Команда зберігання мала політику, яка дратувала розробників: синхронна семантика залишалася ввімкненою, і «виправлення продуктивності», що відключали безпеку, вимагали підписання ризику.
Це не було популярно, але було послідовно.
Під час оновлення апаратури постачальник запропонував швидке рішення: встановити sync=disabled на гарячих датасетах і «запустити систему».
Команда відмовилася і натомість додала належний mirrored SLOG на пристроях з захистом при відключенні живлення, потім виміряла латентність fsync під навантаженням.
Результат не був магічним, але був стабільним. І що важливіше — передбачуваним.
Через місяці стався інцидент з живленням, який вивів з ладу PDU стійки так, що час роботи UPS виявився коротшим, ніж очікували.
Деякі системи впали жорстко. Сховище повернулося чистим.
Постмортем був нудним — немає втрати даних, немає корупції, немає дивного відтворення — а нудьга була метою.
Якщо вам потрібна надійна цитата для стіни, ось та, якій я довіряю, бо вона операційно застосовна:
«Надія — не стратегія.»
— General Gordon R. Sullivan
Регулювання ZFS, що змінює IOPS vs пропускну здатність (і ті, що не допоможуть)
Топологія: mirror проти RAIDZ
Mirrors зазвичай перемагають у випадкових читаннях IOPS і часто в латентності випадкових записів. RAIDZ виграє в ефективності місткості і часто в послідовній пропускній здатності за долар, але платить податок на малі випадкові записи (паритетні обчислення + шаблони IO).
Якщо ваше навантаження чутливе до IOPS/латентності, mirrors — типовий вибір, якщо немає сильної причини інакше.
recordsize (datasets) і volblocksize (zvols)
Ці налаштування впливають на IO amplification і ефективність кешування.
Більші блоки допомагають послідовній пропускній здатності і зменшують накладні метадані. Менші блоки можуть зменшити read amplification для маленьких випадкових читань.
Встановлюйте їх на основі розміру IO додатка і шаблону доступу. Якщо ви не знаєте шаблону додатка — дізнайтеся. Вгадування — як будувати дороге розчарування.
sync, SLOG і справжнє значення «швидких записів»
Sync-записи стосуються гарантій надійності. Якщо додаток цього вимагає, ZFS повинен зафіксувати безпечно перед підтвердженням.
Хороший SLOG може зменшити латентність sync-записів, взявши цей удар на швидкий низьколатентний пристрій, а потім відфлушивши в основне сховище.
Поганий SLOG (не PLP побутовий SSD, перевантажений або спільний з іншими навантаженнями) гірший за відсутність: він стає вузьким місцем і додає ризик відмови.
special vdev для метаданих і малих блоків
Якщо ваша біль — IO метаданих (багато малих файлів, обходи директорій, поштові сховища) або випадкові читання малих блоків, special vdev може перемістити найгарячіші, найчутливіші частини на SSD.
Це часто чистіше рішення, ніж вливати більше RAM в ARC, коли робочий набір занадто великий.
Компресія
Компресія змінює математику. Якщо ви стиснули 2:1 — ви наполовину зменшуєте фізичні байти і часто фізичний час IO.
Але ви витрачаєте CPU-цикли. На HDD пулах це зазвичай виграш. На NVMe при екстремальній пропускній здатності CPU може стати стелевим обмеженням.
На що не варто одержимо звертатися
Дрібні «tunables» не виправлять невідповідність навантаження і топології.
Якщо у вас RAIDZ HDD, що обслуговує випадкові 8K sync-записи, жоден sysctl вас не врятує. Диски все одно зроблять роботу, одну болісну операцію за одною.
Жарт №2: Налаштування без вимірювання — як дебаг з закритими очима: технічно можливо, але переважно форма інтерпретативного танцю.
Поширені помилки: симптоми → корінь проблеми → виправлення
1) «Пропускна здатність низька, значить сховище в порядку» (коли затримка жахлива)
Симптоми: Таймаути додатка, високі p95/p99 затримки, але графіки MB/s виглядають помірно.
Корінь: Навантаження обмежене IOPS з малим розміром IO; чергування на vdev; промахи кешу потрапляють на повільні носії.
Виправлення: Виміряйте розмір IO і затримку; додайте паралельність vdev (більше mirrors), перемістіть гарячі датасети на SSD, додайте special vdev або збільшіть ARC якщо робочий набір дійсно поміститься.
2) «Купили швидші диски, усе ще повільно»
Симптоми: NVMe всюди, але виграш у продуктивності малий; CPU гарячий; затримка не покращується.
Корінь: Обмеження CPU (компресія/шифрування/контрольні суми), або вузьке місце в мережі/NFS/SMB, або додаток серіалізований (однопотоковий IO).
Виправлення: Перевірте завантаження CPU і мережі; профілюйте паралелізм додатка; підтвердіть, що клієнти можуть видавати паралельні IO; налаштуйте там, де справжній вузький прошарок.
3) «Випадкові записи впали після заповнення пулу»
Симптоми: Новий пул був швидким; через місяці сплески затримки випадкових IO; звільнення простору трохи допомагає.
Корінь: Фрагментація + тиск алокацій у copy-on-write, плюс зменшений вільний простір веде до менш ефективного розподілу блоків.
Виправлення: Тримайте здоровий запас вільного простору, особливо на HDD RAIDZ; використайте special vdev для метаданих/малих блоків; розгляньте переписування або міграцію датасетів; уникайте паталогічних патернів перезапису на RAIDZ для гарячих випадкових навантажень.
4) «Sync-записи вбивають нас, тож ми відключили sync»
Симптоми: Продуктивність «покращується» відразу; пізніше ви бачите корупцію після збою або реплікація передає пошкоджені блоки.
Корінь: Торгівля надійністю за швидкість; система була sync-bound і потребувала належного SLOG або зміни поведінки навантаження.
Виправлення: Відновіть sync=standard; додайте mirrored, захищений від втрати живлення SLOG; або свідомо змініть поведінку додатка (групуйте fsync, груповий commit), а не брехні додатку.
5) «Змінили recordsize і стало гірше»
Симптоми: Послідовні навантаження покращилися, але інтерактивні деградували; hit rate кешу впав; більше IO операцій для тієї ж роботи.
Корінь: Невідповідний розмір блока відносно навантаження; read amplification; забруднення ARC.
Виправлення: Підберіть recordsize/volblocksize відповідно до шаблону доступу: малий для випадкових читань БД, великий для потоків. Розділяйте навантаження на різні датасети і встановлюйте властивості для кожного датасету.
6) «Один пул для всього»
Симптоми: Бекапи роблять бази даних повільними; scrubs роблять VMs ривками; продуктивність непередбачувана.
Корінь: Конкуруючі шаблони IO; відсутність ізоляції; спільні черги vdev.
Виправлення: Розділіть по пулах або принаймні по класах vdev; плануйте scrubs/resilvers; обмежуйте масові роботи; розгляньте виділені цілі для бекапів.
Контрольні списки / покроковий план
Покроково: вирішіть, чи ви IOPS-bound або throughput-bound
- Зберіть затримку (p95/p99) з додатка і з хоста сховища (
iostat -x). - Зберіть розподіл розміру IO за допомогою fio-профілів, що відповідають додатку (випадкове 4K, 8K sync-записи, 1M послідовні читання і т.д.).
- Обчисліть передбачену пропускну здатність: IOPS × розмір IO. Якщо спостережуваний MB/s відповідає математиці — метрики сумісні і можна логічно міркувати.
- Перевірте рівень промахів ARC. Високий промах з високим disk await означає, що ви обмежені диском; високий промах при низькому disk await вказує на інше (CPU/мережа/додаток).
- Огляньте завантаження vdev через
zpool iostat -v. Ідентифікуйте найгарячіший vdev і чому він гарячий.
Чекліст: проєктування для випадкових IOPS (бази даних, VMs)
- Надавайте перевагу декільком mirror vdev над невеликою кількістю широких RAIDZ vdev.
- Встановлюйте recordsize/volblocksize у відповідності до очікуваних розмірів IO.
- Розгляньте special vdev для метаданих/малих блоків.
- Тримайте резерв вільного простору (не працюйте пул майже повністю заповненим і потім дивуйтеся).
- Переконайтеся, що шлях sync адекватний: або належний SLOG, або прийміть затримку і спроєктуйте навколо цього.
Чекліст: проєктування для послідовної пропускної здатності (бекапи, ETL)
- Використовуйте більший recordsize для великих файлів.
- Виміряйте мережеві обмеження; сховище може бути не вашим вузьким місцем.
- Підтвердіть запас CPU, якщо використовуєте компресію/шифрування.
- Відокремлюйте послідовні великі роботи від інтерактивних чутливих до затримки датасетів якщо можливо.
Операційний чекліст: перед тим як чіпати налаштування
- Захопіть
zpool status,zpool iostat -v,iostat -xпід час інциденту. - Захопіть статистику ARC і індикатори тиску пам’яті.
- Запишіть шаблон навантаження (sync? випадкове? розмір IO?). Якщо не можете — зупиніться і інструментуйте.
- Робіть одну зміну за раз і переміряйте. Сховище — не дисципліна «за відчуттями».
FAQ
1) Який показник спочатку дивитися в ZFS: IOPS чи пропускну здатність?
Спочатку стежте за затримкою (p95/p99), потім зіставляйте її з IOPS і розміром IO. Самотня пропускна здатність — це число для настрою і часто хибне.
2) Чому пул показує високу пропускну здатність, а база даних повільна?
Ваш пул може добре обслуговувати послідовні читання/записи (високий MB/s), тоді як база потребує низької латентності для малих випадкових IO.
Якщо випадкові читання промахуються ARC і йдуть на HDD RAIDZ, затримка домінує, і пропускна здатність не відображає біль користувачів.
3) Чи mirrors завжди швидші за RAIDZ?
Для випадкових IOPS і латентності mirrors зазвичай кращі.
Для ефективності місткості і часто для послідовної пропускної здатності за долар RAIDZ може бути привабливим.
Вибирайте на основі навантаження. «Завжди» — для маркетингу, не для інженерії.
4) Чи збільшить додавання дисків у RAIDZ IOPS?
Це може підвищити послідовну пропускну здатність, але випадкові IOPS не масштабуються лінійно так, як багато хто сподівається.
Якщо вам потрібні випадкові IOPS, зазвичай додають більше vdev (більше незалежних черг), а не лише ширший RAIDZ.
5) Коли SLOG допомагає?
Коли у вас навантаження з істотними sync-записами (fsync-насичені БД, NFS з sync, деякі шаблони VM) і ваші основні vdev мають вищу латентність.
Він не допоможе для async-записів і не виправить випадкову читальну латентність.
6) Чи коли-небудь прийнятно sync=disabled?
Це прийнятно, коли ви свідомо вирішуєте брехати щодо надійності і можете терпіти втрату даних при відключенні живлення або збоях.
Більшість продакшен-систем цього не витримають, і ті, хто вважає, що витримають, часто знаходять «релігію» після першого інциденту.
7) Чи варто змінювати recordsize для моєї бази даних?
Часто так, але лише з розумінням. Якщо сторінка вашої БД 8K і доступи випадкові, менший recordsize може зменшити read amplification при промахах кешу.
Тестуйте з репрезентативним навантаженням і стежте за p95/p99 затримкою, а не лише MB/s.
8) Як зрозуміти, чи я залежний від ARC або від диска?
Якщо рівень влучень ARC високий і диски показують низький await/util — ймовірно ви обмежені кешом/CPU/додатком.
Якщо промахи ARC корелюють з високим disk await/util і зростаючою затримкою — ви дисково обмежені і потрібні швидші носії або більше паралельності vdev (або зменшений робочий набір).
9) Чи може компресія покращити IOPS?
Так. Якщо дані стискаються, ZFS пише менше байтів і може задовольняти читання швидше (менше фізичного IO), що може покращити і пропускну здатність, і ефективні IOPS.
Якщо CPU стає вузьким місцем, виграш зникає.
10) Чому бенчмарки не відповідають продакшену?
Бо бенчмарки часто послідовні, дружні до кешу і нереально глибоко чергують запити, у той час як продакшен — змішаний, бурстовий і чутливий до затримки.
Якщо ваш бенчмарк не відповідає розміру IO, sync-поведінці, конкуренції та робочому набору, він вимірює інший всесвіт.
Наступні кроки, які можна зробити цього тижня
Якщо ви хочете припинити сперечатися про «швидке сховище» і почати постачати передбачувану продуктивність, зробіть ці кроки в порядку:
- Виберіть два репрезентативні fio-профілі: один випадковий малих блоків (4K або 8K) з реалістичною конкурентністю, і один послідовний великих блоків (1M). Запустіть їх на тих самих датасетах, що й ваші додатки.
- Інструментуйте затримку: захопіть p95/p99 у додатку і на хості сховища під піковим навантаженням.
- Аудит топології проти навантаження: якщо ви запускаєте чутливі до затримки навантаження на RAIDZ HDD vdev — прийміть фізику або переробіть з mirror/SSD/special vdev.
- Виправте розміри блоків: вирівняйте recordsize/volblocksize з тим, як додаток фактично робить IO, а не з тим, як би ви хотіли, щоб він робив.
- Підтвердіть поведінку sync: не відключайте безпеку в продакшені; додайте належний SLOG якщо sync-записи є обмежувачем.
- Відокремте шумних сусідів: бекапи, scrubs і реплікації необхідні. Вони також розтрощать інтерактивну латентність, якщо ви дозволите їм ділити черги без контролю.
Потім переміряйте. Якщо ви не можете показати «до і після» за процентилями затримки і формою IO, ви не робили інженерію — ви займалися фантазіями з root-привілеями.