Коли ZFS «повільний», люди звинувачують диски, потім мережу, потім «той гучний сусідній датасет». Насправді ж часто винним є метадані: крихітні IO, переслідування вказівників і dnodes, які більше не поміщаються в кеш. Ваш пул може видавати гігабайти на секунду великих послідовних читань і при цьому впасти, коли ви намагаєтеся виконати git status великого репозиторія, розпакувати дерево ядра або просканувати мільйони об’єктів розміром 4–16 KB.
Це та проблема продуктивності, яка псує виклики на вихідні, бо виглядає одночасно як щось і ні про що. CPU начебто в порядку. Пропускна здатність теж здається нормальною. І все ж затримки підскакують, операції з каталогами повзають, а застосунок кричить, що «база впала», тоді як ваші дашборди показують, що диски зайняті лише наполовину.
Що насправді означає «вузьке місце метаданих» у ZFS
У ZFS «метадані» — це не абстрактне поняття. Це конкретні блоки та структури на диску, що описують усе інше: набори об’єктів, вказівники блоків, непрямі блоки, dnodes, записи каталогів (ZAP), spacemaps, класи розподілу, журнали намірів та інше. Метадані — це також шаблон навантаження: багато крихітних читань/записів, багато випадкових IO, численні залежності та часті синхронні семантики, які примушують до порядку виконання.
Вузькі місця метаданих виникають, коли система витрачає більше часу на те, щоб з’ясувати де дані і що це, ніж на пересилання самих даних. Ось чому пул, який стримує бекапи на повну, може провалитись через:
- CI-завдання, що створюють/видаляють мільйони файлів щодня
- шари контейнерів та розпакування образів
- патерни на кшталт maildir (багато дрібних файлів, багато перейменувань)
- флот віртуальних машин зі снапшотами, клонуванням і великою мінливістю
- будь-що, що обходить каталоги й виконує stat на масштабі
Навантаження метаданих — це насамперед проблема затримки. Ворог — не «низька пропускна здатність», а черги та час очікування: ланцюжок залежних читань, де кожен пропуск у ARC перетворюється на дисковий IO, занадто малий, щоб компенсувати накладні витрати на переміщення головок/флеш-трансляцію, і який блокує наступний пошук.
Отже, зміна мислення: при навантаженні, що залежать від метаданих, ваш бюджет IOPS і бюджет попадань у кеш важать більше, ніж бюджет МБ/с. Ви можете мати достатньо пропускної здатності і водночас бути в повному застої.
Dnodes 101: крихітна структура, що вирішує вашу долю
Що таке dnode?
Dnode — це пер-об’єктна структура метаданих у ZFS. Для файлу dnode описує його тип, метадані розміру блоків, bonus-дані та як знайти блоки даних файлу (через вказівники блоків). Для каталогів та інших об’єктів ZFS він виконує ту саму роль: «цей об’єкт існує і ось як до нього дістатися».
Звучить нешкідливо. Насправді dnodes беруть участь майже в кожній операції з метаданими, яка вам важлива: пошуки, stat, права доступу, розширені атрибути та обхід непрямих блоків.
Чому важливий dnodesize
dnodesize контролює, наскільки великим може бути dnode. Більші dnode можуть зберігати більше «bonus»-інформації вбудовано (особливо корисно для розширених атрибутів). Але більші dnode також збільшують метаданевий слід. Більший слід означає менше dnode в ARC при тій самій кількості RAM, більше промахів, більше дискових IO і повільніші операції з метаданими.
Це не теорія. Це одна з найпоширеніших причин, чому система на ZFS «добре працює з великими файлами» і «жахливо для дрібних файлів». Команда застосунків називає це регресією зберігання. Ви називаєте це математикою.
Bonus-дані та xattr: прихований множник
ZFS може зберігати розширені атрибути (xattrs) різними способами. Коли xattr зберігаються як sa, вони поміщаються в бонус-область dnode, коли це можливо. Це чудово, бо уникнути додаткового IO на атрибут — аж доки ви не підвищите dnodesize всюди й не виявите, що просто зробили метадані кожного об’єкта важчими, незалежно від того, чи потребували вони цього.
Жарт №1: Метадані схожі на канцелярію в офісі — ніхто не хоче більше паперів, але вони чомусь продовжують з’являтися, поки ви спите.
«Але я встановив dnodesize=auto, тож я в безпеці.» Не обов’язково.
dnodesize=auto дозволяє ZFS збільшувати розмір dnode, щоб вмістити bonus-дані. Це часто допомагає для навантажень з великою кількістю xattr. Але це також означає, що ваш датасет може поступово накопичувати більші dnode під час створення файлів. Якщо згодом ви перейдете до «мільйонів крихітних файлів без xattr», ви все одно можете платити податок за кешовий слід через попередні рішення, снапшоти та клони. ZFS чесний; він пам’ятає.
Де живуть метадані і чому це дорого
ARC: ваш демпфер для метаданих
ARC (Adaptive Replacement Cache) — це не просто кеш читання; це ваш прискорювач метаданих. Для навантажень, що залежать від метаданих, рівень попадань ARC — різниця між «достатньо швидко» і «пейджинг через пул по одному 4K читанню за раз». Доступ до метаданих схильний до випадковості і важко передбачуваний. Тому кешування — король.
Але ARC — це загальний ресурс. Дані блоки, метадані, потоки попереднього читання і списки MFU/MRU всі конкурують. Якщо ваш ARC переповнений потоковими читаннями або великими блоками, метадані можуть бути витиснуті. Система тоді «працює сильніше», видаючи більше дрібних читань, що підвищує затримку, змушує користувача скаржитися, змушує когось запускати ще діагностики, що додає навантаження. Вітаємо: ви створили зворотний зв’язок.
Непрямі блоки, вказівники блоків і переслідування вказівників
ZFS — copy-on-write і використовує дерево вказівників блоків. Операція над метаданими часто перетворюється на: прочитати dnode → прочитати(и) непрямий блок(и) → прочитати блоки ZAP → можливо прочитати SA spill-блоки → повторити. Кожен крок залежить від попереднього. Якщо ці блоки не закешовані, операція стає серією синхронних випадкових читань.
Ось чому NVMe так часто «вирішує» біль метаданих: не тому, що навантаження раптом стало послідовним, а тому, що латентність випадкових читань впала на порядок. На дисках можна мати багато шпинделів і все одно програти, бо ланцюжок залежностей серіалізує ефективну пропускну здатність.
Special vdev: навмисне відокремлення метаданих (та дрібних блоків)
OpenZFS підтримує vdev «special» класу (часто звуть special vdev), який може зберігати метадані і, за бажанням, дрібні блоки. Правильно зроблено — це один з найпотужніших інструментів, щоб перетворити систему, зв’язану метаданими, на терпиму.
Неправильно зроблено — це перетворюється на джерело драм у стилі «чому пул не імпортується», бо втрата special vdev може зробити пул неімпортованим, якщо там зберігаються метадані. Ставтесь до нього як до топ-рівня сховища: дзеркальте, моніторте і правильно розмірюйте.
Синхронні записи і ZIL
Операції з метаданими часто включають синхронні семантики: створення, перейменування, інтенсивні fsync-патерни, бази даних, які наполягають на порядку. ZFS використовує ZIL (ZFS Intent Log) для задоволення синхронних записів безпечно. Окремий SLOG-пристрій може знизити затримку для синхронних записів, але він не робить метадані читання швидшими. Він допомагає, коли вузьке місце — «затримка синхронного запису», а не «латентність випадкового читання метаданих».
Інакше кажучи: SLOG — не пластир для повільного обходу каталогів. Це пластир для застосунків, що потребують синхронних записів і чутливі до затримки.
Снапшоти: подорож у часі метаданих не безкоштовна
Снапшоти дешеві, поки ви не почнете використовувати їх як систему контролю версій для цілих файлових систем. Кожен снапшот зберігає старі вказівники блоків. Це збільшує метадані, які потрібно підтримувати доступними, підвищує фрагментацію і може підвищити вартість видалень (бо видалення — не видалення, а «звільниться, коли жоден снапшот більше не посилається на нього»). Чим більше снапшотів і чим вища мінливість, тим більше метаданих залучено до повсякденного прибирання.
Режими відмов: як вузькі місця метаданих проявляються в продакшені
Кластер симптомів A: «Сховище повільне, але iostat виглядає добре.»
Класика. Ви бачите помірну пропускну здатність, помірну кількість IOPS, але користувацька затримка. Чому? Бо вузьке місце — це затримка на операцію, а не агрегований пропуск. Скан каталогу може виконувати по одному-дві залежні операції одночасно, ніколи не створюючи достатньої глибини черги, щоб «виглядати зайнятим», але кожне читання достатньо повільне, щоб зіпсувати досвід користувача.
Кластер симптомів B: «CPU високе в системному часі; застосунок заблокований.»
Навантаження, що залежать від метаданих, навантажують DMU, шар vnode і перевірку контрольних сум/шляхи компресії. Високе системне CPU плюс багато контекстних переключень можуть бути навіть при ненавантажених дисках — особливо, якщо система трясеться від промахів кеша і обробляє багато дрібних завершень IO.
Кластер симптомів C: «Все було добре, поки ми не додали снапшоти / не увімкнули xattr / не змінили dnodesize.»
Це множники. Снапшоти зберігають історію, що зберігає доступність метаданих. Xattr у вигляді SA можуть збільшити використання bonus-даних. Більші dnode збільшують кешовий слід. Кожен із них раціональний сам по собі. Разом вони можуть перетворити «достатньо швидко» на «чому ls виконується 10 секунд».
Кластер симптомів D: «Scrub/resilver роблять кластер непрацездатним.»
Scrub і resilver зачіпають метадані і дані широко. Якщо ви вже близькі до межі, фоновий IO штовхає вас у видиму користувачем затримку. Промахи метаданих зростають, бо ARC витісняється scrub-читаннями. Якщо ваш special vdev перевантажений або метадані на повільних дисках — ефект ще гірший.
Кластер симптомів E: «Видалення повільне; звільнення простору займає вічність.»
Видалення за навантаженням снапшотами означає обхід метаданих, оновлення spacemaps і відкладене фактичне звільнення. Якщо ви звільняєте мільйони дрібних файлів, система виконує величезну кількість оновлень метаданих. Якщо фрагментація metaslab висока, алокації й звільнення стають дорожчими.
Цікаві факти та історичний контекст (те, що пояснює сьогоднішні дивності)
- ZFS народився в середині 2000-х у Sun Microsystems з end-to-end контрольними сумами та copy-on-write як ключовими принципами; безпека метаданих була головним пріоритетом, а не доповненням.
- Copy-on-write означає, що оновлення метаданих — це записи: оновлення вказівника блоку або запису каталогу створює нові блоки, а не редагує на місці, що безпечніше, але може посилити IO при частих змінах метаданих.
- ARC спроєктовано для кешування і даних, і метаданих, і на практиці кешування метаданих часто дає найвищий ROI від RAM на ZFS-серверах.
- System attributes («SA») були введені, щоб зменшити IO для xattr шляхом упаковки атрибутів у бонус-буфери, що прискорює операції для навантажень з великою кількістю атрибутів.
- Feature flags дозволили ZFS еволюціонувати без розкарбування формату на диску: сучасні OpenZFS-системи узгоджують функції як
extensible_datasetта інші, змінюючи можливості метаданих із часом. - Спеціальні класи vdev з’явилися, щоб вирішити реальну біль: метадані на HDD були лімітером продуктивності навіть коли пропускна здатність даних була в порядку.
- ZAP (ZFS Attribute Processor) — це формат «словника» ZFS, який використовується для каталогів і властивостей; його можна мікрооптимізувати в кеші, і він стає «гарячою» точкою при навантаженнях, що інтенсивно працюють із каталогами.
- Налаштування recordsize часто неправильно розуміють: воно впливає на блоки даних файлу, а не на dnode, і саме по собі не вирішить повільний обхід каталогів.
- NVMe не просто зробив ZFS швидшим; він змінив режими відмов: латентність випадкових читань настільки покращилась, що видно CPU і блокування, які раніше ховалися за дисками.
Швидкий план діагностики
Коли система ZFS «відчувається повільною», треба вирішити, чи вона обмежена даними, метаданими або синхронними записами. Не переосмислюйте ситуацію. Робіть триаж у порядку.
1) По-перше: підтвердіть, що це затримка, і визначте читання чи запис
- Перевірте затримку пулу і черги за допомогою
zpool iostat -vі, якщо доступно, колонок латентності по vdev. - Шукайте шаблони дрібного IO: багато операцій, мала пропускна здатність, висока очікувана затримка.
- Запитайте: чи проблема проявляється в
ls -l,find, розпакуванні, git-операціях? Якщо так — це метадані, поки не доведено протилежне.
2) По-друге: перевірте стан ARC і тиск на метадані
- Чи стабільний розмір ARC і близький до цілі? Чи трясе він?
- Чи високі промахи по метаданих? Чи попереднє читання засмічує ARC?
- Чи достатньо RAM для кількості об’єктів і робочого набору?
3) По-третє: знайдіть, куди фізично потрапляють метадані
- Чи є special vdev? Чи він дзеркалений? Чи насичений?
- Чи метадані та дрібні блоки на HDD, а дані на SSD (або навпаки)?
- Чи фрагментований пул або високе заповнення, що підвищує накладні витрати на метадані?
4) По-четверте: ізолюйте «мікро-бенчмарк метаданих» від симптомів продакшена
- Заміряйте час обходу каталогу.
- Заміряйте цикл створення/видалення, що важкий для метаданих, у тестовому датасеті.
- Корелюйте з
zpool iostatта статистикою ARC під час виконання.
5) По-п’яте: перевірте шлях синхронних записів, якщо застосунок fsync-важкий
- Перевірте налаштування
sync, наявність SLOG і затримку записів. - Підтвердіть, що навантаження справді виконує синхронні записи (не робіть припущень).
Практичні завдання: команди, виводи, що це означає і рішення
Ось команди, які я запускаю, коли хтось каже «ZFS повільний», і я хочу припинити гадати. Виводи репрезентативні; ваші числа відрізнятимуться. Суть — у тому, до чого ви дійдете.
Завдання 1: Визначити конфігурацію пулу і наявність special vdev
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
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
special
mirror-1 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
errors: No known data errors
Що це означає: Є дзеркальний special vdev. Метадані (і, можливо, дрібні блоки) можуть потрапляти на NVMe, що добре. Якщо special vdev відсутній і у вас HDD, латентність метаданих — головний підозрюваний.
Рішення: Якщо метадані на повільних дисках, а навантаження — метаданеве, плануйте special vdev (дзеркальний) або переміщення навантаження на флеш.
Завдання 2: Заміряти латентність пулу та форму IO
cr0x@server:~$ zpool iostat -v tank 1 5
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 7.12T 3.88T 950 420 12.3M 8.1M
raidz2-0 7.12T 3.88T 120 80 12.0M 8.0M
sda - - 30 20 3.0M 2.0M
sdb - - 30 20 3.0M 2.0M
sdc - - 30 20 3.0M 2.0M
sdd - - 30 20 3.0M 2.0M
special 80G 720G 830 330 0.3M 0.1M
mirror-1 - - 415 165 0.3M 0.1M
nvme0n1 - - 210 85 0.1M 0.0M
nvme1n1 - - 205 80 0.1M 0.0M
Що це означає: Багато операцій і мала пропускна здатність на special vdev: це кричить «метадані». Великий vdev має скромну пропускну здатність; ваш біль, ймовірно, від дрібної латентності IO і ланцюжків залежностей.
Рішення: Зосередьтеся на показнику попадань ARC, насиченості special vdev і датасетах, що важкі для метаданих. Не витрачайте час на переслідування послідовної пропускної здатності.
Завдання 3: Перевірити властивості датасету, що впливають на поведінку метаданих
cr0x@server:~$ zfs get -o name,property,value -s local,received -r atime,recordsize,xattr,dnodesize,primarycache,secondarycache,sync tank/app
NAME PROPERTY VALUE
tank/app atime off
tank/app recordsize 128K
tank/app xattr sa
tank/app dnodesize auto
tank/app primarycache all
tank/app secondarycache all
tank/app sync standard
Що це означає: xattr=sa і dnodesize=auto можуть бути корисні для xattr-важких навантажень, але також можуть роздути метаданевий слід. primarycache=all дозволяє кешувати і дані, і метадані в ARC; іноді хочеться metadata для стримингових навантажень, щоб метадані не вилучалися.
Рішення: Якщо датасет використовується для дрібних файлів і метаданих-важких операцій, зберігайте кешування включеним і розгляньте, чи підходить primarycache=metadata для змішаних навантажень.
Завдання 4: Підтвердити розмір ARC і базову поведінку
cr0x@server:~$ sudo arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
21:10:01 480 22 4 10 45 12 55 6 27 62G 64G
21:10:02 510 28 5 14 50 14 50 8 29 62G 64G
21:10:03 495 30 6 18 60 12 40 10 33 62G 64G
Що це означає: ARC близький до цілі (arcsz близько до c). Рівень промахів помірний. Якщо ви бачите, що miss% зростає і тримається високим під час операцій з метаданими, ви йдете на диск за метаданими.
Рішення: Якщо ARC надто малий або забруднений даними, змініть стратегію кешування або додайте RAM. Якщо промахи переважно по метаданих (mm% висока), tuning special vdev та метаданеві налаштування стають пріоритетними.
Завдання 5: Підтвердити, чи читання реально потрапляють на диски або в кеш
cr0x@server:~$ sudo zpool iostat -r tank 1 3
read
pool ops/s bandwidth
------------ ----- ---------
tank 980 12.6M
raidz2-0 130 12.2M
special 850 0.4M
Що це означає: Велика кількість операцій читання на special vdev вказує, що метадані недостатньо в ARC. Якби кеш теплий, ви б очікували менше фізичних читань під час повторних обходів каталогів.
Рішення: Досліджуйте витіснення ARC, налаштування кешування та тиск пам’яті. Розгляньте фіксацію поведінки кешування по датасетах.
Завдання 6: Перевірити заповнення пулу і ризик фрагментації
cr0x@server:~$ zpool list -o name,size,alloc,free,cap,frag,health tank
NAME SIZE ALLOC FREE CAP FRAG HEALTH
tank 11T 7.12T 3.88T 64% 38% ONLINE
Що це означає: 64% зайнятості не лякає, але 38% фрагментації вказує, що алокації втрачають суміжність. Метаданевий churn може підвищувати це, роблячи майбутні операції дорожчими.
Рішення: Якщо капа >80% і frag висока, плануйте очищення місця, переписування або розширення пулу. Продуктивність метаданих часто різко падає при високій заповненості.
Завдання 7: Визначити датасети з «дрібними файлами» і порахувати об’єкти
cr0x@server:~$ zfs list -o name,used,refer,logicalused,compressratio,usedsnap -r tank
NAME USED REFER LUSED RATIO USEDSNAP
tank 7.12T 256K 7.10T 1.18x 1.4T
tank/app 2.20T 2.00T 2.30T 1.12x 600G
tank/ci-cache 1.10T 920G 1.30T 1.05x 480G
tank/backups 3.60T 3.50T 3.40T 1.24x 320G
Що це означає: Датасети з великим USEDSNAP і високою мінливістю (CI-кеші) часто викликають метаданеві проблеми: видалення стають дорогими, простір не повертається одразу, а операції з каталогами сповільнюються.
Рішення: Спрямуйте увагу на tank/ci-cache для перегляду політик снапшотів, змін кешування і, можливо, виділення власного пулу або special vdev.
Завдання 8: Спостерігати живу латентність і глибину черги по vdev
cr0x@server:~$ zpool iostat -v tank 1 3
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 7.12T 3.88T 1100 600 14.0M 10.2M
raidz2-0 7.12T 3.88T 150 120 13.7M 10.1M
special 80G 720G 950 480 0.3M 0.1M
Що це означає: Якщо операції special vdev підскакують під час обходів каталогів або штормів створення файлів, він виконує свою роботу — але також може стати вузьким місцем, якщо недорозмірений або пристрої споживчого класу задихаються на тривалому випадковому IO.
Рішення: Якщо special vdev насичений, оновіть його (швидші NVMe, більше дзеркал) або зменшіть IO метаданих (політика снапшотів, ізоляція навантаження).
Завдання 9: Перевірити, чи навантаження важке по sync-записах
cr0x@server:~$ zpool status tank
pool: tank
state: ONLINE
scan: scrub repaired 0B in 06:12:44 with 0 errors on Sun Dec 22 03:10:01 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
logs
nvme2n1 ONLINE 0 0 0
special
mirror-1 ONLINE 0 0 0
errors: No known data errors
Що це означає: Є SLOG-пристрій (logs). Це допомагає затримкам синхронних записів. Це нічого не зробить для читань метаданих. Люди часто плутають це щодня.
Рішення: Якщо користувачі скаржаться на затримку fsync, перевірте стан і латентність SLOG. Якщо скаржаться на find та ls, перестаньте дивитись тільки на SLOG.
Завдання 10: Шукати патологічну кількість снапшотів і політику зберігання
cr0x@server:~$ zfs list -t snapshot -o name,used,refer,creation -s creation | tail -n 5
tank/ci-cache@auto-2025-12-26-0900 0B 920G Fri Dec 26 09:00 2025
tank/ci-cache@auto-2025-12-26-1000 0B 920G Fri Dec 26 10:00 2025
tank/ci-cache@auto-2025-12-26-1100 0B 920G Fri Dec 26 11:00 2025
tank/ci-cache@auto-2025-12-26-1200 0B 920G Fri Dec 26 12:00 2025
tank/ci-cache@auto-2025-12-26-1300 0B 920G Fri Dec 26 13:00 2025
Що це означає: Часті снапшоти можуть бути нормою, але для churn-датасетів вони зберігають мертві блоки й підвищують метаданеву складність обходу. «0B used» на снапшоті все одно може приховувати велику метаданеву складність залежно від churn.
Рішення: Для CI-кешів і тимчасових датасетів: скоротіть збереження, зменшіть частоту або взагалі не знімайте снапшоти.
Завдання 11: Визначити, чи ARC засмічується потоковими читаннями
cr0x@server:~$ zfs get -o name,property,value primarycache -r tank/backups
NAME PROPERTY VALUE
tank/backups primarycache all
Що це означає: Датасети бекапів часто стрімлять дані. Якщо їм дозволено кешувати дані в ARC, вони можуть витіснити метадані, потрібні для чутливих до затримки навантажень.
Рішення: Розгляньте встановлення primarycache=metadata на стрімінгових датасетах, щоб ARC зберігав гарячі метадані без витрати RAM на великі дані, які ви не будете скоро перечитувати.
Завдання 12: Застосувати цілеспрямовану зміну кешування (обережно)
cr0x@server:~$ sudo zfs set primarycache=metadata tank/backups
Що це означає: ZFS припинить кешувати блоки даних файлів цього датасету в ARC, але все одно кешуватиме метадані. Це часто покращує затримку метаданих у решти пулу.
Рішення: Якщо датасети чутливі до затримки ділять пул зі стрімінговими навантаженнями, це один з найчистіших і найменш ризикових важелів.
Завдання 13: Перевірити xattr і dnodesize на найгарячішому датасеті
cr0x@server:~$ zfs get -o name,property,value xattr,dnodesize tank/ci-cache
NAME PROPERTY VALUE
tank/ci-cache xattr sa
tank/ci-cache dnodesize auto
Що це означає: Ця комбінація зазвичай коректна для xattr-важких навантажень (контейнери можуть бути такими). Але вона може роздмухати метаданевий слід. Якщо ваше навантаження не є xattr-важким, це може бути зайвим.
Рішення: Не відключайте ці налаштування необережно на існуючому датасеті, очікуючи миттєвого результату; вони впливають на новостворені об’єкти. Розгляньте A/B тестування на новому датасеті.
Завдання 14: Базовий «чисто метаданевий» мікро-тест з вимірюванням обходу каталогу
cr0x@server:~$ time find /tank/ci-cache/workdir -type f -maxdepth 3 -printf '.' >/dev/null
real 0m28.412s
user 0m0.812s
sys 0m6.441s
Що це означає: Високий системний час і довгий реальний час для простого обходу сильно вказують на метадані та очікування IO. Якщо ви повторите це і стане значно швидше — ARC допоміг; якщо ні — ARC надто малий або його витісняють.
Рішення: Повторіть негайно і порівняйте. Якщо другий запуск не набагато швидший, ви промахуєтесь в ARC і сильно вантажите диски/special vdev.
Три корпоративні міні-історії (анонімізовані, правдоподібні, болісно знайомі)
1) Інцидент через неправильне припущення: «У нас NVMe, тож метадані не можуть бути проблемою»
Середня SaaS-компанія запустила білд-фарм на ZFS. У них були сучасні NVMe для «пулу», тож інфраструктурна команда припустила, що латентність зберігання майже вирішена. Система білдів все одно відчувала спорадичні затримки 30–90 секунд у пікові години. Інженери звинувачували оркестратор CI і списували це як «Kubernetes як він є».
On-call врешті корелював затримки з задачами, що розпаковували великі дерева залежностей: десятки тисяч дрібних файлів. Під час цих вікон середня пропускна здатність пулу була низькою, а NVMe далеко не насичені. Усі погоджувалися з дашбордами і робили висновок: не сховище.
Помилка: вони дивилися лише на пропускну здатність і агреговану завантаженість. Не дивилися на розподіл розмірів IO, рейтинг попадань у кеш або на те, що операції з метаданими серіалізовані і домінуються латентністю. ARC був малим відносно робочого набору об’єктів, а паралельний процес бекапу стрімив дані через ARC, витісняючи метадані.
Виправлення було нудним: виставили primarycache=metadata на бекап-датасетах, додали RAM і перемістили метадані CI-датасету на коректно дзеркалений special vdev. «Таємничі» затримки розвіялися. Оркестратор не змінювався. Змінилися припущення.
2) Оптимізація, що призвела до відкату: «Давайте накрутимо dnodesize, щоб xattr були швидшими»
Велике підприємство керувало сервісом з вимогами відповідності. Вони зберігали багато тегів і аудиту як xattr. Хтось прочитав, що більші dnode допомагають тримати атрибути inline і зменшують IO. Правда. Небезпечна половинна правда.
Вони застосували шаблон датасету з dnodesize=auto широко по багатьом датасетам, включно з тими, що цього не потребували: staging логів, артефакти збірки, тимчасовий scratch. З часом ті датасети накопичили об’єкти з більшими dnode. Ефективність ARC погіршилась. Промахи метаданих зросли. Операції з каталогами сповільнились, потім латентність клієнтів зросла, потім обсяг звернень у службу підтримки різко піднявся.
Відкат не був простим перемиканням, бо існуючі об’єкти зберігали свою dnode-структуру. Довелося створювати нові датасети з адекватними налаштуваннями, мігрувати дані і ставитись до старих як до радіоактивних, поки їх не виведуть з експлуатації. Урок — не «ніколи не використовувати більші dnode». Урок — застосовуйте це там, де виміряли ефект, а не де це модно.
3) Нудна, але правильна практика, що врятувала ситуацію: поводитися зі special vdev як із критичним продакшн-сховищем
Фінансова організація використовувала OpenZFS зі special vdev, щоб тримати метадані на флеші. Вони зробили це правильно: дзеркальні enterprise NVMe, строгий моніторинг і правило, що місткість special vdev ніколи не має «випадково» заповнюватись.
Під час проблем з прошивкою вендора, що спричиняла інциденти тайм-аутів NVMe під тривалим випадковим навантаженням, special vdev почав кидати помилки. Моніторинг зреагував рано, бо вони відстежували лічильники помилок і латентність по класу special окремо, а не лише здоров’я пулу.
Вони плавно деградували пул: сповільнили непотрібні задачі, відклали scrub-и і мігрували найгарячіші метаданеві навантаження з системи, перш ніж ситуація загострилась. Запасні диски були під рукою, і у них уже була протестована процедура заміни членів special vdev без паніки.
Більшість команд виявляють, що «метадані — особливі», коли пул не імпортується. Ця команда виявила це у вікні змін у вівторок, і всі змогли пообідати. Надійність часто виглядає як параноя з кращою документацією.
Поширені помилки: симптом → корінна причина → виправлення
1) ls і find повільні, але читання великих файлів швидкі
Симптом: Стрімінгове читання/запис дають очікувану пропускну здатність; обходи каталогів і інструменти, що інтенсивно роблять stat, повзають.
Корінна причина: Промахи метаданих в ARC; метадані на повільних vdev; недостатньо IOPS для дрібних випадкових читань.
Виправлення: Збільшити ефективність ARC (додати RAM, primarycache=metadata на стрімінгових датасетах), розгорнути дзеркальний special vdev, зменшити churn снапшотів.
2) «Ми додали SLOG і нічого не покращилось»
Симптом: Затримка застосунку не змінилася після встановлення швидкого журналу.
Корінна причина: Вузьке місце — читання метаданих або асинхронні операції; SLOG допомагає тільки синхронним записам.
Виправлення: Підтвердіть рівень синхронних записів; збережіть SLOG, якщо потрібен, але вирішіть шлях метаданих (ARC, special vdev, кількість об’єктів, снапшоти).
3) Піки латентності під час scrub/resilver
Симптом: Користувацький IO сповільнюється під час обслуговування.
Корінна причина: Витіснення ARC scrub-читаннями; контенція vdev; читання метаданих перекладаються на повільні носії; special vdev насичений.
Виправлення: Плануйте scrubs поза піковими навантаженнями, тротлінгуйте де підтримується, переконайтесь, що special vdev не недорозмірений, і уникайте важких сканів у пікові години.
4) Видалення файлів повільне і простір не повертається
Симптом: rm -rf йде вічно; пул залишається заповненим.
Корінна причина: Снапшоти зберігають блоки; оновлення метаданих масштабні; звільнення відкладається по TXG.
Виправлення: Зменшіть політику збереження снапшотів на churn-датасетах; розгляньте періодичну ротацію датасетів (новий датасет + міграція) для scratch.
5) «Ми збільшили recordsize, але все одно повільно»
Симптом: Налаштування recordsize не покращило операції, що важкі для метаданих.
Корінна причина: Recordsize впливає на блоки даних файлів, а не на операції з каталогами чи кеш dnode.
Виправлення: Зосередьтесь на метаданих: ARC, special vdev, стратегія xattr/dnodesize, політика снапшотів і ізоляція навантажень.
6) Special vdev раптово заповнився
Симптом: Розподіл special vdev зріс поки він не майже повний.
Корінна причина: Дрібні блоки виділяються на special через налаштування special_small_blocks, або зростання метаданих внаслідок вибуху об’єктів.
Виправлення: Перегляньте поріг special_small_blocks, розширте ємність special vdev (додайте ще одне дзеркало) і контролюйте зростання об’єктів (політики очищення).
Жарт №2: special vdev «особливий» так само, як єдиний Single Point of Failure — він привертає увагу всіх о 3-й ранку.
Контрольні списки / покроковий план
Крок 1: Класифікуйте навантаження (не вгадуйте)
- Запустіть
zpool iostat -vпід час уповільнення. - Заміряйте операцію, що важка для метаданих (обхід каталогу), і операцію, що важка для даних (послідовне читання).
- Якщо ops високі, а пропускна здатність низька — вважайте це метаданями/дрібними IO.
Крок 2: Зробіть так, щоб ARC працював на вас
- Перевірте розмір ARC і рівень промахів (користуйтесь
arcstat). - Якщо стрімінгові навантаження ділять пул, виставте
primarycache=metadataна цих датасетах. - Якщо ARC просто занадто малий для робочого набору, додайте RAM перед тим, як купувати більше дисків. Так, серйозно.
Крок 3: Помістіть метадані на правильні носії
- Якщо у вас HDD-пули, що обслуговують метаданево-важкі навантаження, плануйте дзеркальний special vdev на enterprise NVMe.
- Розмірюйте його з запасом. Метадані ростуть з об’єктами та снапшотами, а не лише з «використаних байтів».
- Моніторьте його окремо: латентність, помилки і алокацію.
Крок 4: Контролюйте churn і снапшоти як дорослий
- Визначте датасети з високим churn (CI-кеші, тимчасове staging, шари контейнерів).
- Зменшіть частоту та збереження снапшотів для цих датасетів.
- Розгляньте ротацію датасетів: створіть новий, переключіть навантаження, знищіть старий, коли це безпечно. Це уникає патологічних довгоживучих метаданих.
Крок 5: Тестуйте зміни реалістичним мікро-бенчмарком
- Використовуйтесь репрезентативним деревом каталогів.
- Вимірюйте холодний і теплий кеш.
- Валідуйте з
zpool iostatі статистикою ARC під час тестування, а не після.
Крок 6: Операційна гігієна, що зменшує «таємничі» інциденти
- Тримайте заповненість пулу під контролем (уникайте життя вище ~80% якщо вам не подобаються сюрпризи).
- Тримайте розклади scrub-перевірок передбачуваними.
- Документуйте налаштування датасетів:
xattr,dnodesize,primarycache, політики снапшотів.
Цитата, яка пасує у ваш runbook
Парафраз ідеї, приписано John Allspaw: «У складних системах успіх та провал походять з одного джерела: нормальна робота й нормальні рішення.»
Часті запитання
1) Що таке dnode в операційному розумінні?
Dnode — це метаданевий запис для об’єкта ZFS (файл, каталог, об’єкт датасету тощо). Якщо ваше навантаження багато виконує stat(), пошуків, створень, видалень або працює з xattr, доступ до dnode буде на гарячому шляху.
2) Чи завжди метадані потрапляють на special vdev, якщо він є?
Зазвичай метадані виділяються в special-клас, коли він присутній, але поведінка може варіюватися в залежності від фіч та налаштувань. Також дрібні блоки можуть йти туди, якщо встановлено special_small_blocks. Припускайте, що special vdev критичний і моніторьте його відповідно.
3) Чи завжди варто вмикати special_small_blocks?
Ні. Це потужний інструмент, але може заповнити ваш special vdev і перетворити прискорювач метаданих на проблему ємності. Використовуйте його, коли навантаження домінують дрібні блоки і у вас достатньо дзеркальної флеші з запасом.
4) Чи допоможе збільшення recordsize метаданим?
Не дуже. recordsize стосується блоків даних файлу. Операції над метаданими домінуються dnode, ZAP, непрямими блоками та іншими структурами метаданих.
5) Чи виправить SLOG повільний обхід каталогів?
Ні. SLOG допомагає затримкам синхронних записів. Повільні обходи каталогів зазвичай пов’язані з читаннями метаданих і поведінкою кеша. Залишайте SLOG, якщо навантаження його потребує, але не очікуйте, що він вирішить проблеми з читаннями метаданих.
6) Чому другий запуск find іноді набагато швидший?
Тому що ARC розігрівся. Перший запуск підвантажив метадані в кеш. Якщо другий запуск не швидший — робочий набір не вміщується в ARC або щось інше витісняє його.
7) Як снапшоти впливають на продуктивність метаданих?
Снапшоти зберігають старі вказівники блоків, що збільшує обсяг метаданих, які залишаються доступними. Для churn-датасетів це може підсилити вартість видалень і підвищити фрагментацію, роблячи операції з метаданими повільнішими з часом.
8) Який найбезпечніший «швидкий виграш», коли метадані — вузьке місце?
Зазвичай: виставити primarycache=metadata на стрімінгові датасети, що забруднюють ARC, і зменшити збереження снапшотів на churn-датасетах. Ці дві зміни можуть підвищити показники попадань метаданих без зміни формату на диску.
9) Чи варто змінювати dnodesize на існуючому датасеті?
Будьте обережні. Це впливає на нові об’єкти, а існуючі об’єкти не «зменшаться» автоматично. Якщо потрібен чистий стан, створіть новий датасет з потрібними властивостями і мігруйте дані.
10) Якщо на папері у мене багато IOPS, чому операції з метаданими все одно зависають?
Тому що метадані часто вимагають послідовних залежних читань. Ви не можете розпаралелити ланцюжок «прочитати вказівник, щоб знайти наступний вказівник» так само, як можна розпаралелити великі читання. Тут домінує латентність.
Висновок: наступні кроки, що дійсно дають результат
Якщо ви візьмете одну оперативну настанову з поведінки метаданих ZFS — візьміть цю: перестаньте трактувати «продуктивність зберігання» лише як проблему пропускної здатності. Для багатьох реальних навантажень це проблема кеша і латентності, що ховається під прикриттям.
Зробіть наступне:
- Запустіть швидкий план діагностики під час наступного інциденту і правильно класифікуйте вузьке місце.
- Захистіть ARC від стрімінгових навантажень, виставивши
primarycache=metadataна рівні датасетів там, де це доцільно. - Якщо у вас метаданеві навантаження на HDD, припиніть сперечатись з фізикою: використайте дзеркальний special vdev на enterprise флеші, розмірений з запасом.
- Аудитуйте політики снапшотів на churn-датасетах і припиніть знімати снапшоти для scratch як для безцінної історії.
- Документуйте налаштування датасетів і забезпечте їхнє дотримання. Більшість метаданевих катастроф починаються з «однієї зміни властивості».
ZFS робитиме саме те, чого ви його просите, і робитиме це довго після того, як ви забудете, що просили. Зробіть шлях метаданих нудним, передбачуваним і швидким. Ваше майбутнє «я» отримає менше пейджерів і кращі вихідні.