У вас гора обертових дисків, немає бюджету на SSD, а користувачі вважають, що «сховище» — це кнопка, яку натискають, щоб прискорити додаток.
Тим часом ваш пул ZFS робить рівно те, що дозволяє фізика: повільні випадкові I/O, прийнятна послідовна пропускна здатність і іноді драма під час scrub.
Це польовий посібник, як зробити ZFS лише на HDD дивно придатним — без самообману. Ми налаштуємо те, що можна налаштувати, уникнемо
пасток, що виглядають швидко, поки не зіпсують вам тиждень, і натренуємо м’яз діагностики, який працює о 3-й ранку.
Ментальна модель: у чому сильні HDD-пули
Налаштування ZFS лише на HDD — це переважно мистецтво не просити диски робити те, на що вони не здатні. Обертові диски чудові в послідовній
пропускній здатності і жахливі в випадкових IOPS. ZFS чудовий у забезпеченні цілісності й гнучкості, і доволі добре показує себе за продуктивністю — якщо лише ви підете на зустріч геометрії пулу та поведінці запису/читання ZFS.
Одне правило, щоб вытатуїти в runbook
Для HDD-пулів ви не «налаштовуєте під IOPS». Ви зменшуєте випадкові I/O, збільшуєте ефективну послідовність і запобігаєте зайвому write amplification.
Якщо ваше навантаження справді складається з випадкових малих блокових записів, найчесніша оптимізація — змінити навантаження або купити SSD.
Все інше — переговори з фізикою.
Чому ZFS може здаватися повільним на HDD (і чому це часто ваша вина)
- Copy-on-write означає, що перезаписи стають новими записами, з оновленнями метаданих також. Фрагментація і розкидані записи з’являються при великій кількості випадкових оновлень.
- Transaction groups (TXGs) групують зміни і флашать періодично. Це добре для пропускної здатності; може бути погано для затримки, якщо ви неправильно розумієте поведінку sync.
- Checksumming додає навантаження на CPU і іноді змушує читати весь блок при частковому записі (read-modify-write).
- RAIDZ ефективний за ємністю, але малі випадкові записи можуть викликати накладні витрати на паритет і RMW-пенальті.
Жарт №1: HDD схожі на менеджера, що читає пошту — чудові в обробці довгої нитки, але катастрофічно повільні, коли їй кажуть переривати кожні 8 мс.
«Швидкість» має кілька значень
Більшість помилок у налаштуванні походять від оптимізації неправильної метрики:
- Пропускна здатність (MB/s) — для бекапів, медіа, об’єктного сховища, великих ETL-читань.
- IOPS — для навантажень, що інтенсивно працюють з метаданими, поштових сховищ, випадкових записів VM.
- Затримка — для баз даних, синхронних записів VM, NFS із синхронною семантикою.
Часто ви можете підвищити пропускну здатність і зменшити хвостову затримку, уникаючи патологічних патернів (дрібні sync-записи, невідповідні recordsize,
надміру широкі RAIDZ, агресивні scrubs під час піку). Але ви не зробите вісім HDD такими ж, як вісім SSD. Мета — зробити їх схожими на
догоджений восьмидисковий масив, а не мішок сумних пошуків.
Факти та історія, що справді важливі
Налаштування сховищ легше, коли ви знаєте, які «старі» ідеї все ще вплетені в сучасну поведінку. Ось короткі, конкретні факти, що продовжують працювати:
- ZFS з’явився в Sun Microsystems у середині 2000-х, щоб покінчити з тихою корупцією даних і спростити адміністрування сховищ; продуктивність була спроєктована передбачуваною в умовах вимог цілісності.
- Copy-on-write — причина, чому ZFS завжди може перевірити дані за допомогою чексум, але це також означає, що випадкові перезаписи з часом фрагментуються.
- RAIDZ створено, щоб уникнути «write hole» який спостерігається в класичному RAID‑5 при втраті живлення; узгодженість паритету — частина дизайну.
- ARC (Adaptive Replacement Cache) виник, щоб перевершити прості LRU-кеші, балансуючи недавність і частоту; на HDD-пулах ефективність ARC часто є найбільшим «безкоштовним» важелем продуктивності.
- 4K-сектори змінили правила гри і ashift існує тому, що ОС не завжди може довіряти повідомленому розміру сектора; неправильний вибір може назавжди вдарити по продуктивності.
- LZ4 compression став улюбленцем, бо за типової пропускної здатності HDD він зазвичай швидший за диски; часто підвищує ефективну пропускну здатність за рахунок меншого запису.
- ZIL (intent log) не є кешем записів; він потрібен, щоб безпечно підтверджувати синхронні семантики. Без виділеного SLOG, ZIL знаходиться на пулі, і sync-записи можуть стати королем затримок.
- Scrubs — не опція у культурі ZFS, бо чексум виявляє корупцію лише при читанні; scrubs примушують читати, щоб проактивно знайти й вилікувати латентні помилки.
- Широкі RAIDZ стали популярні з великими дисками заради ємності, але операційно вони збільшують час resilver і радіус ураження при поганій продуктивності — особливо на навантажених HDD-пулах.
Одну перефразовану думку варто пам’ятати: Надія — не стратегія; виміряйте систему, яка у вас є, а потім змінюйте по одній речі
— ідея з дисципліни операцій місії Gene Kranz.
Рішення щодо розмітки пулу, що змінюють усе
Якщо ви налаштовуєте існуючий пул, деякі рішення з розмітки вже зафіксовані. Але вам потрібно зрозуміти, що ви побудували, бо багато «налаштувань»
насправді є проблемами вибору: «вибрали RAIDZ2 для ферми VM».
Mirrors проти RAIDZ на HDD
Якщо ваше навантаження чутливе до IOPS (VM, бази даних, сплески метаданих), mirrors перемагають. І не трохи. Mirrors дають більше незалежних
привідних актюаторів (шпиндлів) для випадкових читань і зазвичай кращу поведінку записів. RAIDZ дає ефективність по ємності і чудовий
послідовний пропуск, але платить за це накладними на паритет і read-modify-write при малих записах.
- Mirrors: найкращі випадкові read IOPS, пристойні випадкові записи, швидкий resilver (копіюються лише використані блоки).
- RAIDZ2/3: найкраща корисна ТБ на диск, хороші послідовні потоки, складніша поведінка при малих записах, resilver може бути важким.
Не робіть занадто широкий vdev
«Ширші vdev-и швидші» — це правда для послідовної пропускної здатності, але часто хибна для затримки та поведінки при відновленні. 12-колонний RAIDZ2 може
стрімко працювати на потоці, але перетворитися на зайнятого, крихкого звіра під час resilver, поки ваші додатки борються за seeks.
Вирівнювання recordsize і геометрія vdev
ZFS пише блоки змінного розміру до recordsize (datasets) або volblocksize (zvols). На RAIDZ кожний блок розбивається по колонках плюс паритет.
Якщо розміри блоків не узгоджені з геометрією RAIDZ, ви можете отримати більше IO-операцій, ніж очікували, і більше partial-stripe записів.
Для пулів лише на HDD прагматична ціль: записувати менше, більших, добре стиснутих блоків; уникати частого їх перезапису; контролювати метадані і шляхи дрібних випадкових записів.
ashift: вибираєш один раз — терпиш вічно
ashift встановлює експоненту розміру сектора пулу. На практиці майже завжди хочеться ashift=12 (4K). Іноді
потрібен ashift=13 (8K) для деяких сучасних дисків або щоб зменшити накладні витрати на пристроях з великими секторами. Якщо ви вибрали ashift=9 на 4K-дисках, ви купили собі постійні проблеми з read-modify-write.
Якщо ви застрягли з поганим ashift, «налаштування» не виправить ситуацію — потрібно мігрувати в новий пул. ZFS чемна: вона не перепише
фундаменти пулу тільки тому, що ви пошкодували.
Налаштування dataset і zvol (recordsize, compression, sync)
Більшість перемог ZFS лише на HDD походять від встановлення властивостей dataset відповідно до навантаження. ZFS дозволяє налаштовувати на рівні dataset,
тож перестаньте думати «весь пул» і почніть думати «цей dataset містить диски VM, той — бекапи, цей — логи».
recordsize: не змушуйте диск робити зайву роботу
recordsize впливає на те, як ZFS нарізує дані файлу. Більші записи зазвичай підвищують послідовну пропускну здатність і ефективність стиснення.
Менші записи можуть зменшити read amplification для випадкових читань і зменшити розмір перезапису для дрібних оновлень — іноді.
Думка щодо значень за замовчуванням:
- Бекапи, медіа, великі об’єкти:
recordsize=1Mзазвичай відмінно підходить для HDD-пулів. - Загальні файлові шаринги: стандартне
128Kпідходить; міняйте лише за доказами. - Бази даних: часто
16Kабо32K, але тестуйте з розміром сторінки БД і патерном доступу. - VM-образи як файли: залежить; багато хто краще працює з
64Kабо128Kплюс компресія, але випадкові записи все ще можуть шкодити на RAIDZ.
volblocksize для zvols: встановлювати при створенні, не пізніше
Якщо ви шиєте iSCSI або використовуєте zvol-backed VM-диски, volblocksize — це ваш розмір блоку. Його не можна змінити після створення zvol.
Зіставляйте з файловою системою гостя/розміром сторінки БД коли можливо (часто 8K або 16K). Занадто малий розмір збільшує метадані та IO-операції; занадто великий — підсилює write amplification для дрібних випадкових оновлень.
Compression: найближче до «безкоштовного обіду»
На HDD компресія зазвичай вигідна, бо зменшує фізичний IO. lz4 зазвичай правильна базова опція. Зауважте CPU: якщо ви обмежені CPU або використовуєте важчі алгоритми, вузьке місце може зміститися.
sync: куди йде затримка
Синхронні записи вимагають підтвердження збереження. Без SSD (немає виділеного SLOG) ваші sync-записи фіксуються в on-pool ZIL, що означає рух головки та ротаційну затримку. Додатки, які роблять багато дрібних sync-записів, зроблять HDD-пул схожим на «непрацюючий».
Жорстка правда: включення sync=disabled робить бенчмарки гарними, а постмортеми — дорогими.
atime: перестаньте писати, бо хтось прочитав файл
На HDD-пулах atime=on може створювати безглузді цикли записів для read-важких datasets. Виключайте його для більшості серверних навантажень, якщо вам не потрібні часи доступу.
xattr і dnodesize: метадані важливі на шпинделях
Навантаження, що інтенсивно працюють з метаданими, можуть бути жорстокими для HDD. Налаштування такі як xattr=sa і dnodesize=auto можуть зменшити IO метаданих для деяких навантажень, але це не універсальне диво.
Краще правило: виявляйте метадані-сплески рано і відгороджуйте їх у datasets, які можна налаштувати.
ARC, тиск пам’яті та чому “більше RAM” — не міф
Якщо у вас немає SSD, RAM — це ваш рівень продуктивності. ARC — це місце для «гарячих» читань, щоб уникнути дискових seeks. На HDD-пулах здоровий ARC може стати різницею між «досить швидко» і «чому ls повільний».
Розмір ARC: не голодуйте ОС, не голодуйте ZFS
Правильний розмір ARC залежить від платформи і навантаження. Неправильний розмір — це легко: занадто великий — і ви свопите; занадто малий — і ви шурхотите дисками.
Свопінг на системі, що обслуговує IO, — це як налити пісок у коробку передач, бо ви хочете більше зчеплення.
Знати, що в ARC: дані проти метаданих
HDD-пули часто отримують надпропорційну користь від кешування метаданих (записи директорій, індексні блоки, dnodes). Якщо промахи по метаданих змушують робити дискові seeks,
ваше «випадкове читання» стає «випадкове читання метаданих» плюс «випадкове читання даних». Це дві проблеми одночасно.
Особлива примітка щодо dedup
Dedup на пулах лише на HDD зазвичай — кошмар продуктивності, якщо тільки у вас немає дуже специфічного виміряного випадку й багато RAM для таблиць dedup.
Якщо хочете економію місця, почніть з компресії. Якщо все ще хочете dedup — беріть калькулятор і план відкату.
Prefetch, послідовні читання та потокові навантаження
Prefetch — це коли ZFS намагається підвантажити дані наперед при виявленні послідовного доступу. На HDD-пулах послідовний доступ — ваша щаслива зона.
Коли prefetch працює, пропускна здатність росте, а латентність вирівнюється. Коли він помиляється (поширено для деяких патернів VM), він може марнувати пропускну здатність і витісняти корисні записи з ARC.
Підхід до налаштування — не «відключити prefetch, бо хтось на форумі так сказав». Це: визначити, чи навантаження справді послідовне, а потім тестувати.
Якщо ви можете реорганізувати IO, щоб він був більш послідовним — більші блоки, менше sync-точок, батчеві записи — робіть це до того, як чіпати налаштування prefetch.
Scrub і resilver: безпека без вбивства продуктивності
Scrub і resilver — це моменти, коли ваш пул перестає бути просто сховищем і стає проєктом з обслуговування. HDD мають обмежену пропускну здатність і IOPS.
Якщо в продуктивній системі починається resilver, щось постраждає. Ваше завдання — вирішити, що постраждає, і зробити це передбачуваним.
Планування scrub — це налаштування продуктивності
Scrub необхідні. Але проводити scrub опівдні на зайнятому NFS-сервері — це спосіб почути, як звучать ваші керівники, коли вони дізнаються про затримки.
Плануйте scrubs у не пікові години і відстежуйте їхню тривалість з часом. Зростання тривалості — запах: фрагментація, зростання обсягу даних, деградуючі диски або зміна навантаження.
Resilver поводиться по-різному залежно від розмітки
Mirrors часто resilver швидше, бо вони копіюють лише виділені блоки. RAIDZ resilver покращився за останні роки (послідовний resilver), але все ще навантажує пул,
оскільки реконструкція потребує читань з багатьох дисків і записів в один, плюс оновлення метаданих.
Жарт №2: Resilver під час піку — це найближче, що має сховище до тренування з бойовою стрільбою — тільки вогонь тут ваш квиток у службу підтримки.
Практичні завдання: команди, виводи та рішення (12+)
Це реальні операційні завдання, які ви можете виконати на хості ZFS (Linux або illumos-варіанти з подібними інструментами). Кожне завдання показує, що вивід вам каже і яке рішення приймати.
Використовуйте блокнот. Не «налаштовуйте» з пам’яті.
Завдання 1: Визначити стан пулу та очевидні повільні шляхи
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
status: Some supported features are not enabled on the pool.
action: Upgrade the pool to enable all features.
scan: scrub repaired 0B in 05:12:41 with 0 errors on Sun Dec 22 03:10:14 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d4 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d5 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d6 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d7 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d8 ONLINE 0 0 0
wwn-0x5000c500a1b2c3d9 ONLINE 0 0 0
errors: No known data errors
Значення: Стан пулу нормальний; scrub тривав ~5 годин; помилок немає. «Features not enabled» самі по собі не сигнал про продуктивність.
Рішення: Якщо scrubs стають довшими щомісяця, розслідуйте фрагментацію та продуктивність дисків. Інакше — рухайтеся далі.
Завдання 2: Дивитися затримки в реальному часі та визначити, що гальмує — читання чи записи
cr0x@server:~$ zpool iostat -v tank 2 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 48.2T 21.6T 120 980 12.8M 38.4M
raidz2-0 48.2T 21.6T 120 980 12.8M 38.4M
wwn-... - - 18 155 2.1M 6.2M
wwn-... - - 20 160 2.2M 6.4M
wwn-... - - 16 148 1.9M 6.0M
wwn-... - - 19 158 2.1M 6.3M
wwn-... - - 17 152 2.0M 6.1M
wwn-... - - 18 154 2.1M 6.1M
---------- ----- ----- ----- ----- ----- -----
Значення: Записи домінують в операціях; пропускна здатність помірна. Якщо навантаження повільне, швидше за все проблема в затримці/IOPS, а не в пропускній здатності.
Рішення: Далі перевірте sync-записи і патерни IO по процесах; також впевніться, що додаток не робить дрібні fsync-штормы.
Завдання 3: Перевірити властивості dataset, які найчастіше визначають продуктивність
cr0x@server:~$ zfs get -o name,property,value -r recordsize,compression,atime,sync tank
NAME PROPERTY VALUE
tank recordsize 128K
tank compression lz4
tank atime off
tank sync standard
tank/vm recordsize 128K
tank/vm compression lz4
tank/vm atime off
tank/vm sync standard
tank/backup recordsize 1M
tank/backup compression lz4
tank/backup atime off
tank/backup sync disabled
Значення: Dataset для бекапів налаштований на пропускну здатність, але має sync=disabled, що створює ризик втрати даних для всього, що заявляє про довговічність.
Рішення: Якщо цей dataset дійсно «використовується лише для тимчасових бекапів», можливо припустимо; інакше встановіть назад sync=standard та виправте записувач, щоб він пачкував.
Завдання 4: Перевірити ashift (вирівнювання секторів) для кожного vdev
cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n
45: vdev_tree:
62: ashift: 12
Значення: ashift=12 добрий для сучасних 4K-дисків.
Рішення: Якщо ви бачите ashift: 9 на 4K-дисках, плануйте міграцію. Не витрачайте час на мікро-налаштування.
Завдання 5: Виміряти ARC hit ratio і тиск пам’яті
cr0x@server:~$ arcstat 1 5
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:10:01 980 62 6 40 4% 22 2% 10 1% 22G 24G
12:10:02 1012 74 7 51 5% 23 2% 12 1% 22G 24G
12:10:03 990 69 6 44 4% 25 3% 9 1% 22G 24G
12:10:04 1005 80 8 55 5% 25 2% 13 1% 22G 24G
12:10:05 970 58 5 37 4% 21 2% 9 1% 22G 24G
Значення: Miss rate ~5–8% — пристойно; ARC близький до цілі. Якщо miss% високий і диски зайняті, ви робите більше фізичного IO, ніж потрібно.
Рішення: Якщо у вас є вільна RAM — збільшіть ARC max (залежить від платформи). Якщо ні — пріоритезуйте кешування метаданих через розділення навантажень або зменшення робочого набору (мінус snapshots, спрощення namespace).
Завдання 6: Визначити, чи навантаження обмежене sync-записами
cr0x@server:~$ grep -E "zil|sync" /proc/spl/kstat/zfs/zil
5 1 0x01 107 4080 173968149 2130949321
zil_commit_count 4 84211
zil_commit_writer_count 4 84211
zil_itx_count 4 5128840
zil_itx_indirect_count 4 120
zil_itx_indirect_bytes 4 983040
Значення: Високий zil_commit_count означає часті синхронні транзакції.
Рішення: Якщо користувачі скаржаться на затримки і ви бачите інтенсивні ZIL-комміти, виявіть, який dataset отримує sync-IO і який процес викликає fsync().
Виправлення зазвичай на боці додатка/БД, а не ZFS.
Завдання 7: Знайти процеси, що генерують IO
cr0x@server:~$ iotop -oPa
Total DISK READ: 12.31 M/s | Total DISK WRITE: 41.22 M/s
PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
8421 be/4 postgres 1.12 M/s 9.65 M/s 0.00 % 23.11 % postgres: wal writer
9102 be/4 qemu 4.88 M/s 18.33 M/s 0.00 % 31.74 % qemu-system-x86_64 -drive file=/tank/vm/vm01.img
2210 be/4 root 0.00 B/s 6.10 M/s 0.00 % 9.12 % rsync -a /staging/ /tank/backup/
Значення: PostgreSQL WAL і QEMU пишуть інтенсивно. WAL зазвичай чутливий до sync.
Рішення: Для БД розгляньте налаштування dataset (recordsize, розміщення журналу) і параметри БД (пакетування комітів). Для VM — сильно розгляньте mirrors для dataset VM, навіть якщо інша частина пулу RAIDZ.
Завдання 8: Перевірити фрагментацію та запас вільного місця
cr0x@server:~$ zpool list -o name,size,alloc,free,cap,frag,health tank
NAME SIZE ALLOC FREE CAP FRAG HEALTH
tank 69.8T 48.2T 21.6T 69% 38% ONLINE
Значення: 69% заповнення, 38% фрагментація. Це не катастрофа, але тенденція в бік «випадкові записи погіршаться».
Рішення: Якщо CAP перевищує ~80% на HDD-пулах, очікуйте падіння продуктивності. Плануйте розширення, видалення або міграцію до того, як досягнете цього рівня.
Завдання 9: Перевірити коефіцієнт стиснення і чи зменшується IO
cr0x@server:~$ zfs get -o name,used,compressratio -r tank | head
NAME USED COMPRESSRATIO
tank 48.2T 1.52x
tank/vm 18.4T 1.18x
tank/backup 22.1T 1.94x
Значення: Бекапи добре стиснуті (добре). VM dataset стиснутий погано (нормально для вже стиснутих OS-образів).
Рішення: Залишайте lz4, якщо тільки CPU не є вузьким місцем. Якщо стиснення VM близьке до 1.00x і CPU гарячий — можна розглянути відключення компресії для цього dataset.
Завдання 10: Перевірити властивість sync по dataset і виправити небезпечні скорочення
cr0x@server:~$ zfs get -o name,property,value sync tank/vm tank/backup
NAME PROPERTY VALUE
tank/vm sync standard
tank/backup sync disabled
Значення: Бекап небезпечний для sync-семантики. Деякі інструменти бекапу покладаються на fsync для коректності.
Рішення: Якщо ви не можете це обґрунтувати в огляді ризиків, змініть:
cr0x@server:~$ sudo zfs set sync=standard tank/backup
Завдання 11: Визначити, чи робите ви випадкові I/O дрібними блоками (вбивця HDD)
cr0x@server:~$ sudo fio --name=randread --filename=/tank/testfile --size=4G --direct=1 --rw=randread --bs=4k --iodepth=32 --runtime=20 --time_based
randread: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, ioengine=psync, iodepth=32
fio-3.33
randread: IOPS=420, BW=1.64MiB/s (1.72MB/s)(32.8MiB/20001msec)
lat (usec): min=210, max=185000, avg=76000.15, stdev=22100.42
Значення: 4K випадкові читання жорстокі: кілька сотень IOPS і погана хвостова латентність. Це нормальна поведінка для HDD.
Рішення: Якщо ваше продуктивне навантаження схоже на це, потрібні архітектурні зміни: mirrors, більше шпиндлів, більше RAM/ARC або зробіть IO менш випадковим (пакетування, більші блоки, кешування поза ZFS).
Завдання 12: Визначити запас по послідовній пропускній здатності
cr0x@server:~$ sudo fio --name=seqread --filename=/tank/testfile --size=8G --direct=1 --rw=read --bs=1M --iodepth=8 --runtime=20 --time_based
seqread: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, ioengine=psync, iodepth=8
fio-3.33
seqread: IOPS=620, BW=620MiB/s (650MB/s)(12.1GiB/20003msec)
lat (msec): min=2, max=52, avg=12.9, stdev=4.2
Значення: Послідовні читання сильні. Тож якщо ваш додаток повільний, ймовірно він виконує непослідовні IO або примушує sync-затримку.
Рішення: Налаштуйте recordsize і патерни навантаження в бік послідовного IO де можливо (потокові записи, більші IO, менше fsync).
Завдання 13: Перевірити затримки на рівні диска, щоб знайти одного повільного актора
cr0x@server:~$ iostat -x 2 3
avg-cpu: %user %nice %system %iowait %steal %idle
6.12 0.00 2.20 18.40 0.00 73.28
Device r/s w/s r_await w_await aqu-sz %util
sda 9.20 45.10 12.40 18.70 1.22 78.0
sdb 10.10 44.90 13.20 19.10 1.24 79.4
sdc 9.40 45.20 12.10 17.90 1.18 77.9
sdd 8.90 44.70 61.30 88.10 5.90 99.8
Значення: Один диск (sdd) має набагато більші await і завантажений на 99.8% util. Це може тягнути весь vdev вниз.
Рішення: Подивіться SMART-статистику і подумайте про проактивну заміну. «Повільний диск» — реальний режим відмови, не забобон.
Завдання 14: Перевірити SMART на перепризначені сектори та pending errors
cr0x@server:~$ sudo smartctl -a /dev/sdd | egrep -i "Reallocated_Sector_Ct|Current_Pending_Sector|Offline_Uncorrectable|SMART overall"
SMART overall-health self-assessment test result: PASSED
5 Reallocated_Sector_Ct 0x0033 098 098 010 Pre-fail Always 12
197 Current_Pending_Sector 0x0012 100 100 000 Old_age Always 4
198 Offline_Uncorrectable 0x0010 100 100 000 Old_age Offline 4
Значення: «PASSED» — марна втіха; pending sectors і uncorrectables — погано. Цей диск спричинить повторні спроби і затримки.
Рішення: Замініть диск, потім resilver у непіковий час і відстежуйте лічильники помилок під час операції.
Завдання 15: Перевірити, чи snapshots не роздувають метадані і робочий набір
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation | head
NAME USED REFER CREATION
tank/vm@auto-2025-12-01 28G 18.4T Mon Dec 1 02:00 2025
tank/vm@auto-2025-12-02 31G 18.4T Tue Dec 2 02:00 2025
tank/vm@auto-2025-12-03 33G 18.4T Wed Dec 3 02:00 2025
Значення: Багато VM-снапшотів зі зростаючим «USED» вказує на churn. Така активність може підвищувати фрагментацію і навантаження на ARC.
Рішення: Встановіть політику утримання. Для HDD-пулів розростання snapshot слід вважати проблемою продуктивності, а не лише ємності.
Швидкий план діагностики
Коли хтось каже «сховище повільне», у вас немає часу на філософські дебати про патерни IO. Потрібен швидкий фільтр, щоб локалізувати вузьке місце:
диск, поведінка ZFS, пам’ять або семантика додатка.
Перше: підтвердіть, проблема пулу чи хоста
-
Перевірте стан пулу і поточну активність.
Запустітьzpool status. Якщо активний scrub/resilver — це ваш заголовок. Якщо є чексум-помилки — припиніть налаштування і почніть інцидент-реакцію. -
Перевірте iowait і свопінг на CPU.
Якщо хост свопить або CPU насичений, симптоми сховища можуть бути вторинними. Високий iowait часто означає, що диски — вузьке місце, але також може означати sync-write блокування.
Друге: визначте, чи ви обмежені затримкою або пропускною здатністю
-
Запустіть
zpool iostat -v 2.
Якщо операцій багато, а пропускна здатність низька — це IOPS/затримково-обумовлена проблема (класичний біль HDD). -
Запустіть
iostat -x 2.
Шукайте один диск з високим await/%util. Один хромий диск може зробити vdev схожим на проєктний провал.
Третє: перевірте на sync-write шторми і метадані-шторми
-
Подивіться на лічильники ZIL і визначте топ-записувачів.
Якщо бачите інтенсивну ZIL-активність і БД/WAL-процес вгорі — ваша «повільність» зустрілася з синхронними семантиками на HDD. -
Перегляньте статистику ARC.
Поганий hit ratio плюс зайняті диски означає, що робочий набір більший за RAM або погана локальність; потрібні зміни в навантаженні або більше пам’яті. -
Перевірте фрагментацію та заповнення.
Високий CAP і зростаючий FRAG — класичні передтечі «раніше було добре».
Що вирішувати з планом
- Якщо працює scrub/resilver: обмежте/перенесіть у вікно; комунікуйте; не «налаштовуйте» посеред відновлення, якщо не хочете хаосу.
- Якщо один диск повільний: замініть його; не звинувачуйте ZFS за вмираючий HDD.
- Якщо домінують sync-записи: виправте пакетування в додатку; ізолюйте dataset; не робіть імпульсивно
sync=disabled. - Якщо промахи ARC високі: додайте RAM або зменшіть робочий набір; розділіть datasets; виправте політику snapshot.
- Якщо ви просто виконуєте випадкові 4K I/O на RAIDZ: «налаштування» — це зміна розмітки (mirrors) або очікувань.
Поширені помилки: симптом → причина → виправлення
1) «Записи повільні і здригаються; графіки схожі на пилку»
Причина: TXG flush + sync-write сплески + затримка HDD. Часто погіршується додатком, що робить часті fsync.
Виправлення: Визначте sync-важкий dataset і процес. Збільшіть пакетування в додатку; налаштуйте параметри БД щодо комітів; ізолюйте в dataset з відповідним recordsize; тримайте sync=standard, якщо ви не офіційно приймаєте ризик втрати даних.
2) «Випадкові читання жахливі; навіть ls в директорії гальмує»
Причина: Промахи метаданих в ARC або фрагментовані метадані через churn snapshot і дрібні випадкові оновлення.
Виправлення: Забезпечте достатню RAM; зменшіть кількість snapshot; розгляньте xattr=sa для релевантних datasets; тримайте CAP нижче ~80%; розділіть метаданево-важкі навантаження в окремі datasets і перегляньте recordsize.
3) «Після того як ми заповнили пул понад 85%, все стало повільно»
Причина: Просторові мапи, тиск алокатора, фрагментація; seeks на HDD зростають; metaslabs стають обмеженими.
Виправлення: Звільніть простір агресивно; додайте ємності; мігруйте у більший пул; не тримайте «майже повно» як постійний стан. Резерв простору — це вимога продуктивності.
4) «Scrub тепер займає вічність, і користувачі скаржаться під час scrub»
Причина: Зростаючий обсяг даних, фрагментація, один повільний диск або scrubs під час піку IO.
Виправлення: Перенесіть вікно scrub; перевірте затримки по диску; замініть повільні диски; розгляньте зменшення ширини пулу в майбутньому дизайні; відстежуйте тривалість scrub.
5) «Ми відключили компресію, щоб «зменшити CPU», і продуктивність погіршилась»
Причина: Ви збільшили фізичний IO на HDD; диски знову стали вузьким місцем.
Виправлення: Використовуйте compression=lz4 як базу; вимірюйте CPU. Якщо CPU дійсно є вузьким місцем, зменшіть інші витрати на CPU (шифрування, важкі чексуми) перед тим, як відмовлятися від зменшення IO.
6) «Зберігання для VM на RAIDZ непередбачуване під навантаженням»
Причина: IO VM часто — дрібні випадкові записи + синхроноподібна поведінка з гостьових FS і гіпервізора. RAIDZ підсилює біль.
Виправлення: Використовуйте mirrors для vdev-ів VM; налаштуйте volblocksize; зменшіть частоту fsync у гостях, де це безпечно; розгляньте окремий пул/dataset для VM з жорсткішою політикою.
7) «Один клієнт повільний, інші — ні»
Причина: Мережеві проблеми, клієнтські sync-семантики або несумісність властивостей dataset.
Виправлення: Перевірте властивості dataset для цього шару/тома; порівняйте з відомо робочим dataset; перевірте опції монтування клієнта і поведінку додатка.
Три міні-історії з практики
Інцидент від неправильної передумови: «ZIL — це кеш, чи не так?»
Середня компанія запускала внутрішню аналітичну платформу на пулі ZFS лише на HDD. Все було «нормально», поки нова команда не розгорнула сервіс,
що писав дрібні станні оновлення з синхронною семантикою. Затримки пішли від терпимих до абсурду. Інженер на виклику побачив, що пропускна
здатність дисків низька, і зробив висновок, що пул недовантажений. «У нас достатньо запасу», — сказав він, дивлячись на MB/s графіки, ніби це були таблиці істини.
Вони переключили sync=disabled на dataset, щоб «потестувати». Сервіс одразу прискорився. Тікети зникли. Зміна залишилася.
Ніхто це не задокументував. Так починається завжди.
Через тижні подія з живленням вивела стійку з ладу. Системи повернулися. Сервіс теж — але частина стану була несумісною, і довго шукали, що зникло,
бо додаток вірив, що коміти були довговічні. Рев’ю після інциденту було неприємним у корпоративному стилі: багато «покращень процесу», мало названих технічних помилок.
Неправильна передумова не в «sync=disabled небезпечний». Це відомо. Неправильна передумова була в тому, що ZIL поводиться як кеш записів, який можна ігнорувати.
Насправді ZIL існує, щоб sync-семантика була правдою. На HDD це означає: ротаційна затримка — частина вашого SLA.
Виправлення було нудним: перемістити sync-важливий стан на інший рівень зберігання (згодом SSD), а тимчасово зменшити fsync-частоту пакетуванням і чергуванням.
Також додали політику: будь-які зміни властивостей dataset потребують тікета з описом ризиків. Це не зробило їх швидшими. Але зробило менш крихкими.
Оптимізація, що повернулася бумерангом: «Зменшимо recordsize для всього»
Інша організація експлуатувала ZFS для змішаного навантаження: файлові шари, бекап-таргети і кілька VM-образів. Вони помітили повільний випадковий доступ під час піку і
знайшли пораду в інтернеті про зменшення recordsize для «кращої продуктивності». Без профайлингу вони встановили recordsize=16K рекурсивно на весь пул.
Спочатку один робочий процес покращився: маленька БД на NFS трохи зменшила латентність випадкових читань. Але потім все інше стало гірше. Бекапи тривали довше.
CPU піднявся. Операцій з метаданими стало більше. Ефективність ARC впала, бо кешу потрібно було відслідковувати набагато більше блоків. Scrub тривав довше, бо блоків стало більше.
Через місяць фрагментація зросла, CAP піднявся, і користувачі почали відчувати періодичні паузи при великих передачах файлів. Команда зберігання потрапила у типовий цикл: «налаштуємо ще», щоб виправити поламане.
Вони майже відключили компресію, щоб «зберегти CPU», що було б другим самопошкодженням.
Відновлення було методичним. Вони повернули recordsize для бекапів і файлових шарів до 128K і 1M відповідно, залишивши маленький recordsize лише для бази даних.
Розділили VM-зберігання в окремий dataset, а згодом — у mirrored пул. Продуктивність стабілізувалася.
Урок: recordsize — це скальпель, а не валик фарби. Якщо застосувати його на весь пул, отримаєте наслідки на весь пул.
Нудна, але правильна практика, що врятувала день: «Трендувати тривалість scrub»
Фінансова компанія запустила великий HDD RAIDZ2 пул для архівів відповідності та внутрішнього файлового сховища. У них не було SSD. Вони також не мали ілюзій щодо продуктивності.
Їхній керівник зберігання навчив двом звичкам: scrubs планувалися в не пікові години, і тривалість scrub записувалася щомісяця в простий дашборд. Без складного стеку обсервації.
За кілька місяців тривалість scrub повільно зростала. Не драматично, але помітно: тренд був неправильний. Ніхто поки не скаржився. Ось у чому сенс: ви хочете знаходити проблеми, коли користувачі сплять.
Вони розслідували. Затримки по дисках показали один диск з підвищеним await, але без явних помилок SMART. Він все ще «PASSED». Вони замінили його проактивно в заплановане вікно,
тихо провели resilver — і тривалість scrub повернулась до попереднього рівня.
Через кілька тижнів подібний диск в іншому шасі просто вмер. Та команда отримала гучний інцидент і довгий resilver під навантаженням. Фінансова команда — ні.
Їхня «нудна» практика — трендувати тривалість scrub і перевіряти затримки по диску — окупилася без героїчних ночей.
Чеклісти / поетапний план
Покроковий план налаштування для існуючого пулу лише на HDD
-
Задокументуйте базове навантаження.
Зберіть:zpool iostat -v 2,iostat -x 2, ARC-статистику та топ процесів за IO. Збережіть висновки. -
Перевірте непідлягаючі компромісу речі.
Стан пулу, ashift, відсутність деградованих дисків, відсутність прихованих помилок читання. Якщо диск повільний — вирішуйте апаратно спочатку. -
Розділіть datasets за навантаженням.
Мінімум:tank/vm,tank/db,tank/backup,tank/home. Налаштування виконуються по dataset; спроєктуйте namespace відповідно. -
Встановіть безпечні, орієнтовані значення для dataset.
- Більшість dataset:
compression=lz4,atime=off - Бекапи/медіа:
recordsize=1M - Бази даних: почніть з
recordsize=16Kабо32K(тестуйте) - VM zvols: коректно встановлюйте
volblocksizeпри створенні
- Більшість dataset:
-
Робіть з sync-записами інженерну роботу, а не заперечення.
Тримайтеsync=standard. Якщо затримка неприйнятна, первинне виправлення — пакетування в додатку або переміщення sync-важкого шляху на інший рівень зберігання (навіть «більше RAM і менше fsync»). -
Забезпечте запас простору.
Встановіть внутрішню політику: тримайте HDD-пули нижче ~80% CAP для стабільної продуктивності. Якщо не можете — це інцидент ємності. -
Керуйте snapshot як за витратами.
Тримайте коротку утримку для VM-churn. Архівні snapshot повинні бути обдуманими і обмеженими. -
Плануйте scrubs і відстежуйте тренд.
Scrub щомісяця (зазвичай) у не пікові години. Відстежуйте тривалість. Розслідуйте зміни. -
Переміряйте після кожної зміни.
Одна зміна за раз. Порівнюйте з базою. Залишайте те, що допомагає. Відкатуйте те, що шкодить.
Чекліст: коли команда питає «Чи можемо ми зробити швидше без SSD?»
- Чи навантаження переважно послідовне? Якщо так — налаштуйте recordsize і компресію і можна виграти.
- Чи це випадкове дрібне блокове IO? Якщо так — mirrors і RAM допоможуть; RAIDZ не стане чарівним.
- Чи багато sync-записів? Якщо так — виправте семантику додатка або прийміть затримку; не відключайте sync легковажно.
- Чи пул >80% заповнений або сильно фрагментований? Якщо так — управління ємністю — це робота з продуктивністю.
- Чи якийсь диск повільний або дає помилки? Якщо так — замініть його перед тим, як чіпати налаштування.
Чекліст: безпечні налаштування за замовчуванням для пулів лише на HDD (по dataset)
- Загальні шари:
compression=lz4,atime=off,recordsize=128K - Бекапи:
compression=lz4,recordsize=1M,sync=standard— якщо не прийнято ризик - Бази даних (файли): почніть з
recordsize=16Kабо32K, бенчмарк, тримайтеsync=standard - VM: віддавайте перевагу mirrored vdev-ам; для zvol правильно встановлюйте
volblocksizeпри створенні
FAQ
1) Чи можу я отримати «SSD-подібну» продуктивність від ZFS тільки на HDD за допомогою налаштувань?
Ні. Ви можете отримати «добре спроєктовану HDD-масиву» продуктивність. І це цінно. Перевага — в усуненні самонанесеного IO і узгодженні навантаження з послідовною поведінкою.
2) Чи використовувати RAIDZ чи mirrors для пулів лише на HDD?
Mirrors для IOPS-чутливих навантажень (VM, БД, інтенсивні метадані). RAIDZ для ємнісно-ефективного масивного зберігання і переважно послідовного IO.
Якщо ви запускаєте VM на RAIDZ і це погано — це тому, що так і є.
3) Чи безпечно використовувати compression=lz4 в продакшені?
Так, і зазвичай це швидше на HDD-пулах. Зменшує фізичний IO. Головна причина вимкнути — якщо CPU справді вузьке місце для цього dataset і коефіцієнт стиснення близький до 1.00x.
4) Який recordsize мені слід використовувати?
За замовчуванням 128K для загального призначення. 1M для бекапів/медіа/великих об’єктів. Менші (16K/32K) для баз даних або навантажень з дрібним випадковим IO, але тестуйте.
Recordsize — це регулятор форми IO, а не «макс-настройка».
5) Чи нормально встановлювати sync=disabled для підвищення продуктивності?
Лише якщо ви готові втратити останні записи і явно прийняли цей ризик. Це також може поламати додатки, що покладаються на fsync для коректності. Для більшості продакшнових даних: тримайте sync=standard і виправляйте навантаження.
6) Чи потрібно налаштовувати параметри модуля ZFS для досягнення гарної продуктивності?
Зазвичай ні. Більшість виграшів приходять від розмітки, властивостей dataset, розміру ARC і операційних практик. Налаштування ядра/модуля — це остання миля і легко помилитися, особливо при оновленнях.
7) Чому продуктивність падає при заповненні пулу?
Тиск алокатора і фрагментація зростають, вільний простір стає менш суцільним, і ZFS має менше гарних виборів. HDD платять за seeks. Тримайте запас як частину дизайну.
8) Як часто робити scrub для пулу лише на HDD?
Зазвичай щомісяця, у не піковий час. Реальна відповідь залежить від толерантності до ризику, якості дисків і вікон відновлення. Відстежуйте тривалість scrub і кількість виправлень; тренди важливіші за точний розклад.
9) Чи допомагає dedup продуктивності?
Рідко на пулах лише на HDD. Зазвичай шкодить продуктивності і пам’яті. Якщо вам потрібна продуктивність — використовуйте компресію. Якщо вам потрібна економія місця — вимірюйте і перевіряйте перед увімкненням dedup.
10) Яке одне найбільше покращення без SSD?
Для читально-важких навантажень: більше RAM (ARC). Для змішаних навантажень: правильні властивості dataset і уникнення sync-write штормів. Для VM-важких навантажень: mirrors.
Якщо «все повільно»: припиніть тримати пул майже повним і замініть повільні диски.
Наступні кроки, які можна зробити цього тижня
-
Пройдіть швидкий план діагностики один раз у піковий час.
Збережіть виводиzpool iostat,iostat -xі ARC-статистику. Бази — це як припинити гадання. -
Розділіть datasets за навантаженням та встановіть розумні властивості.
Особливо:recordsizeдля бекапів іatime=offдля більшості серверних dataset. -
Полюйте за sync-write штормами.
Визначте, хто викликає fsync і навіщо. Виправляйте на рівні додатка, якщо можливо. Не «вирішуйте» цеsync=disabled. -
Перевірте на наявність повільного диска.
Використовуйтеiostat -xі SMART. Один поганий актор може виглядати як системний регрес продуктивності. -
Забезпечте запас місця і політику утримання snapshot.
Якщо пул трендиться вище 80% CAP — поводьтеся як з інцидентом ємності. Це дешевше, ніж розслідування продуктивності. -
Трендуйте тривалість scrub.
Це нудно. Це працює. Це та практика, яку ви будете висміювати, поки вона не врятує вас.
Продуктивність ZFS лише на HDD — це дисципліна: правильна розмітка, коректні семантики, розумні межі dataset і невпинне вимірювання. Ви не намагаєтеся вигравати бенчмарки.
Ви намагаєтеся виграти вівторки.