Ви на виклику. База даних повільна. Хтось викладає скриншот: «ARC hit ratio впала до 72%!» і дюжина людей вирішує, що це — Проблема.
Майже чутно, як складається запит на бюджет на більше RAM.
Іноді вони праві. Часто — ні. Рівні попадань кешу ZFS схожі на прогноз погоди: корисні для планування, марні для пошуку винних.
Це поле-путівник, який допоможе розуміти різницю — без перетворення зберігання на науковий проєкт.
Що насправді вимірюють рівні попадань кешу ZFS
ZFS має первинний кеш у пам’яті, названий ARC (Adaptive Replacement Cache). Також є опційний вторинний кеш на швидких пристроях — L2ARC.
Обидва кеші відслідковують «попадання» і «промахи», а інструменти з радістю рахує відношення попадань, яке виглядає як KPI.
Ось у чому підступ: «попадання» — це не те саме, що «швидко», а «промах» — не те саме, що «повільно». Рівень попадань говорить лише про те,
звідки було обслуговано дані, а не про те, чи система відповідала цілям затримки, чи навантаження здорове, і чи запул насичений.
ARC — це не одна гроша
ARC містить як дані (блоки файлів), так і метадані (dnode, непрямі блоки, записи каталогів тощо). Багато навантажень
залежать від кешування метаданих. Наприклад, файлова система з мільйонами дрібних файлів може відчувати себе «швидкою» в першу чергу тому, що метадані залишаються гарячими,
навіть якщо блоґи даних холодні.
ARC також тримає внутрішні «списки» (MRU/MFU та їх «ghost»-варіанти) для балансування недавності й частоти. Це важливо, бо попадання може означати
«ми бачили це нещодавно» або «ми бачимо це постійно», а ці випадки вимагають різного налаштування.
L2ARC — не магічне розширення RAM
L2ARC зберігає блоки на SSD/NVMe для повторного використання. Звучить як «RAM, але дешевше». Насправді це «SSD-кеш із накладними витратами на облік».
L2ARC потребує ARC для індексації. Якщо вам бракує RAM, додавання L2ARC парадоксально може зробити вас ще більш голодними до пам’яті.
Попадання рахуються на запит, а не на бізнес-результат
ZFS може задовольнити запит з ARC, L2ARC або диска. Також може виконувати read-ahead (prefetch), стиснення, дедуплікацію (якщо ви ввімкнули),
і об’єднувати I/O. Ваш додаток відчуває кінцеву затримку, яка формується CPU, блокуваннями, чергами I/O, txg commit-ами та поведінкою sync.
Рівень попадань — це лише одна лінза, а не абсолютна істина.
Практичне правило: продуктивність — це затримка під навантаженням, а не відсотки кешу.
Цитата, яку варто тримати на стикері: «Надія — не стратегія.»
— General Gordon R. Sullivan.
Відношення попадань кешу — це місце, куди надія приходить, щоб виглядати як математика.
Коли рівні попадань важливі (і передбачувальні)
1) Навантаження, орієнтовані на читання, зі стабільним робочим набором
Якщо ваше навантаження багаторазово читає одні й ті самі блоки — VM boot storms, повторне використання артефактів CI, роздача веб-ресурсів, аналітика з повторними сканами «гарячого» підмножини — то ARC hit ratio сильно корелює з тиском на диски й затримками читання.
У цьому випадку покращення hit rate (більше RAM, краще узгодження recordsize, зменшення churn) часто дає прямий приріст продуктивності.
Можна логічно думати: менше читань з диска → менша глибина черги → краща затримка.
2) Навантаження, де домінують метадані
Переліки директорій з мільйонами записів. Глибокі деревоподібні обходи. Створення шарів контейнерів. Операції git на великих монорепозиторіях.
Такі навантаження виграють від попадань метаданих в ARC, навіть якщо попадання даних залишаються посередніми.
Тут корисніша метрика — не «загальний hit rate», а «чи викликають промахи метаданих синхронні випадкові читання?». Якщо так — налаштування ARC і розміщення метаданих (special vdev) можуть змінити ситуацію.
3) Пули, де вузьке місце — диски
Якщо ваш пул на ротаційних дисках (HDD) або ваші SSD вже сильно завантажені, промахи ARC шкодять, бо час обслуговування диска — ваш обмежувач.
Рівні попадань важливі, коли альтернатива — повільно.
4) Ви оцінюєте інвестицію в L2ARC або special vdev
Самі по собі рівні попадань недостатні, але вони — частина моделі витрат:
якщо потік промахів ARC — переважно випадкові читання і робочий набір трохи перевищує RAM, L2ARC може допомогти.
Якщо ж промахи — послідовні читання, бекапи або стріми, L2ARC здебільшого — дорогою обігрівач.
Жарт №1: L2ARC — це як стажер із навантажувачем — іноді геніально, іноді дорого, завжди потребує нагляду.
5) Ви діагностуєте регресію і показники попадань змінились разом із нею
Якщо оновлення, зміна конфігурації або властивостей dataset збігається з різким зниженням рівня попадань, це може бути сильною підказкою. Це не доказ.
Це сигнал «ідіть за димом».
Коли рівні попадань неважливі (і вводять в оману)
1) Навантаження, орієнтовані на запис
ARC не є кешем запису в тому значенні, яке часто мають на увазі люди в нарадах. Так, ZFS використовує пам’ять для dirty data і transaction groups, і використовує кеш для читань, які часто йдуть після записів. Але якщо ваша проблема — затримка записів або fsync-шторми, рівні попадань — то другорядне.
Для записів важливіше: sync vs async, SLOG (якщо є), латентність пулу і поведінка txg. Красивий ARC hit rate не врятує вас від насиченого пулу, що робить малі синхронні записи.
2) Стримінгові читання і одноразові скани
Бекапи, копіювання великих файлів, медіа-пайплайни, повторна обробка логів, холодні аналітичні скани: вони багато читають один раз і йдуть далі.
Низький hit rate тут очікуваний і нормальний. Prefetch може створити ілюзію, що кеш «працює» (бо тимчасово підвантажує блоки в ARC),
але це не змінює фізики: ви обмежені пропускною здатністю.
3) Коли вузьке місце — CPU або блокування
Декомпресія, контрольні суми, шифрування, dedup таблиці, патологічний метаданний churn або однопотоковий додаток можуть упиратися в CPU.
У такому випадку ARC hit rate може бути чудовим, але система все ще відстає, бо час витрачається вище за шар I/O.
4) Коли ваш пул уже достатньо швидкий
Якщо ви на сучасних NVMe і ваші цілі затримки досягаються, промахи допустимі. «Більше попадань» стає метрикою марнославства. Ви не отримаєте нагороду за 99% ARC hits, якщо сервіс уже швидкий і стабільний.
5) Коли рівень попадань завищений через неправильні речі
ARC може виглядати «чудово», бо кешує дані, які вам не важливі: послідовні prefetch, тимчасові блоки або розігріті дані від одного бенчмарка.
Тим часом ваше реальне навантаження промахується по метаданих і страждає.
Якщо треба запам’ятати одне правило: не налаштовуйтеся на основі одного агрегованого відношення попадань. Розбийте по: метадані vs дані, попит vs prefetch,
затримка vs пропускна здатність, sync vs async. Інакше ви просто налаштовуєте свої відчуття.
ARC vs L2ARC: практичні відмінності
ARC: найшвидший, найпростіший і все ще легкий для зіпсування
ARC живе в RAM. Він надзвичайно швидкий і може обслуговувати читання без звернення до дисків. Але ARC ділить RAM з усім іншим:
додатками, page cache (на деяких платформах), структурами ядра та метаданими ZFS.
Якщо ARC замалий відносно вашого робочого набору, відбувається thrashing: багато промахів, часті витіснення і стрибок читань з диска.
Якщо ARC занадто великий — ви голодуєте додатки та ОС. Це може виглядати як «зберігання повільне», коли насправді тиск на звільнення пам’яті.
L2ARC: іноді корисний, іноді — податок
L2ARC може бути вигідним, коли:
- Ваше навантаження орієнтоване на читання і повторно використовує блоки.
- Робочий набір більший за RAM, але не надто великий.
- Ваш пул повільніший за пристрій кешу (HDD-пул або завантажений SSD-пул).
- У вас достатньо RAM, щоб тримати заголовки L2ARC і не тонути.
L2ARC може бути програшем, коли:
- Ваше навантаження — в основному стримінгові читання (великий churn кешу).
- Пристрій кешу не тримає стабільної малої латентності під навантаженням запису.
- Ви вже обмежені по пам’яті.
- Ви очікуєте, що він прискорить синхронні записи (це не так).
Чому рівні попадань відрізняються між ARC і L2ARC
ARC hit rate вимірює запити, обслуговані з RAM. L2ARC hit rate вимірює запити, обслуговані з пристрою кешу.
«Гарний» L2ARC hit rate залежить від вашої мети:
іноді навіть 10–20% L2ARC hits можуть мати значення, якщо ці попадання замінюють дорогі випадкові читання з HDD.
Також: L2ARC наповнюється асинхронно і історично не зберігався між перезавантаженнями. На багатьох сучасних OpenZFS-системах існує persistent L2ARC,
але операційна реальність все ще важлива: час розігріву, поведінка витіснення і знос пристрою.
Цікаві факти та історичний контекст
- ARC був загальною особливістю дизайну ZFS від Sun: він замінив традиційний розподіл «buffer cache vs page cache» одним адаптивним кешем.
- Ідея «ghost lists» в ARC (запам’ятовування нещодавно витіснених блоків) прийшла з академічних досліджень по кешуванню і допомагає уникнути thrashing.
- L2ARC з’явився пізніше, коли флеш став життєздатним; ранні SSD були швидкими, але крихкими, що сформувало консервативну поведінку при записах кешу.
- Історично L2ARC не був персистентним між перезавантаженнями: кеши стартували холодними, що впливало на «ранкові шторми входу» у понеділок.
- Статистика ARC стала культурою: адміністратори порівнювали hit rates так само, як геймери FPS, навіть коли графіки затримок були реальною метою.
- Special vdev (метадані/дрібні блоки на швидких пристроях) змінили розмову про кешування, зробивши промахи менш болісними.
- Стиснення змінило математику кешу: ARC зберігає стиснуті блоки в багатьох конфігураціях, ефективно збільшуючи ємність кешу на GiB RAM.
- Поведінка prefetch еволюціонувала: те, що колись виглядало як «забруднення кешу», в одній версії могло стати розумнішим readahead в іншій.
- Репутація dedup як поглинача пам’яті заслужена: dedup table може домінувати в потребі пам’яті і спотворювати все, що ви думаєте про ARC.
Швидкий план діагностики
Мета — не милуватися метриками. Мета — ідентифікувати вузьке місце за 15 хвилин, вибрати наступний тест і уникнути ритуального налаштування.
Перше: це IOPS/затримка чи пропускна здатність?
- Якщо користувачі скаржаться на «різкі затримки» і таймаути: підозрюйте затримки/IOPS.
- Якщо передачі просто повільні в плані end-to-end: підозрюйте обмеження пропускної здатності, CPU або мережу.
Друге: це читання чи запис, синхронні чи асинхронні?
- Високі read IOPS + висока disk await: кеш може мати значення.
- Високі write IOPS з sync: SLOG/латентність пулу важливіша за ARC hit rate.
- Високі записи з великими блоками: дивіться на throughput vdev і фрагментацію.
Третє: чи ARC під тиском або поводиться нормально?
- Розмір ARC закритий і багато витіснень: можливо, ви thrash-ите.
- Розмір ARC стабільний, промахи стабільні, але затримка все ще погана: вузьке місце — в іншому.
Четверте: чи пул насичений або нездоровий?
- Перевірте
zpool iostat -vна предмет дисбалансу vdev, високого чергування і повільних пристроїв. - Перевірте помилки, resilvering, scrub або помираючий диск, який тягне vdev вниз.
П’яте: підтвердіть одним цілеспрямованим експериментом
- Очистити кеші? Зазвичай погана ідея в продакшені, але можна провести контрольований тест читання на некритичному dataset.
- Змінити одну властивість (наприклад,
primarycache=metadataна backup dataset) і дивитися поведінку ARC. - Використайте
fioабо метрики додатка, щоб підтвердити покращення.
Практичні завдання: команди, виводи, рішення
Це робочі перевірки, які я очікую від SRE перед тим, як пропонувати залізо, і перед тим, як торкатися нюансів налаштувань з великим страхом.
Кожне завдання включає: команду, приклад виводу, що це означає і яке рішення ви приймаєте.
Завдання 1: Підтвердити стан пулу і наявність поточних робіт обслуговування
cr0x@server:~$ sudo zpool status
pool: tank
state: ONLINE
scan: scrub repaired 0B in 02:14:33 with 0 errors on Tue Dec 24 03:10:11 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
errors: No known data errors
Значення: Якщо у вас відбувається resilver, scrub або пул дегрейдований, продуктивність може впасти незалежно від рівня попадань кешу.
Рішення: Якщо scan виконується в піковий час — перенесіть. Якщо пул дегрейдований — спочатку виправте апарат; не «налаштовуйте ARC», щоб компенсувати.
Завдання 2: Визначити читання vs запис і латентність на рівні пулу
cr0x@server:~$ sudo zpool iostat -v tank 1 5
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
tank 4.22T 7.58T 1.20K 3.40K 140M 220M
raidz2-0 4.22T 7.58T 1.20K 3.40K 140M 220M
sda - - 280 860 35.0M 56.0M
sdb - - 300 820 34.5M 55.0M
sdc - - 310 870 36.0M 56.5M
sdd - - 320 850 34.5M 55.0M
-------------------------- ----- ----- ----- ----- ----- -----
Значення: Це показує розподіл по vdev і дисках. Великий дисбаланс підказує про повільний диск або чергування.
Рішення: Якщо один диск відстає — дослідіть його. Якщо весь vdev насичений, кеш може допомогти читанням — але лише якщо промахи випадкові та повторно використовувані.
Завдання 3: Перевірити властивості dataset, що безпосередньо змінюють поведінку кешування
cr0x@server:~$ zfs get -o name,property,value,source recordsize,primarycache,secondarycache,compression,sync tank/data
NAME PROPERTY VALUE SOURCE
tank/data recordsize 128K local
tank/data primarycache all default
tank/data secondarycache all default
tank/data compression lz4 local
tank/data sync standard default
Значення: primarycache/secondarycache вирішують, що може потрапити в ARC/L2ARC. recordsize впливає на форму I/O і ефективність кешу.
Рішення: Для backup/streaming dataset розгляньте primarycache=metadata, щоб запобігти забрудненню кешу. Для БД оцініть recordsize і sync окремо.
Завдання 4: Переглянути розмір ARC і цільовий розмір
cr0x@server:~$ arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
12:01:01 22K 1.8K 8 980 4 740 3 120 1 64G 64G
12:01:02 21K 2.0K 9 1.1K 5 760 3 140 1 64G 64G
12:01:03 20K 1.9K 9 1.0K 5 780 4 120 1 64G 64G
Значення: arcsz — поточний розмір ARC; c — цільовий. Miss% — це промахи попиту. Prefetch-промахи йдуть окремо.
Рішення: Якщо arcsz зафіксований на c і промахи високі з ростом latency диска — можливо, ви недокешовані. Якщо промахи низькі — кеш не є вашим вузьким місцем.
Завдання 5: Розділити попит і prefetch, щоб виявити забруднення кешу
cr0x@server:~$ arcstat -f time,read,miss,miss%,pmis,pm%,arcsz,c 1 3
time read miss miss% pmis pm% arcsz c
12:02:10 18K 2.2K 12 1.9K 10 64G 64G
12:02:11 19K 2.1K 11 1.8K 10 64G 64G
12:02:12 18K 2.3K 12 2.0K 11 64G 64G
Значення: Високі prefetch-промахи (pm%) під час стрімінгових читань — нормальні; високі prefetch-попадання можуть витісняти корисні дані.
Рішення: Якщо бекап створює величезну prefetch-активність і інтерективна затримка стрибає — ізолюйте його (окремий dataset, обмеження IO, властивості кешу).
Завдання 6: Перевірити наявність L2ARC і його ефективність
cr0x@server:~$ arcstat -f time,l2read,l2miss,l2hit%,l2asize,l2size 1 3
time l2read l2miss l2hit% l2asize l2size
12:03:01 6K 4K 33 420G 800G
12:03:02 7K 5K 29 421G 800G
12:03:03 6K 4K 33 421G 800G
Значення: L2ARC hit% може виглядати «ніпро що» і все ще бути цінним, якщо він рятує повільні читання з диска. Також стежте за тим, наскільки він реально заповнений (l2asize).
Рішення: Якщо L2ARC майже не використовується або hit% низький і ваші промахи — стримінгові, видаліть його або перерозподіліть пристрій. Якщо він допомагає випадковим читанням — лишіть.
Завдання 7: Перевірити тиск пам’яті перед тим, як звинувачувати розмір ARC
cr0x@server:~$ free -h
total used free shared buff/cache available
Mem: 256Gi 210Gi 3.1Gi 2.0Gi 43Gi 20Gi
Swap: 16Gi 9.5Gi 6.5Gi
Значення: Низький «available» і активне використання swap означають, що у вас тиск пам’яті. ARC може конкурувати з додатками.
Рішення: Якщо є свопінг — усуньте тиск пам’яті спочатку (зменшіть навантаження, додайте RAM, обмежте ARC), аніж ганятися за hit rate.
Завдання 8: Виміряти латентність дисків безпосередньо в Linux
cr0x@server:~$ iostat -x 1 3
Device r/s w/s r_await w_await aqu-sz %util
sda 280.0 860.0 18.2 12.4 9.10 98.0
sdb 300.0 820.0 17.9 12.1 8.80 97.5
sdc 310.0 870.0 18.5 12.7 9.30 98.3
sdd 320.0 850.0 18.0 12.2 9.00 97.9
Значення: Високі await і %util вказують на насичення пристрою / чергування. Попадання кешу не виправлять насичені записи.
Рішення: Якщо латентність висока через записи, дослідіть поведінку sync, SLOG і write amplification (recordsize, дрібні блоки, фрагментація).
Завдання 9: Перевірити, чи домінують синхронні записи
cr0x@server:~$ zfs get -o name,property,value,source sync tank/db
NAME PROPERTY VALUE SOURCE
tank/db sync standard default
Значення: sync=standard означає, що рішення приймає додаток. Бази даних часто роблять багато синхронних записів.
Рішення: Якщо затримка корелює з fsync, потрібен правильний SLOG (швидкий, з захистом від відключення живлення) або налаштування додатка — а не підгонка ARC hit rate.
Завдання 10: Підтвердити, чи існує SLOG і де він
cr0x@server:~$ sudo zpool status tank | sed -n '1,80p'
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
logs
nvme0n1 ONLINE 0 0 0
Значення: log vdev — це ваш SLOG. Його латентність і характеристики надійності важливі для синхронних записів.
Рішення: Якщо у вас немає SLOG і синхронні записи вбивають вас — розгляньте його. Якщо у вас дешевий споживчий SSD як SLOG — замініть його, поки не зіпсував вам вихідні дні.
Завдання 11: Виявити тиск витіснення ARC (Linux /proc)
cr0x@server:~$ egrep 'c_max|c_min|size|memory_throttle_count|deleted|evict' /proc/spl/kstat/zfs/arcstats
c_max 4 68719476736
c_min 4 17179869184
size 4 68719476736
memory_throttle_count 4 0
deleted 4 119283
evict_skip 4 0
Значення: size на c_max не обов’язково погано. Зростання memory_throttle_count говорить про те, що ARC змушують звужуватися або він обмежується.
Рішення: Якщо throttling зростає під час навантаження — шукайте конкуренцію за пам’ять і розгляньте обмеження ARC або переміщення робочих навантажень.
Завдання 12: Зрозуміти метадані vs дані в ARC
cr0x@server:~$ egrep 'demand_data_hits|demand_data_misses|demand_metadata_hits|demand_metadata_misses' /proc/spl/kstat/zfs/arcstats
demand_data_hits 4 99887766
demand_data_misses 4 5544332
demand_metadata_hits 4 22334455
demand_metadata_misses 4 112233
Значення: Багато промахів по даних може бути нормальним для стрімінгу. Промахи метаданих більш тривожні для робочих навантажень «поведінка інтерфейсу».
Рішення: Якщо промахи метаданих ростуть разом зі стрибками затримки — розгляньте special vdev для метаданих або збільшення ARC (якщо пам’ять дозволяє).
Завдання 13: Перевірити, чи існує «special» vdev для метаданих/дрібних блоків
cr0x@server:~$ sudo zpool status tank | sed -n '1,140p'
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
nvme1n1 ONLINE 0 0 0
nvme2n1 ONLINE 0 0 0
Значення: Special vdev може зняти навантаження з метаданих і за потреби дрібних блоків на швидкі пристрої, зменшуючи біль від промахів кешу.
Рішення: Якщо промахи метаданих болісні і у вас немає special vdev — оцініть його. Якщо він є — перевірте ці пристрої на знос/латентність.
Завдання 14: Виявити невідповідності політик кешування на рівні dataset
cr0x@server:~$ zfs get -r -o name,property,value primarycache tank | head
NAME PROPERTY VALUE
tank primarycache all
tank/backups primarycache all
tank/backups/daily primarycache all
tank/db primarycache all
tank/home primarycache all
Значення: Датасети для бекапів за замовчуванням кешують усе, що може витісняти гарячі дані без користі.
Рішення: Встановіть бекап-датасети primarycache=metadata (або навіть none у крайніх випадках) і залиште інтерактивні датасети без змін.
Завдання 15: Підтвердити, що recordsize відповідає реальному навантаженню
cr0x@server:~$ zfs get -o name,property,value recordsize tank/db tank/vm tank/backups
NAME PROPERTY VALUE
tank/db recordsize 16K
tank/vm recordsize 64K
tank/backups recordsize 1M
Значення: Дрібні випадкові I/O БД віддають перевагу меншим recordsize; бекапи — великим. Неправильний recordsize може витрачати ARC і викликати read-modify-write amplification.
Рішення: Якщо hit rate низький, бо ви кешуєте гігантські записи для дрібних читань — відрегулюйте recordsize на dataset (обережно, для нових записів).
Завдання 16: Швидка перевірка «це додаток?» з I/O на процеси
cr0x@server:~$ sudo pidstat -d 1 3
Linux 6.8.0 (server) 12/25/2025 _x86_64_ (32 CPU)
12:05:10 PM UID PID kB_rd/s kB_wr/s kB_ccwr/s Command
12:05:11 PM 1001 24182 1200.00 9800.00 0.00 postgres
12:05:11 PM 0 19876 0.00 64000.00 0.00 zfs
Значення: Підтверджує, хто генерує I/O. Якщо пакетна робота лупить записи — рівні попадань кешу лиш спостерігачі.
Рішення: Якщо шумний сусід відповідальний — обмежте/перерозподіліть/перемістіть роботу, перш ніж чіпати ZFS-налаштування.
Три корпоративні міні-історії з практики
Міні-історія 1: Інцидент через неправильне припущення
Середня компанія керувала мультиорендною аналітичною платформою. Під час тижня запуску дашборди сповільнилися і деякі запити таймаутили.
Команда зберігання побачила, що ARC hit ratio впав із середини 90% до низу 70%. Висновок сформувався миттєво: «Потрібно більше RAM».
Вони екстрено закупили пам’ять, запланували вікно обслуговування, а тим часом спробували «захистити кеш», вимкнувши prefetch і змінивши декілька властивостей dataset по всьому пулу. Наступного дня система стала гіршою. Стрибки затримки стали різкішими, а внутрішні помилки зросли.
Справжня проблема була в синхронних записах. Новий шлях інжесту використовував бібліотеку, що агресивно викликала fsync. Пул не мав SLOG, а vdev вже був близький до насичення в піковий час. Читання страждали, бо записи чергувалися; ARC ratio впав, бо система просто повільніше обслуговувала промахи, а не тому, що кеш «зламався».
Коли вони намалювали графік latency синхронних записів, все стало очевидно. Вони додали правильний SLOG з захистом від відключення живлення, виправили шлях інжесту і відкотили зміни в кешуванні. ARC hit ratio дещо відновився, але найголовніше — хвостові затримки впали до SLO.
Урок: падіння hit rate може бути ефектом, а не причиною. Метрики кешу — чудові свідки і жахливі підозрювані.
Міні-історія 2: Оптимізація, що відбилася бумерангом
Інша організація керувала VM-кластером на ZFS. Хтось запропонував L2ARC на блискучому споживчому NVMe: «Отримаємо RAM-подібну продуктивність без купівлі RAM.»
Зміна пройшла у тиші. Початкові тести виглядали краще. Усі заспокоїлися.
Через два тижні кластер отримав новий патерн навантаження: багато короткоживучих VM, що робили package install та CI build — багато читань, але й величезний churn.
L2ARC почав заповнюватися блоками, які ніколи не переиспользувалися. ARC мусив підтримувати метадані для тих кешованих блоків, і тиск на пам’ять зріс.
Гіпервізор почав іноді свопитися. Латентність стала дивною: не постійно поганою, але непередбачуваною у найгірший спосіб.
Команда дивилася на L2ARC hit rate. Він не був чудовим, але й не катастрофічним. Вони сперечалися, чи «30% hit rate» — це «добре».
Тим часом клієнти писали тікети.
Виправлення було нудним: видалити L2ARC, обмежити ARC щоб лишити місце для гіпервізора і гостів, і перенести scratch CI на dataset з обмеженим кешуванням тільки метаданих. Також вони перемістили найшумніше навантаження на інший пул.
Урок: L2ARC не безкоштовний. Якщо навантаження має великий churn, ви платите накладні витрати за кешування даних, які ніколи не будуть повторно використані.
Міні-історія 3: Нудна, але правильна практика, що врятувала ситуацію
Фінансова команда використовувала ZFS для домашніх директорій і внутрішніх інструментів. Нічого гламурного; багато дрібних файлів, багато обходів каталогів,
і іноді — скарги «чому checkout репо повільний». У них була звичка: щоквартально переглядати властивості датасетів і перевіряти, що «масові» датасети (бекапи, експорти, архіви) не можуть засмічувати ARC.
Тому їхній бекап-датасет мав primarycache=metadata. Архів мав великий recordsize. Інтерактивні датасети залишалися за замовчуванням. Команда також відстежувала latency пулу і промахи метаданих як перша черга сигналів.
Одного дня нова задача бекапу випадково вказала на інтерактивний датасет замість бекапу. Вона почала стрімінгові читання кілька годин протягом робочого часу. На більшості систем це б вичистило ARC і викликало хвилю «зберігання повільне» скарг.
Тут радіус ураження був обмежений. Неправильний датасет шумів, але основна частина пулу залишилася чутливою. ARC hit ratio коротко впав, але метадані залишилися гарячими і затримка, видима користувачам, не провалилася. Команда виявила неправильну конфігурацію з логів задач і виправила її без серйозного інциденту.
Урок: невелика, послідовна гігієна кешу перемагає героїчне налаштування. Найкращий інцидент — той, що виглядає як похибка округлення.
Поширені помилки (симптоми → корінь → виправлення)
1) «ARC hit ratio низький, отже зберігання повільне»
Симптоми: Hit ratio падає під час пакетних робіт; користувачі скаржаться; хтось пропонує подвоїти RAM.
Корінь: Стримінгові читання або одноразові скани обходять повторне використання. Промахи очікувані; обмеження — пропускна здатність.
Виправлення: Ізолюйте пакетні навантаження (окремий dataset/pool), встановіть primarycache=metadata на масових датасетах, обмежуйте з cgroups/ionice і вимірюйте пропускну здатність/затримку безпосередньо.
2) «L2ARC вирішить нашу затримку записів»
Симптоми: fsync-латентність БД погана; команда додає L2ARC; нічого не змінюється.
Корінь: L2ARC прискорює читання, а не синхронні записи. Затримка записів керується SLOG/пулом.
Виправлення: Додайте правильний SLOG для sync-heavy навантажень або змініть поведінку синхронізації додатка (якщо безпечно). Не купуйте SSD для неправильної задачі.
3) Рівень попадань високий, але додаток все одно повільний
Симптоми: ARC hit > 95%, але користувачі все одно бачать таймаути.
Корінь: Насичення CPU (checksums/compression/encryption), блокування або один гарячий dataset, що викликає затримки txg.
Виправлення: Пропрофілюйте CPU, перевірте iowait vs user time, інспектуйте txg і поведінку синхронних записів, і підтвердіть вузькі місця на рівні додатка.
4) «Ми поставимо primarycache=none скрізь, щоб зупинити забруднення»
Симптоми: Hit ratio падає, промахи метаданих вибухають, операції з каталогами стають болісно повільними.
Корінь: Перекіс: ви видалили саме те, що робить файлові робочі навантаження чутливими.
Виправлення: Використовуйте primarycache=metadata тільки для масових датасетів. Тримайте all для інтерактивних датасетів, якщо немає конкретної причини проти.
5) L2ARC робить систему менш стабільною
Симптоми: Іноді свопінг, стрибкоподібна латентність, тиск на reclaim ядра.
Корінь: Ні достатньо RAM для заголовків L2ARC і самого ARC; пристрій кешу стимулює більше churn кешу.
Виправлення: Видаліть або зменшіть L2ARC, обмежте ARC або додайте RAM. Якщо ви запускаєте гіпервізори, захистіть пам’ять хоста в першу чергу.
6) «Ми вимкнули prefetch і стало гірше»
Симптоми: Пропускна здатність послідовних читань падає, додатки роблять більше синхронних читань.
Корінь: Prefetch допомагав вашому реальному навантаженню; ви припустили, що це забруднення, бо бачили prefetch-статистику.
Виправлення: Увімкніть prefetch назад, а замість цього контролюйте навантаження, що створює шкідливий стрімінг. Вимірюйте до/після.
7) «ARC у нас величезний; чому ми промахуємося на метаданих?»
Симптоми: Достатньо RAM, але промахи метаданих зростають під час обходу каталогів.
Корінь: Робочий набір справді величезний, або special vdev відсутній і метадані читаються з повільних дисків, що робить промахи болісними.
Виправлення: Розгляньте special vdev для метаданих/дрібних блоків, зменшіть кількість файлів у директорії і переконайтеся, що масові датасети не витісняють метадані.
Жарт №2: Якщо ваш єдиний інструмент — ARC hit ratio, кожна проблема продуктивності виглядає як «додайте RAM». Так ви стаєте особою, яка замовляє RAM.
Контрольні списки / покроковий план
Покроково: вирішіть, чи є рівні попадань дієвими
- Визначте біль: хвостова затримка, пропускна здатність, джиттер чи таймаути? Спочатку отримаєте графік додатка.
- Класифікуйте навантаження: орієнтоване на читання, запис, змішане; стрімінгове vs повторно використовуваний робочий набір.
- Перевірте стан пулу:
zpool statusна предмет degraded/resilver/scrub під час піку. - Перевірте латентність пристроїв:
zpool iostat -vіiostat -x. Якщо диски завантажені — кеш не вирішить записів. - Перевірте поведінку sync: визначте додатки з fsync-heavy; перевірте конфігурацію SLOG за потреби.
- Огляньте поведінку ARC: промахи попиту vs prefetch; промахи метаданих vs даних; лічильники eviction/throttle.
- Перевірте тиск пам’яті: свопінг або низький available роблять прості налаштування ARC недоречними.
- Лише потім вирішіть: додати RAM, налаштувати політики кешу dataset, додати special vdev, додати/видалити L2ARC або не робити нічого.
Операційний чекліст: не дайте кешам стати забобоном
- Розділяйте датасети за типом навантаження: інтерактивні vs масові vs БД vs VM.
- Встановлюйте
primarycache=metadataна масових датасетах (бекапи, експорти), якщо не доведено протилежне. - Тримайте
compression=lz4за замовчуванням, якщо тільки CPU справді не є вузьким місцем. - Переглядайте
recordsizeдля БД/VM; не успадковуйте «1M скрізь», бо хтось так прочитав в блозі. - Відстежуйте латентність на рівні пулу і пристрою, а не лише hit rates.
- Документуйте, навіщо існує кожне налаштування; видаляйте те, що ніхто не може пояснити.
- Тестуйте L2ARC з реальними робочими трасами; не приймайте рішення лише на синтетичних бенчмарках.
- Захищайте запас пам’яті на гіпервізорах і мульти-орендних системах; жадібність кешу — спосіб викликати своп.
Питання й відповіді
1) Який «добрий» ARC hit ratio?
Однозначного числа немає. Для стабільного навантаження, орієнтованого на читання, більше зазвичай означає менше читань з диска і кращу затримку.
Для стрімінгових читань низький hit ratio — нормальний і не проблема. Судіть за затримкою та завантаженням диска, а не за естетикою.
2) Чому ARC hit ratio впав після додавання більше RAM?
Тому що навантаження змінилося, тому що ви почали вимірювати інакше, або тому що система тепер виконує більше паралельної роботи, що змінює динаміку кешу.
Також додавання RAM може підвищити конкурентність (додатки роблять більше роботи). Дивіться на промахи й latency, а не тільки на відношення.
3) Чи покращує стиснення показники попадань кешу?
Часто так — побічно. Із стисненням, як lz4, ARC може тримати більше логічних даних на GiB RAM (залежно від реалізації і навантаження).
Але якщо CPU обмежений, стиснення може обміняти I/O на CPU і погіршити затримку.
4) Чи треба ставити primarycache=metadata скрізь?
Ні. Це часта надреакція. Використовуйте його на датасетах з великими стрімінговими читаннями (бекапи, архіви), щоб вони не витісняли корисний кеш.
Залишайте інтерактивні датасети зі значенням all, якщо не доведено, що кешування блоків шкідливе.
5) Чи варто ставити L2ARC на NVMe-пули?
Іноді, але критерій вищий. Якщо ваш пул уже низьколатентний NVMe, L2ARC може нічого не дати і додати накладні витрати.
L2ARC блищить, коли уникнути повільних випадкових читань з HDD або зайнятих SSD, особливо коли робочий набір трохи більший за RAM.
6) Чи може L2ARC шкодити продуктивності?
Так. Він споживає RAM під заголовки і може збільшити тиск пам’яті. Також може марнувати записний пропуск на пристрій кешу, заповнюючись невикористовуваними блоками.
Якщо після увімкнення ви бачите свопінг або стрибкоподібну латентність — сприймайте це серйозно.
7) Якщо hit rate L2ARC лише 20%, чи варто його видалити?
Не автоматично. Спробуйте відповісти: що ці 20% hits замінюють? Якщо вони уникають повільних випадкових читань з HDD, 20% може бути великим.
Якщо вони замінюють вже- швидкі читання або здебільшого prefetch — можливо, це марно.
8) Чи допомагає ARC записам?
ARC переважно кеш читань. ZFS буферизує dirty data в пам’яті перед commit-ом transaction groups, і записи можуть бути згорнуті.
Але якщо ваша проблема — латентність синхронних записів, рівні попадань ARC не допоможуть. Дивіться на SLOG і латентність записів пулу.
9) Чому рівні попадань виглядають чудово, але користувачі все одно скаржаться?
Тому що затримки можуть походити з іншого місця: синхронні записи, насичення CPU, проблеми мережі, блокування додатка або один повільний пристрій у vdev.
Перевірте за допомогою zpool iostat -v, iostat -x і метрик додатка.
10) Яке найпростіше безпечне налаштування, що зазвичай допомагає?
Розділіть масові датасети і встановіть primarycache=metadata на них. Тримайте compression=lz4. Перевірте відповідність recordsize для БД/VM.
Усе інше має бути обґрунтоване вимірюваннями.
Висновок: що робити далі
Рівні попадань кешу — це не продуктивність. Це підказка. Іноді дуже хороша підказка. Але якщо ви ставите їх як табло оцінок, ви налаштуєте не те,
і все ще будете повільними — просто з гарнішими графіками.
Практичні наступні кроки:
- Почніть відстежувати латентність та завантаження пулу/пристроїв разом зі статистикою ARC/L2ARC.
- Розділіть датасети за робочим навантаженням і застосуйте політики кешу свідомо (масові vs інтерактивні vs БД/VM).
- Використовуйте швидкий план діагностики: стан → латентність → поведінка sync/записів → розклад ARC → і лише тоді налаштування кешу.
- Якщо щось змінюєте, робіть по одному кроку і перевіряйте за метриками додатка, а не лише за hit ratios.
Вам не потрібні ідеальні рівні попадань. Вам потрібна передбачувана затримка, стабільна пропускна здатність і система, що не дивує вас о 2-й ночі.
ZFS може цього досягти — якщо перестати робити з кешу релігію.