Ваш пул ZFS «показував добрі результати в бенчмарку», доки не настав день, коли треба було пропустити кілька терабайтів — відтворення медіа, бекапи, аналітичні скани, відновлення об’єктного сховища — і пропускна здатність впала в сумну пилкоподібну картину. CPU виглядає байдуже. Диски завантажені. Мережа звинувачує сховище, сховище звинувачує мережу, і хтось пропонує додати більше RAM, ніби це свята вода.
Тут послідовні читання на ZFS стають цікавими: ZFS може бути жорстоко швидким у потоковому читанні, але це також файлова система зі своїми принципами, кількома кешами,
контрольними сумами скрізь і транзакційною моделлю, яка іноді перетворює «просто» на «складно з шармом». Налаштуємо її, як для реальних продакшн-систем.
Що означає «послідовні читання» у ZFS (і що ні)
«Послідовне читання» — це опис навантаження, а не гарантія. На обертових дисках послідовні читання означають довгі суміжні ділянки на диску, мінімум перемикань голівки та великі I/O.
На SSD це здебільшого означає великі I/O і високий рівень чергування без дрібних випадкових читань, що засмічують конвеєр.
У ZFS послідовні читання формуються трьома шарами реальності:
- Логічна послідовність: ваш застосунок читає офсети файлу по порядку.
- Розташування на диску: блоки можуть бути не фізично суміжними через copy-on-write і фрагментацію вільного простору.
- Поведение I/O у ZFS: recordsize, prefetch, перевірка контрольних сум, компресія та кешування вирішують, які саме I/O ви фактично ініціюєте.
Ви можете мати логічно послідовне читання в застосунку, яке все одно стає «напіввипадковим» на диску, бо файл переписувався місяцями,
снапшоти тримали старі блоки, а вільний простір перетворився на конфетті. Це не те, що ZFS погана; це чесність ZFS щодо фізики.
Ваша мета для максимальної пропускної здатності потоків — змусити ZFS видавати великі читання, тримати чергу достатньо глибокою, щоб наситити пристрої,
уникати марного зриву кешу і переконатися, що CPU не є прихованим вузьким місцем. Найшвидші пули — не ті, що мають найбільше хитрощів; це ті, де найменше сюрпризів.
Факти та історія, що мають практичне значення
- ZFS з’явився в Sun Microsystems у середині 2000-х зі скрізними контрольними сумами як фундаментальною особливістю. Перевірка контрольних сум — це не «накладні витрати»; це частина дизайну.
- Початкова презентація ZFS включала «немає fsck» — велика операційна перевага. Для потокових читань це також означає, що цілісність метаданих постійно охороняється, а не епізодично.
- Copy-on-write — причина дешевих снапшотів — і також причина того, що довготривалі набори даних можуть фрагментуватися. Пропускна здатність потоків часто погіршується з часом, якщо ви переписуєте великі файли на місці.
- «128K блоки» стали культурно стійкими тому, що recordsize=128K добре працює для багатьох послідовних навантажень. Це не магія; це просто пристойний дефолт.
- L2ARC з’явився, щоб розширити кеш на SSD коли RAM був дорогий. Він допомагає випадковим читанням більше, ніж тривалим потокам, бо потокові читання часто «читаються один раз».
- ZFS send/receive змінив бекап-процеси, роблячи реплікацію блоково-обізнаною. Це також генератор потокових читань, який швидко виявляє межі пропускної здатності пулу.
- OpenZFS уніфікував кілька форків, тому рекомендації не є «фольклором тільки Solaris», але дефолти все ще різняться між платформами й версіями.
- Сучасний ZFS має спеціальні класи алокацій (special vdev) для відокремлення метаданих/малих блоків. Це може опосередковано підвищити потік, зменшуючи блокування метаданих.
- Алгоритми контрольних сум еволюціонували (fletcher4, sha256 тощо). Потужніші хеші коштують CPU; швидкі хеші — менше. Потокове читання може стати CPU-залежним, перш ніж ви це помітите.
Шлях даних при потоковому читанні: де гине пропускна здатність
Якщо ви хочете максимальної пропускної здатності, треба знати, на якому етапі вас обмежують. Послідовне читання у ZFS зазвичай проходить так:
- Застосунок ініціює читання (зазвичай 4K–1MB залежно від застосунку/бібліотеки).
- DMU мапить офсети файлу на блоки на основі recordsize та фактичних розмірів блоків на диску.
- Prefetch приймає рішення чи спекулятивно зчитувати наступні блоки.
- ARC перевіряє кеш — хіти або місси; при miss плануються дискові операції.
- Планувальник vdev перетворює логічні читання у фізичні по дзеркалах/RAIDZ, дотримуючись лімітів черги.
- Пристрій виконує читання; дані перевіряються контрольними сумами, розпаковуються якщо треба, потім потрапляють в ARC і копіюються в userland.
Пропускна здатність потоків зазвичай гине в одному з чотирьох місць:
- Пропускна здатність пристрою (просто недостатньо шпинделів, ліній або пропускної спроможності SSD).
- Надто малий розмір I/O (багато накладних витрат на байт; ви «виграєте» в IOPS, але програєте в MB/s).
- CPU-залежність через контрольні суми/декомпресію (особливо на швидких NVMe пулах).
- Фрагментація і геометрія RAIDZ (більше операцій I/O, зламане злиття читань або накладні витрати на обчислення парності).
Жарт №1: Якщо ваші «послідовні читання» виглядають випадковими, вітаю — ви винайшли нову категорію бенчмарку: «інтерпретативне сховище».
Швидкий план діагностики (перший/другий/третій)
Перший: доведіть, що саме насичене
- Перевірте пропускну здатність і затримки по vdev за допомогою
zpool iostat -v. Якщо один vdev завантажений повністю, а інші простіють, у вас проблеми з розміщенням або чергуванням. - Перевірте CPU (user/system, softirqs) під час потоку. Якщо CPU росте разом з обмеженою пропускною здатністю, контрольні суми/компресія або обробка переривань може обмежувати вас.
- Перевірте мережу, якщо це віддалено. «Проблема зі сховищем», яка лімітується точно на 9.4 Gbps, часто виявляється проблемою мережі в образі сховища.
Другий: перевірте розмір I/O і поведінку prefetch
- Подивіться recordsize датасету і чи були файли записані з ним. Старі файли зберігають старі розміри блоків.
- Спостерігайте фактичні розміри читань через
zpool iostatіfio. Якщо ви бачите багато читань 4K–16K, ви платите податок на накладні витрати. - Підтвердіть, що prefetch не відключено (параметри модуля) та що доступні патерни не руйнують його (наприклад, багато читачів з перемішаним доступом).
Третій: перевірте стан пулу і «фонве» конфлікти
- Scrub/resilver у процесі можуть сильно знизити пропускну здатність потоків. Те саме стосується масового видалення снапшотів (великий free).
- Помилки або повільні пристрої призводять до повторів і збільшення затримки; дзеркала часто виберуть «швидшу» сторону, але якщо обидві повільні — ви це побачите.
- Місце і фрагментація: пул на 85–90% зайнятості рідко є чемпіоном потокового читання. ZFS потребує простору для «ліктів».
Практичні завдання: команди, виводи, рішення
Ось завдання, які я фактично виконую, коли хтось каже «послідовні читання ZFS повільні». Кожне містить: команду, що означає вивід і яке рішення прийняти далі.
Припущення: Linux + інструменти OpenZFS; адаптуйте шляхи й імена пулів.
Завдання 1: Визначити топологію пулу та рівень RAID (не можна налаштувати те, чого не розумієш)
cr0x@server:~$ sudo zpool status -v tank
pool: tank
state: ONLINE
scan: scrub repaired 0B in 02:11:33 with 0 errors on Wed Dec 18 03:21:39 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
sde ONLINE 0 0 0
sdf ONLINE 0 0 0
errors: No known data errors
Значення: Ви маєте один RAIDZ2 vdev з шести дисків. Потокові читання будуть обмежені агрегатною пропускною здатністю цього vdev і поведінкою RAIDZ.
Один великий vdev — це з точки зору ZFS одна велика «лінія».
Рішення: Якщо потрібно більше пропускної здатності, додавайте vdev-и (більше ліній) або переходьте на дзеркала для кращої паралельності. Налаштування не створить смугу пропускання з нічого.
Завдання 2: Побачити реальний час пропускної здатності, IOPS і затримки по vdev і диску
cr0x@server:~$ sudo zpool iostat -v tank 1 5
capacity operations bandwidth
pool alloc free read write read write
------------------------------------------ ----- ----- ----- ----- ----- -----
tank 8.11T 2.77T 480 5 820M 1.20M
raidz2-0 8.11T 2.77T 480 5 820M 1.20M
sda - - 85 1 135M 256K
sdb - - 76 1 130M 256K
sdc - - 79 1 138M 256K
sdd - - 80 1 140M 256K
sde - - 78 1 139M 256K
sdf - - 82 0 138M 0K
------------------------------------------ ----- ----- ----- ----- ----- -----
Значення: Ви отримуєте ~820 MB/s читань, рівномірно розподілених. Це правдоподібно близько до послідовного ліміту цих дисків.
Якщо ви очікували 2–3 GB/s, то топологія це пояснює.
Рішення: Якщо пул виглядає рівномірно завантаженим і пропускна здатність «розумна», перестаньте звинувачувати ZFS і починайте планувати ємність. Якщо один диск відстає, розслідуйте його.
Завдання 3: Підтвердити налаштування датасету, що впливають на потокові читання
cr0x@server:~$ sudo zfs get -o name,property,value -s local,received recordsize,compression,atime,primarycache,secondarycache tank/media
NAME PROPERTY VALUE
tank/media recordsize 1M
tank/media compression lz4
tank/media atime off
tank/media primarycache all
tank/media secondarycache all
Значення: recordsize 1M (добре для великих потокових файлів), compression lz4 (зазвичай добре), atime вимкнено (добре; уникнення записів при читанні).
Рішення: Якщо recordsize 128K, а ви зберігаєте величезні файли, розгляньте recordsize=1M для цього датасету до інгесту даних. Якщо дані вже є, треба їх переписати/реплікувати, щоб отримати вигоду.
Завдання 4: Перевірити, які фактичні розміри блоків у файлів (не те, чого ви бажаєте)
cr0x@server:~$ sudo zdb -ddddd tank/media | head -n 20
Dataset tank/media [ZPL], ID 52, cr_txg 11423, 2.31T, 98123 objects
Object lvl iblk dblk dsize dnsize lsize %full type
17 1 128K 1M 1.00M 512B 4.00M 100.00 ZFS plain file
18 1 128K 128K 128.0K 512B 512.0K 100.00 ZFS plain file
Значення: Деякі файли використовують 1M блоки, деякі — 128K. recordsize — верхня межа; існуючі дані можуть бути менші через спосіб їх запису.
Рішення: Якщо ключові об’єкти для потоків не використовують великі блоки, плануйте перепис: zfs send|recv, rsync без reflinks або повторний інгест через застосунок.
Завдання 5: Виміряти сирий послідовний пропуск на рівні файлу (обійти «застосунок дивний»)
cr0x@server:~$ fio --name=seqread --filename=/tank/media/bigfile.bin --rw=read --bs=1M --iodepth=32 --direct=1 --numjobs=1 --time_based --runtime=30
seqread: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=32
fio-3.36
Starting 1 process
seqread: Laying out IO file (1 file / 0MiB)
Jobs: 1 (f=1): [R(1)][100.0%][r=1050MiB/s][r=1050 IOPS][eta 00m:00s]
seqread: (groupid=0, jobs=1): err= 0: pid=21944: Thu Dec 26 11:08:14 2025
read: IOPS=1048, BW=1048MiB/s (1099MB/s)(30.7GiB/30005msec)
clat (usec): min=310, max=2200, avg=720.12, stdev=120.44
Значення: З direct I/O, 1M блоками й помірним iodepth ви отримуєте ~1.0 GiB/s. Це близько до спостережуваної vdev пропускної здатності пулу.
Рішення: Якщо fio швидкий, а застосунок повільний, вузьке місце — у застосунку (малі читання, однопотоковість, синхронні точки). Якщо fio повільний — копаємо глибше в сторону сховища.
Завдання 6: Перевірити розмір ARC і коефіцієнти хітів (потокове читання часто має багато miss і це нормально)
cr0x@server:~$ arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
11:09:01 820M 790M 96 790M 96 0 0 0 0 42G 64G
11:09:02 835M 810M 97 810M 97 0 0 0 0 42G 64G
11:09:03 810M 785M 96 785M 96 0 0 0 0 42G 64G
Значення: Рівень miss у ARC високий під час потоку. Це нормально: потокове читання — «прочитав і пішов».
arcsz 42G з цільовим c=64G, отже ARC здоровий.
Рішення: Не панікуйте та не «збільшуйте ARC» тільки через високі miss. ARC допомагає при повторних читаннях; потокова пропускна здатність зазвичай обмежена пристроєм.
Завдання 7: Перевірити, чи працює prefetch або відключений на рівні модуля
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_prefetch_disable
0
Значення: 0 означає, що prefetch глобально увімкнено.
Рішення: Якщо там 1 і ваше навантаження здебільшого послідовне, поверніть його в 0 (і знайдіть, хто відключив і навіщо).
Завдання 8: Перевірити насичення пристроїв та поведінку черг
cr0x@server:~$ iostat -x 1 3
Device r/s rkB/s rrqm/s %rrqm r_await rareq-sz aqu-sz %util
sda 85.0 138240.0 0.0 0.00 8.40 1626.4 0.71 98.0
sdb 76.0 133120.0 0.0 0.00 8.10 1751.6 0.62 97.2
sdc 79.0 141312.0 0.0 0.00 8.55 1788.8 0.68 98.5
sdd 80.0 143360.0 0.0 0.00 8.60 1792.0 0.69 98.7
sde 78.0 142336.0 0.0 0.00 8.45 1824.8 0.66 98.3
sdf 82.0 141312.0 0.0 0.00 8.50 1723.3 0.70 98.6
Значення: %util близько до 100% і ~8–9ms await вказують на насичення дисків. rareq-sz велике (~1.6–1.8MB), що добре для потоків.
Рішення: Якщо пристрої насичені, налаштування над vdev рівнем не допоможе. Додайте шпинделі, додайте vdev-и або перейдіть на SSD/NVMe.
Завдання 9: Підтвердити, що ви не боретеся зі scrub/resilver (тихі вбивці пропускної здатності)
cr0x@server:~$ sudo zpool status tank | sed -n '1,12p'
pool: tank
state: ONLINE
scan: scrub in progress since Thu Dec 26 10:41:12 2025
2.11T scanned at 1.20G/s, 1.02T issued at 595M/s, 8.11T total
0B repaired, 12.59% done, 03:56:18 to go
Значення: Scrub споживає ~595 MB/s виданої пропускної здатності. Ваші потокові читання конкурують за ті ж диски.
Рішення: Якщо це вікно продакшн-потоку, призупиніть/скасуйте scrub або заплануйте його коректно. Scrub — корисний; scrub під час піку — ні.
Завдання 10: Перевірити ashift і вирівнювання секторів (погане вирівнювання — назавжди)
cr0x@server:~$ sudo zdb -C tank | grep -E 'ashift|vdev_tree' -n | head -n 8
45: vdev_tree:
61: ashift: 12
Значення: ashift=12 означає 4K сектори, що коректно для більшості сучасних HDD/SSD. Якщо ви бачите ashift=9 на 4K дисках, продуктивність може сильно страждати.
Рішення: Якщо ashift невірний, виправлення — перебудова vdev з правильними налаштуваннями. Немає «налаштування», що зніме біль від невірного вирівнювання.
Завдання 11: Оцінити фрагментацію та тиск за місцем (послідовні читання ненавидять переповнений пул)
cr0x@server:~$ sudo zpool list -o name,size,alloc,free,capacity,fragmentation tank
NAME SIZE ALLOC FREE CAPACITY FRAG
tank 10.9T 8.11T 2.77T 74% 33%
Значення: 74% заповнення — нормально; 33% фрагментація — помірна. Якщо заповнення 90% і фрагментація висока, «послідовне» перетвориться в більш випадкове.
Рішення: Якщо ви вище ~80–85% на пулі, що має бути потоковим, плануйте розширення або міграцію. Якщо фрагментація висока, розгляньте перепис датасетів або відновлення з реплікації, щоб упакувати блоки заново.
Завдання 12: Перевірити primarycache/secondarycache по датасетах (не кешуйте те, що не будете перечитувати)
cr0x@server:~$ sudo zfs get -o name,property,value primarycache,secondarycache tank/backup
NAME PROPERTY VALUE
tank/backup primarycache metadata
tank/backup secondarycache none
Значення: Цей датасет кешує лише метадані і уникає L2ARC. Часто це вірно для одноразових перевірок бекапів і великих послідовних читань.
Рішення: Якщо потокові навантаження витісняють корисний кеш для інших сервісів, встановіть streaming-датасети з primarycache=metadata. Не ставте ARC як «шведський стіл» для всього підряд.
Завдання 13: Перевірити, чи не саботує вас readahead на блоці Linux
cr0x@server:~$ lsblk -o NAME,TYPE,RA,MOUNTPOINT | grep -E 'tank|zd|sd[a-z]'
sda disk 256
sdb disk 256
sdc disk 256
sdd disk 256
sde disk 256
sdf disk 256
Значення: Readahead тут 256 (KiB одиниці в багатьох дистрибутивах). ZFS робить власний prefetch; Linux readahead зазвичай важливіший для не-ZFS.
Проте дивні значення (наприклад, кілька мегабайт на диск) можуть викликати марні I/O.
Рішення: Якщо хтось «налаштував» readahead і воно гігантське, поверніть до гідних дефолтів. Виправляйте один шар за раз.
Завдання 14: Підтвердити, що ваш потік не є таємно роботою з дрібними читаннями
cr0x@server:~$ sudo zpool iostat -r tank 1 2
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 8.11T 2.77T 8500 3 420M 1.00M
---------- ----- ----- ----- ----- ----- -----
tank 8.11T 2.77T 8600 2 430M 1.10M
---------- ----- ----- ----- ----- ----- -----
Значення: 8.5K операцій читання для ~420 MB/s означає середній розмір I/O близько ~50 KB, що небажано для «потоку».
Рішення: Якщо розмір I/O малий, виправте застосунок (збільште розмір читання/чергу), встановіть recordsize=1M для майбутніх записів і перевірте, чи prefetch не руйнується багатьма перемішаними читачами.
Рукоятки налаштування, що реально впливають на пропускну здатність
1) recordsize: встановлюйте під файли, які будете зберігати, а не під ті, що вже зберегли
recordsize встановлює максимальний розмір блока, який ZFS використовуватиме для файлів у датасеті. Для потокових читань більші блоки зазвичай означають менше операцій I/O,
менше обчислень контрольних сум на байт, менше звернень до метаданих і кращу ефективність пристрою.
Що робити:
- Медіа, бекапи, великі об’єкти, аналітичні скани:
recordsize=1Mчасто є хорошою початковою точкою. - Бази даних і образи ВМ: не збільшуйте recordsize сліпо. Це зазвичай змішані/випадкові патерни; оптимізуйте окремо.
Підстереження: recordsize не переписує існуючі файли. Якщо ваш «потоковий датасет» створений з 128K і наповнювався рік, зміна на 1M сьогодні допоможе лише новим записам.
Якщо вам потрібно вигоду вже зараз — переписуйте дані.
2) atime: вимкніть для потокових читань, якщо не любите зайві записи
atime оновлюється при читанні і викликає записи. Записи спричиняють TXG-роботу і потенційно додаткову дискову активність.
Для чисто потокового датасету atime=off — нудна, але корисна оптимізація.
3) primarycache / secondarycache: припиніть отруювати ARC потоками, що читаються один раз
ARC — цінний, бо це RAM і воно гаряче. Потокові читання великих датасетів можуть витісняти справді корисні об’єкти з кешу для інших сервісів (метадані, малі гарячі файли,
часто читаються конфіг-блоби тощо).
Для датасетів, що здебільшого читаються одноразово (перевірки бекапів, архівне відновлення, пакетна аналітика), встановіть:
primarycache=metadata. Часто також ставлять secondarycache=none, якщо є L2ARC і не хочуть спалювати його на «смiття».
Це не обов’язково збільшить пропускну здатність самого потоку; це запобігає побічним збиткам. У реальних системах це — половина боротьби.
4) Компресія: lz4 зазвичай «безкоштовна», поки не стане нею
compression=lz4 часто підвищує пропускну здатність потокового читання, бо ви читаєте менше байтів з диска і тратите трохи CPU на декомпресію.
Це хороший компроміс для HDD-пулів і багатьох SSD-пулів.
Але на швидких NVMe компресія може перемістити вузьке місце на CPU. Ви побачите недовантажені пристрої і CPU-потоки, завантажені декомпресією та перевіркою контрольних сум.
Якщо ваш конвеєр CPU-залежний, розгляньте швидші CPU, перевірте, чи не використовується важка контрольна сума, і порівняйте бенчмарки з компресією і без.
5) Не плутайте «більше ARC» з «більшою пропускною здатністю»
ARC допомагає, коли ви перечитуєте дані. Потокова пропускна здатність зазвичай обмежується пропускною здатністю сховища і тим, наскільки ефективно ZFS видає I/O.
Збільшення ARC може допомогти, якщо ваш потік повторюється або якщо метадані є прихованим обмежувачем, але для одноразових читань це часто просто міняє, хто займає RAM.
6) Паралелізм: один vdev — одна лінія
ZFS масштабує пропускну здатність в основному додаванням vdev-ів. Дзеркала зазвичай забезпечують кращу паралельність читань і простішу геометрію.
RAIDZ може бути пропускною і ємнісно-ефективним, але один RAIDZ vdev — це все ще один vdev, і він обмежує конкурентність.
7) Special vdev: метадані іноді варто винести на швидке сховище
Якщо ваша потокова задача все одно спотикається через метадані (багато файлів, багато обхідних операцій, малі файли-супутники), переміщення метаданих і малих блоків на special vdev
може зменшити блокування на HDD. Це більше про «час до першого байта» і стабільність потоку у навантаженнях з великою кількістю файлів, ніж про сирі MB/s для одного великого файлу.
Жарт №2: L2ARC для потокового читання — як брати валізу на одноденну поїздку — технічно вражає, практично непотрібно.
Розміщення vdev і математичні розрахунки пропускної здатності, які можна захистити на нараді
Налаштування послідовного читання стає екзистенційним, коли хтось питає: «Чому цей пул з 12 дисків не може робити 4 GB/s?» Бо топологія важить більше, ніж оптимізм.
Дзеркала: нудний вибір для високої пропускної здатності
Дзеркала читають з будь-якої сторони. З кількома mirror vdev-ами ZFS може розподіляти читання по vdev-ам і по дисках. Для великих потокових читань дзеркала зазвичай дають:
- високу паралельність (багато vdev-ів)
- передбачувану продуктивність при змішаному навантаженні
- просту поведінку при відновленні (resilver простий)
Обмін — ефективність по ємності. Інженери зі зберігання можуть сперечатися про це годинами — це визнаний вид кардіо в деяких організаціях.
RAIDZ: ефективний по ємності, але чутливий до геометрії
RAIDZ може добре стрімити, особливо з кількома RAIDZ vdev-ами, але є дві практичні реалії:
- Одновендовий вузький мес: Один RAIDZ vdev — один vdev. Якщо ваш пул має один RAIDZ2 vdev, ви обмежені перформансом цього vdev.
- Малі читання болять сильніше: RAIDZ має реконструювати дані з парності для певних патернів доступу, і малі блоки можуть створювати підвищення читань.
Для максимальної послідовної пропускної здатності віддавайте перевагу декільком vdev-ам розміру, що відповідає операційним обмеженням. Якщо ви не можете додати vdev-ів, бо шасі повне,
ваші варіанти налаштування зводяться в основному до не погіршення ситуації.
Широкі vdev-и: спокусливо в електронній таблиці, але складно в продакшні
Дуже широкі RAIDZ vdev-и виглядають добре по ємності за долар. Вони також збільшують час відновлення, вік експозиції і можуть створювати різкі падіння продуктивності, коли диск повільніє.
Якщо ваша головна мета — потокова пропускна здатність і передбачуваність операцій, широкі vdev-и — це домовленість з ентропією.
Глибина черги та конкурентність: потік — це конвеєр, а не одиночний запит
Багато застосунків читають послідовно, але однопотоково з малими буферами. Це може залишити пул недовантаженим, бо диски/SSD хочуть багато одночасних I/O.
Коли fio з iodepth=32 дає 1.5 GB/s, а ваш застосунок дає 400 MB/s, застосунок сам себе обмежує.
Виправте це, збільшивши розмір читання застосунку, увімкнувши асинхронний I/O, додавши потоки читачів або використовуючи інструменти, що ефективно читають великі блоки.
ZFS може prefetch-ити, але це не заміна застосунку, який відмовляється ставити запити в чергу.
Компресія, контрольні суми та CPU: друзі, доки не стануть ворогами
ZFS робить скрізні контрольні суми. Це не опціонально. Хороша новина: перевірка контрольних сум зазвичай достатньо швидка на сучасних CPU.
Погана новина: «зазвичай» кінчається в той момент, коли ви оновлюєте до швидких NVMe і залишаєте той самий CPU від трирічної давності.
Як визначити, що ви CPU-залежні при читанні
- zpool iostat показує, що пристрої не насичені, але пропускна здатність обмежена.
- mpstat/top показують один або більше CPU-ядер завантаженими в kernel/system time.
- fio з direct I/O не збільшує пропускну здатність при збільшенні iodepth понад певну точку.
Компресійні компроміси
lz4 — дефолтна рекомендація з причини: мала вартість CPU, часто краща пропускна здатність і покращення ефективного кешу.
Якщо ваші дані вже стиснені (наприклад, відео), lz4 не сильно допоможе; зазвичай і не зашкодить, але тестуйте.
Сильніша компресія (zstd на високих рівнях) може зменшити дискові I/O ціною CPU. Це може допомогти HDD-пулам для певних датасетів, але для чистої потокової пропускної здатності легко перестаратися і перемістити вузьке місце на CPU. «Більше компресії» — не завжди «більше продуктивності».
Вибір алгоритму контрольної суми
Більшість розгортань використовують fletcher4 (швидко) або sha256 (міцніше, але дорожче по CPU). Для пропускної здатності читань швидші контрольні суми можуть мати значення на NVMe-системах.
Якщо ви в регульованому середовищі, вибір може бути примусовим. Якщо ні — вибирайте прагматично: достатня цілісність і передбачувана продуктивність.
Один вислів, що витримав випробування в операціях: «Сподівання — не стратегія.»
— General Gordon R. Sullivan.
Ви не виправите пропускну здатність читань сподіваннями; ви робите це вимірюваннями і контрольованими змінами.
Prefetch, ARC і чому L2ARC не врятує ваш потік
Prefetch: що він робить для послідовних читань
ZFS prefetch намагається виявити послідовний доступ і опрацювати read-ahead I/O, щоб наступні блоки вже були в польоті.
Для одного великого файлу, який читається послідовно, prefetch зазвичай корисний. Для багатьох перемішаних послідовних читачів він може ставати шумним, але все одно корисним.
Класичний режим відмови: хтось відключає prefetch, щоб «поліпшити latency випадкових читань» для датабази, потім забуває, що це глобально, або забуває, що зміняв,
і тепер медіа-потоки або відновлення бекапів повільніші. Це трапляється частіше, ніж кому приємно зізнатися.
ARC: що кешувати для потокових систем
ARC — це комбінований кеш даних і метаданих. Для потокових навантажень кешування самих потокових даних зазвичай дає мало вигоди, якщо лише ви не перечитуєте їх.
Що має значення: метадані. Якщо навантаження відкриває багато файлів, робить багато stat-викликів, обходить директорії або читає бокові індекси,
кешування метаданих може покращити «підйом» і згладити продуктивність.
L2ARC: коли він допомагає
L2ARC — кеш другого рівня, зазвичай на SSD. Він допомагає, коли робоча множина більша за RAM і часто перечитується.
Потокові читання величезних файлів рідко перечитуються досить скоро, щоб виправдати L2ARC; він також додає накладні витрати, бо ZFS мусить керувати заголовками L2ARC і годувати кеш.
Якщо ваша система — багатокористувацький файловий сервіс і потокові задачі витісняють гарячі дані інших орендарів, L2ARC може допомогти цим іншим орендарям,
але не обов’язково зробить потокову задачу швидшою. Це перемога, але будьте чесні щодо того, яку перемогу купуєте.
Scrub/resilver та інші «фонві» задачі, які такими не є
Потокові читання конкурують з усім, що хоче пропускної здатності диска: scrub, resilver, видалення снапшотів, інтенсивні зміни метаданих і іноді навіть SMART long tests,
якщо контролер і прошивка диска обробляють їх погано.
Операційні правила, що тримають потоки передбачуваними
- Планування scrub: запускайте scrubs, коли можете дозволитися зменшення пропускної здатності. Якщо не можете знайти вікно — ваше планування ємності бреше вам.
- Позиція при resilver: ставте режим деградації як інцидент. Поточна пропускна здатність під час resilver зазвичай зменшена — і це очікувано.
- Видалення снапшотів: масові видалення можуть спричинити інтенсивну активність з вільним простором. Не робіть цього о 10:00 в понеділок.
Три корпоративні міні-історії з полів
Інцидент через неправильне припущення: «воно послідовне, отже мусить бути суміжним»
Команда медіа-пайплайну мала NAS на ZFS, що обслуговував великі відеофайли. Місяцями все було добре: редактори тягнули матеріал, рендер-ноди читали кліпи,
і пропускна здатність була зручною. Потім з’явилося нове шоу і все стало «липким». Відтворення гальмувало. Пакетні транскоди йшли всю ніч і не встигали вранці.
Припущення було простим: «Файли великі і читаються послідовно, отже диски роблять послідовні читання.» Правда була гіршою.
Датасет пережив постійний ажіотаж: інгест, часткові правки, ре-експорти і часті снапшоти для «безпеки».
Файли переписувалися на рівні застосунку, але ZFS copy-on-write перетворював кожне переписування на нові алокації блоків.
Коли ми глянули zpool list, пул був у високих 80% по заповненню і фрагментація була настільки значною, що це мало значення.
zpool iostat показував багато менших читань і більшу затримку, ніж очікувалося для тих самих дисків.
Навантаження було послідовним для застосунку, але не фізично послідовним на диску.
Виправлення не було магічним sysctl. Вони розширили пул, додавши vdev-и, потім відрепліковані дані до нового датасету з потрібним recordsize.
Цей перепис фактично «дефрагментував» дані. Пропускна здатність повернулась. Урок: послідовний доступ на рівні застосунку не гарантує послідовного доступу на диску,
особливо після місяців снапшотів і переписів.
Оптимізація, що відбилася боком: «відключили prefetch, щоб зменшити забруднення кешу»
Одна платформа працювала зі змішаним парком: бази даних, об’єктне сховище і сервіс бекапів на спільному ZFS.
Хтось читав, що prefetch може шкодити latency випадкових читань, особливо для баз даних, які мають власний кеш.
Вони відключили prefetch на рівні модуля, щоб «заспокоїти БД».
Команда БД повідомила про покращення під час піку. Всі тихо пораділи і забули.
Через два тижні пройшов тест відновлення. Пропускна здатність упала різко. Інша команда, що робила аналітичні скани, побачила те саме.
Симптоми були класичними: пул не створював великих гладких конвеєрів читань; він ривками обслуговував запити.
Звіт по інциденту був незручним, бо ніхто не зв’язав глобальний характер параметра prefetch із різноманіттям робочих навантажень.
«Оптимізація» вирішила одну проблему, ввівши іншу.
Ми ввімкнули prefetch і вирішили проблему БД по-правильному: вибір на рівні датасету, налаштування кешування в застосунку і ізоляція навантажень де треба.
Загальна мораль: «глобальне налаштування» — евфемізм для «глобальної зони ураження». Коли ви змінюєте поведінку файлової системи, ви змінюєте її для всіх.
Нудна, але правильна практика, що врятувала день: «ми бенчмаркували план відновлення»
Проект оновлення сховища мігрував платформу бекапу/відновлення на нові сервери з швидшими NIC і більшою кількістю SSD.
Команда зробила непопулярну річ: записали очікувану пропускну здатність по vdev, хосту і мережним шляхам, потім протестували кожен шар окремо простими інструментами.
Це було не гламурно, але вимірювано.
Під час cutover тижня один вузол працював повільніше. Паніка хотіла підпалити невелику пожежу: «ZFS повільний» та «можливо нова прошивка погана».
Але завдяки наявним базовим показникам команда одразу порівняла zpool iostat -v цього вузла з іншими.
Один SSD показав трохи вищу латентність і нижчу пропускну здатність. Не катастрофа, але достатньо, щоб обмежити вузол.
Вони замінили підозрілий пристрій, зробили resilver, і пропускна здатність знову відповідала базі.
Ніяких героїчних налаштувань. Ніяких нічних рулеток зі sysctl. Просто нудлива валідація і заздалегідь узгоджені показники продуктивності.
Перемога була не лише в швидкості; вона була в довірі. Коли знаєш, що «норма», відхилення можна трактувати як факти, а не відчуття.
Поширені помилки: симптом → корінь проблеми → виправлення
1) Симптом: Пропускна здатність значно нижча за специфікації диска, iostat показує малі середні розміри читань
Корінь: Застосунок читає дрібними шматками (4K–64K), що руйнує ефективне потокове читання; або файли збережені в малих блоках через recordsize під час запису.
Виправлення: Збільшити розмір читання/чергу в застосунку; встановити recordsize=1M для датасету і переписати дані; перевірити, що prefetch ввімкнено.
2) Симптом: Один диск у vdev показує більший await і меншу пропускну здатність, пул «пилкоподібний»
Корінь: Повільний або збійний пристрій, проблеми з кабелем/контролером, або SMR-диск під тривалим навантаженням.
Виправлення: Підтвердити з zpool iostat -v і iostat -x; замінити пристрій; перевірити прошивку/контролер; уникати SMR для шарів продуктивності.
3) Симптом: Потокове читання добре, поки не почнеться scrub — тоді все падає
Корінь: Scrub конкурує за пропускну здатність; розклад ігнорує пікові вікна.
Виправлення: Перепланувати scrubs; розглянути політику на рівні пулу; тимчасово поставити на паузу/скасувати під час критичних потоків; планувати більше запасу.
4) Симптом: Під час потоку ARC hit rate низький, і хтось називає це «проблемою кешу»
Корінь: Потокове читання природно має багато miss; ARC не «провалився».
Виправлення: Перестаньте налаштовуватися лише за miss%; фокусуйтеся на насиченні дисків і розмірі I/O; захищайте ARC через primarycache=metadata де доречно.
5) Симптом: NVMe-пул не насичує пристрої, CPU високий, пропускна здатність плато
Корінь: CPU-залежність для перевірки контрольних сум і/або декомпресії; можливо однопотокові читачі.
Виправлення: Проведіть бенчмарки з компресією вкл/викл; забезпечте достатню паралельність (jobs/iodepth); розгляньте швидший CPU, NUMA-пінінг застосунку і уникайте важких рівнів компресії.
6) Симптом: «Ми змінили recordsize але нічого не покращилося»
Корінь: Існуючі файли зберегли свої розміри блоків; recordsize не ретроактивний.
Виправлення: Перепишіть дані: реплікація в новий датасет, повторний інгест або перепис застосунком. Підтвердіть через zdb.
7) Симптом: Пул став повільнішим за кілька місяців без змін апаратури
Корінь: Фрагментація і високе заповнення через CoW + снапшоти + churn переписів.
Виправлення: Тримайте пули продуктивності нижче ~80–85%; додавайте vdev-и; мігруйте/реплікуйте, щоб упакувати блоки; перегляньте політики збереження снапшотів.
Контрольні списки / покроковий план
Покроково: налаштувати датасет для максимальної пропускної здатності потокових читань (нові дані)
- Створіть виділений датасет для потокових об’єктів; не діліться з базами даних/ВМ.
- Встановіть recordsize (типово
1Mдля великих файлів):zfs set recordsize=1M tank/media. - Вимкніть atime:
zfs set atime=off tank/media. - Використовуйте compression lz4, якщо не доведено, що це шкодить:
zfs set compression=lz4 tank/media. - Визначте політику кешування: для одноразових робіт
primarycache=metadata; для спільних гарячих бібліотек — залишайтеall. - Інжестіть дані один раз (уникати перепису); перевірте розміри блоків через
zdb. - Бенчмаркуйте з fio з реалістичними розмірами блоків і iodepth; фіксуйте
zpool iostat -vпід час тесту.
Покроково: відновити пропускну здатність потоків на існуючому «повільному» датасеті
- Запустіть швидкий план діагностики і визначте, чи ви device-bound, CPU-bound або I/O-size-bound.
- Перевірте ємність пулу та фрагментацію; якщо занадто заповнений — розширюйте або мігруйте першочергово.
- Підтвердіть фактичні розміри блоків важливих файлів; не припускайте, що recordsize щось миттєво змінив.
- Зупиніть фонові конфлікти під час тестування (scrub/resilver/видалення снапшотів).
- Перепишіть датасет, якщо блок-сайз/фрагментація проблема: реплікація в новий датасет із правильними налаштуваннями.
- Повторно протестуйте з fio; порівняйте метрики по vdev і завантаження CPU до/після.
Операційний чеклист: утримувати продуктивність потоків від деградації
- Тримайте пули продуктивності нижче ~80–85% заповнення, якщо важлива стабільна пропускна здатність.
- Плануйте scrubs поза вікнами потоків; ставте scrub-перетини як заплановане зниження продуктивності.
- Уникайте змішування навантажень з різними цілями налаштувань на одному датасеті.
- Бенчмаркуйте після змін; зберігайте один «відомо добрий» процедуру бенчмарку.
- При зміні глобальних параметрів модуля документуйте і переглядайте їх так само, як правила брандмауера.
Питання й відповіді (FAQ)
1) Чи ставити recordsize=1M скрізь, щоб отримати кращі послідовні читання?
Ні. Встановлюйте його на датасети, що зберігають великі потокові файли. Бази даних, образи ВМ і змішані випадкові навантаження часто працюють гірше з надмірно великими блоками.
2) Чому після зміни recordsize мої існуючі файли не покращилися?
Тому що recordsize не переписує блоки. Існуючі файли зберігають розміри блоків, з якими були записані. Потрібно переписати або реплікувати дані, щоб отримати вигоду.
3) Чи завжди prefetch корисний для послідовних читань?
Зазвичай так для одного послідовного потоку. Він може бути менш ефективним при багатьох перемішаних читачах. Відключення глобально — рідко хороша ідея, якщо ви не довели конкретну користь.
4) Чи L2ARC збільшує потокову пропускну здатність?
Зазвичай ні для одноразових потоків. L2ARC допомагає, коли дані перечитуються і робоча множина не вміщається в RAM. Для потоків це часто накладні витрати з малою віддачею.
5) Чому під час потоку miss% ARC такий високий? Це погано?
Високе miss% нормально для навантажень «прочитав і пішов». Важливо, чи пристрої насичені і чи розміри читань достатньо великі.
6) RAIDZ чи дзеркала для максимальної пропускної здатності послідовних читань?
Дзеркала зазвичай дають кращу паралельність і більш передбачувані читання при змішаних навантаженнях. RAIDZ теж може добре стрімити, особливо з кількома vdev-ами, але один широкий RAIDZ vdev обмежує пропускну здатність.
7) Чи компресія може покращити потокову пропускну здатність?
Так. lz4 часто покращує пропускну здатність, зменшуючи байти, що читаються з диска. На швидких NVMe або у CPU-обмежених системах компресія може стати вузьким місцем; тестуйте на своїх даних.
8) Як зрозуміти, що я CPU-залежний при читаннях ZFS?
Якщо пристрої не насичені, пропускна здатність плато і CPU зайнятий (часто в kernel/system time), ймовірно, ви CPU-залежні через перевірку контрольних сум/декомпресію або обмежені однопотоковими читачами.
Підтвердіть тестами fio і метриками CPU системи.
9) Мій пул на 90% заповнений. Чи налаштування врятують потокову продуктивність?
Налаштування можуть пом’якшити ситуацію, але вони не повернуть закони фізики. Високе заповнення ускладнює алокацію і фрагментацію. Розширюйте, додавайте vdev-и або мігруйте.
10) Чому scrub так сильно впливає на мої потокові читання?
Scrub — це по суті структуроване, невблаганне читання по всьому пулу. Воно прямо конкурує з потоковими читаннями за ту саму пропускну здатність пристроїв.
Наступні кроки
Якщо ви хочете максимальної потокової пропускної здатності на ZFS і щоб вона була надійною — не лише в день бенчмарка — зробіть це в порядку:
- Виміряйте вузьке місце:
zpool iostat -v,iostat -x, метрики CPU і прямий тестfioпослідовного читання. - Виправте великі рукоятки: кількість/топологія vdev-ів, recordsize датасету для нових даних, розмір читання/черга у застосунку.
- Захистіть решту системи: політики кешування (
primarycache=metadata), планування scrub і уникнення глобальних налаштувань з неочікуваним впливом. - Перепишіть при потребі: якщо розміри блоків і фрагментація проблема, реплікація в свіжий датасет часто найчистіший «дефраг».
Потім запишіть, як виглядає «добре» для вашого пулу: очікувані MB/s на vdev, типовий latency і яка фонова активність прийнятна. Майбутньому собі це сподобається,
навіть якщо теперішньому здається, що документація — це для інших.