Тестування стиснення ZFS: вимірюємо реальні переваги, а не плацебо

Було корисно?

Ви вмикаєте compression=lz4, запускаєте «бенчмарк» й цифри виглядають приголомшливо. Чудово — поки в найзавантаженішого хосту віртуальних машин не починаються провали о 10 ранку,
латентність бази даних не неочікувано не стає стрибкоподібною, а єдина метрика, якою хваляться, — compressratio з zfs get.
Стиснення не «провалилось». Провалився бенчмарк.

Стиснення у ZFS зазвичай — це безкоштовний бонус, але продакшн-системи не роздають обіди; вони виконують SLA.
Якщо ви хочете реальних виграшів — не плацебо — треба вимірювати правильні речі, у правильному порядку, під правильним навантаженням і приймати рішення по-дорослому.

Що означає «реальні переваги» для стиснення ZFS

«Стиснення робить дані меншими» — це правда в того ж сенсі, що й «фізичні вправи роблять вас здоровішими». Так, загалом. Але: залежить від того, що ви робите,
як вимірюєте й на чому вже обмеження.

У продакшні стиснення ZFS вважається успішним, коли воно досягає одного (або кількох) з цих результатів за вашими реальними обмеженнями:

  • Зменшення p95/p99 затримки (через те, що ви проштовхуєте менше байтів через повільний пристрій або насичений HBA).
  • Збільшення пропускної здатності (коли сховище обмежене по смузі й стиснення міняє CPU на менше записів).
  • Ефективніший кеш (ARC/L2ARC вміщують більше логічних даних на фізичний байт, якщо блоки добре стискаються).
  • Менша ампліфікація записів (менше фізичних блоків запису для тих самих логічних змін).
  • Більше корисної ємності без зміни домену відмов або структури пулу.

Стиснення не вважається успішним, якщо єдиний «виграш» — гарніший показник у zfs get compressratio.
Якщо CPU стає обмежувачем, якщо хвости затримок ростуть, або якщо навантаження взагалі не залежить від I/O, ви цілком можете погіршити ситуацію.

Завдання бенчмарку стиснення — не проголосити переможця кодека. Завдання — відповісти на конкретне питання:
«З цим навантаженням, на цьому обладнанні, за цими обмеженнями, що змінюється в затримці, пропускній здатності, CPU і обсязі записів при зміні стиснення?»

Факти та історія, що дійсно мають значення

Трохи контексту вбережуть від карго-культової оптимізації. Ось конкретні факти, які зустрічаються при інтерпретації бенчмарків:

  1. ZFS спроєктовано для цілісності даних від початку до кінця (контрольні суми всюди), а не для «максимальних IOPS за будь-яку ціну». Стиснення працює в цій моделі.
  2. LZ4 став рекомендацією за замовчуванням в багатьох колах OpenZFS, бо він достатньо швидкий, і CPU рідко є обмежувачем для типової серверної роботи.
  3. Сучасний OpenZFS додав ZSTD, бо люди хотіли кращі відношення зі змінними рівнями; це не просто «трохи повільніше», це діапазон.
  4. Стиснення відбувається по блоках і здійснюється перед записом на диск; нестисувані блоки зберігаються без стиснення (зазвичай із невеликим оверхедом).
  5. recordsize важить, бо стиснення застосовується до блоків запису; ті самі дані можуть стиснутися по-різному при 16K та 128K.
  6. Copy-on-write змінює економіку: перезапис невеликих частин великих блоків може створювати більше хаосу; стиснення може зменшити фізичний обсяг змін, якщо дані стисувальні.
  7. ARC кешує післястиснені дані ефективно: якщо дані стискаються 2:1, ARC може вмістити приблизно вдвічі більше логічного вмісту, змінюючи поведінку читань.
  8. Dedup і стиснення не завжди «друзі»: dedup підвищує навантаження на метадані і RAM; тестування стиснення на пулі з dedup може ввести в оману.
  9. Special vdev змінили поведінку метаданих: метадані й невеликі блоки можуть жити на швидшому носії, тому «стиснення покращило затримку» може бути фактично «special vdev вас врятував».

Одна перефразована думка варта стіни: Усе ламається постійно — Вернер Фогельс (перефразовано). Налаштування стиснення — не виняток:
тестуйте з урахуванням режимів відмов і насичення, а не лише порожньо-лабораторними суперзапусками.

Принципи бенчмарку (як не обдурити себе)

1) Визначте, що ви оптимізуєте: хвости затримки, пропускну здатність або ємність

Якщо бізнесу важливий p99, припиніть показувати графіки середньої пропускної здатності. Якщо важлива ємність — припиніть показувати «IOPS зросли»,
коли ваш датасет невживаний для стиснення. Виберіть ціль, потім — вимірювання.

2) Відокремлюйте поведінку з теплим кешем від холодного кешу

Бенчмарки ZFS без контролю кешу — це практично тест Роршаха. ARC може робити «продуктивність диска» схожою на «продуктивність RAM» деякий час.
Це не погано — ARC реальний — але це інша система, ніж «наскільки швидкі мої vdev під тривалим навантаженням?»

3) Вимірюйте вартість CPU явно

Стиснення не безкоштовне. LZ4 дешевий, ZSTD може бути дешевим або дорогим в залежності від рівня і даних. Якщо не вимірювати насичення CPU,
ви неправильно приписуватимете уповільнення «накладним витратам ZFS» або «мережі».

4) Не бенчмаркуйте на порожніх пулах і не оголошуйте перемогу

Поведінка ZFS змінюється з наповненням пулу. Фрагментація, розподіл metaslab і патерни запису еволюціонують. Пул на 20% інакший за пул на 80%.
Якщо тестували тільки порожній пул, ви тестували найкращий випадок, який більше ніколи не побачите.

5) Використовуйте реалістичні розміри I/O та рівень паралелізму

Якщо ваше реальне навантаження — 8K випадкових читань при queue depth 32, не запускайте 1M послідовних записів при queue depth 1 і не називайте це «як база даних».
Фахівці зберігання люблять числа; реальність байдужа.

6) Контролюйте те, що ZFS може змінити під вами

recordsize, volblocksize (для zvol), sync налаштування, atime, xattr-зберігання і політики special vdev все впливають на результат.
Бенчмарк стиснення, який змінює ще чотири інші змінні, — це ритуал, а не експеримент.

Перший жарт (і так, він короткий): бенчмарк без контролю — як пожежна тривога, яку ви забули витягнути.
Усі відчувають себе готовими, допоки будівля не загориться.

Метрики, що мають значення (і які — ванітні)

Метрики стиснення: корисні, але обмежені

  • compressratio: відношення логічного до фізичного місця на рівні датасету. Добре для планування ємності. Погано як передбачувач продуктивності.
  • logicalused vs used: показує, скільки місця економить стиснення. Все ще не про продуктивність.
  • збереження по алгоритмах (lz4 vs zstd-N): має сенс лише якщо форма ваших даних стабільна.

Метрики продуктивності: що вирішує спір

  • p95/p99 затримка читань і записів (реальність для застосунків).
  • час обслуговування пристрою (zpool iostat -v колонки latency — золото).
  • завантаження CPU та steal (стискання спалює цикли; віртуалізація додає свою надбавку).
  • байти, записані/прочитані на рівні vdev (обіцянка «менше байтів» від стиснення тут вимірюється).
  • хіт-рейти ARC у steady state (стиснення може збільшити ефективну ємність кеша).

Ванітні метрики (або «потрібен контекст»)

  • Піковий пропуск одного запуску: часто просто прогрів кешу.
  • Середня затримка: середні приховують болючі хвости. Хвостова латентність не дає спокою вночі.
  • IOPS без розміру блоку: безглуздо. 4K IOPS і 128K IOPS — інші планети.
  • «Зайнятість» пулу з zpool list самостійно: не показує, наскільки ви фрагментовані або маєте ампліфікацію записів.

Практичні завдання: команди, виходи та рішення (12+)

Це ті завдання, які я реально виконую, коли хтось каже: «Стиснення зробило повільніше» або «ZSTD рівня 19 — це безкоштовний виграш».
Кожне завдання включає: команду, що означає вихід, і рішення, яке ви приймаєте.

Завдання 1: Підтвердити налаштування стиснення датасету (і сюрпризи з успадкуванням)

cr0x@server:~$ zfs get -o name,property,value,source compression tank/vm
NAME     PROPERTY     VALUE     SOURCE
tank/vm  compression  zstd      local

Значення: Стиснення встановлено як zstd локально, а не успадковано. Якби було успадковано, ви б бачили inherited from ....
Рішення: Якщо бенчмаркуєте, переконайтесь, що датасет під тестом явно встановлено на кодек, який ви вказуєте, і зафіксуйте джерело.

Завдання 2: Перевірити фактичне досягнуте відношення, а не лише «увімкнено»

cr0x@server:~$ zfs get -o name,property,value -p used,logicalused,compressratio tank/vm
NAME     PROPERTY      VALUE
tank/vm  used          214748364800
tank/vm  logicalused   322122547200
tank/vm  compressratio 1.50x

Значення: Логічно ~300G, фізично зайнято ~200G — стиснення працює (~1.5x).
Рішення: Якщо compressratio ~1.00x, тест продуктивності навряд покаже виграш «менше байтів»; зосередьтесь тоді на накладних CPU і затримках.

Завдання 3: Перевірити здоров’я пулу та помилки перед звинуваченням стиснення

cr0x@server:~$ zpool status -xv tank
pool 'tank' is healthy

Значення: Немає відомих помилок. Якщо ви бачите помилки контрольних сум або degraded vdevs, ваш «бенчмарк стиснення» насправді тестує відновлення після збоїв.
Рішення: Виправте стан пулу спочатку; бенчмаркувати на degraded пулі — як бігти марафон в одній кросівці.

Завдання 4: Перевірити заповненість пулу (детектор «виграшу на порожньому пулі»)

cr0x@server:~$ zpool list -o name,size,alloc,free,capacity,fragmentation tank
NAME  SIZE  ALLOC   FREE  CAP  FRAG
tank  40T   28T     12T   70%  34%

Значення: 70% заповнення з помірною фрагментацією. Продуктивність при 70% може сильно відрізнятися від 20%.
Рішення: Якщо ваш тест проводився на свіжому пулі, повторіть на подібному рівні заповнення або принаймні вкажіть різницю.

Завдання 5: Спостерігати смугу vdev і затримки під навантаженням

cr0x@server:~$ zpool iostat -v tank 1
                              capacity     operations     bandwidth    total_wait     disk_wait
pool                        alloc   free   read  write   read  write   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----  -----  -----  -----  -----
tank                         28T    12T    2200   1800  320M  290M    12ms  18ms     4ms  10ms
  raidz2-0                   28T    12T    2200   1800  320M  290M    12ms  18ms     4ms  10ms
    sda                          -      -     0    300    0B   48M       -     -     3ms  11ms
    sdb                          -      -     0    300    0B   48M       -     -     4ms  10ms
    sdc                          -      -     0    300    0B   48M       -     -     4ms  10ms
    sdd                          -      -     0    300    0B   48M       -     -     4ms  10ms
--------------------------  -----  -----  -----  -----  -----  -----  -----  -----  -----  -----

Значення: Дивіться на total_wait і disk_wait. Якщо disk_wait високе — бутилі в диску.
Якщо total_wait високе, але disk_wait низьке — можливо черга в софті (CPU, блокування, sync тощо).
Рішення: Якщо стиснення зменшує смугу, але затримки лишаються високими, ви не обмежені смугою; шукайте CPU, sync або фрагментацію.

Завдання 6: Спостерігати насичення CPU при перемиканні стиснення

cr0x@server:~$ mpstat -P ALL 1 5
Linux 6.6.0 (server) 	12/26/2025 	_x86_64_	(16 CPU)

12:10:01 AM  CPU   %usr %nice %sys %iowait %irq %soft %steal %idle
12:10:02 AM  all   62.1  0.0  18.4   0.9    0.0  1.1    0.0   17.5
12:10:02 AM    3   96.0  0.0   3.0   0.0    0.0  0.0    0.0    1.0

Значення: Деякі ядра майже завантажені. Стиснення часто концентрує роботу; одне гаряче ядро може стримувати пропуск.
Рішення: Якщо вмикання важчого кодека доводить ядра до насичення, очікуйте спайків затримки. Або знижуйте рівень ZSTD, зменшуйте паралелізм, або додайте резерв CPU.

Завдання 7: Перевірити поведінку ARC (чи тестуєте ви RAM?)

cr0x@server:~$ arcstat 1 3
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  arcsz     c
12:10:20   11K   120     1   110   1    10   0     0   0   48G   64G
12:10:21   12K   140     1   130   1    10   0     0   0   48G   64G

Значення: Рейт пропусків малий; читання здебільшого з ARC. Чудово для застосунків, погано якщо ви намагаєтесь порівняти продуктивність vdev.
Рішення: Якщо потрібні холодні кеш-числа, спроектуйте тест так, щоб перевищити ARC, або використайте унікальні дані для кожного запуску.

Завдання 8: Перевірити recordsize/volblocksize на відповідність навантаженню

cr0x@server:~$ zfs get -o name,property,value recordsize tank/vm
NAME     PROPERTY    VALUE
tank/vm  recordsize  128K

Значення: Файли, зазвичай, використовують 128K записи. Для VM-образів з 4K випадковими I/O це може бути невідповідністю.
Рішення: Для VM-датасетів розгляньте recordsize=16K (або zvol volblocksize=8K/16K) і протестуйте знову — стиснення взаємодіє з цим.

Завдання 9: Підтвердити поведінку sync (бенчмарк стиснення vs бенчмарк sync)

cr0x@server:~$ zfs get -o name,property,value sync tank/vm
NAME     PROPERTY  VALUE
tank/vm  sync      standard

Значення: Sync-запити виконуються. Якщо хтось встановив sync=disabled, вони не пришвидшили стиснення; вони зняли гарантії.
Рішення: Якщо у результатах тестів є sync=disabled, відкиньте ці результати, якщо продакшн також не працює в такому «ненадійному» режимі.

Завдання 10: Спостерігати TXG та симптоми throttling записів через iostat + затримки

cr0x@server:~$ iostat -x 1 3
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          55.40    0.00   17.10    1.20    0.00   26.30

Device            r/s     w/s   rMB/s   wMB/s  avgrq-sz avgqu-sz await  r_await  w_await  svctm  %util
nvme0n1          0.0   1200.0    0.0   280.0    478.0     8.2    6.9    0.0      6.9     0.8   92.0

Значення: Пристрій дуже завантажений, але час обслуговування низький; черга підростає. Стиснення може зменшити wMB/s, що знизить черги.
Рішення: Якщо стиснення знижує смугу і await теж знижуються — це реальний виграш. Якщо смуга падає, але await росте — ви CPU- або sync-обмежені.

Завдання 11: Інспектувати логічні vs фізичні записи на датасет (що ви платите дискам)

cr0x@server:~$ zfs get -o name,property,value -p written,logicalwritten tank/vm
NAME     PROPERTY        VALUE
tank/vm  written         53687091200
tank/vm  logicalwritten  96636764160

Значення: Логічно записано ~90G, фізично — ~50G. Стиснення зменшує фізичні записи.
Рішення: На SSD-системах, чутливих до зносу, це одна з найвагоміших причин використовувати стиснення навіть якщо продуктивність «в порядку».

Завдання 12: Підтвердити ashift і розташування (результати бенчмарків не переносяться добре)

cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head
12:        ashift: 12
45:    vdev_tree:

Значення: ashift=12 означає 4K сектори. Якщо порівнювати з іншим пулом з іншим ashift, ви не порівнюєте стиснення; ви порівнюєте вирівнювання.
Рішення: Коли публікуєте внутрішні бенчмарки, включайте ashift і тип vdev. Інакше хтось повторить ваш тест і «виявить» іншу фізику.

Завдання 13: Безпечно порівняти алгоритми на тестовому датасеті

cr0x@server:~$ sudo zfs create -o compression=lz4 -o recordsize=128K tank/bench_lz4
cr0x@server:~$ sudo zfs create -o compression=zstd -o recordsize=128K tank/bench_zstd
cr0x@server:~$ zfs get -o name,compression,recordsize -r tank/bench_lz4 tank/bench_zstd
NAME            COMPRESSION  RECORDSIZE
tank/bench_lz4  lz4          128K
tank/bench_zstd zstd         128K

Значення: Два датасети ідентичні, крім стиснення. Ось як виглядає «бенчмарк стиснення».
Рішення: Завжди ізолюйте змінну. Якщо ви ще змінюєте recordsize, ви бенчмаркуєте пакет змін.

Завдання 14: Виміряти реальну затримку на рівні застосунку (не лише статистику сховища)

cr0x@server:~$ fio --name=randread --directory=/tank/bench_lz4 --rw=randread --bs=4k --iodepth=32 --numjobs=4 --size=8G --time_based --runtime=60 --group_reporting
randread: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
...
  read: IOPS=128k, BW=500MiB/s (524MB/s)(29.3GiB/60001msec)
  lat (usec): min=80, max=5400, avg=245.10, stdev=110.12
  clat percentiles (usec):
   |  50.00th=[  230],  90.00th=[  320],  95.00th=[  410],  99.00th=[  980],  99.90th=[ 2900]

Значення: Процентилі — ось основна історія. Якщо перехід на ZSTD підвищує 99.9-й процентиль при незмінному середньому, ви отримали хвостову латентність.
Рішення: Використовуйте p99/p99.9 як поріг прийняття для чутливих до затримок робочих навантажень (бази даних, хости VM).

Рецепти FIO, що моделюють реальні навантаження

FIO не «істина в останній інстанції». Це контрольована брехня, яку ви розповідаєте системі, щоб побачити реакцію. Розповідайте правильну брехню.
Суть — наблизити патерни доступу: випадкові vs послідовні, sync vs async, розмір блоку, паралелізм, розмір робочого набору і поведінка перезапису.

Рецепт A: VM-подібні випадкові 4K читання/записи (мікс)

cr0x@server:~$ fio --name=vm-mix --directory=/tank/bench_zstd --rw=randrw --rwmixread=70 --bs=4k --iodepth=32 --numjobs=8 --size=16G --time_based --runtime=120 --direct=1 --group_reporting
vm-mix: (g=0): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
...
  read: IOPS=84.2k, BW=329MiB/s (345MB/s)
  write: IOPS=36.1k, BW=141MiB/s (148MB/s)
  clat percentiles (usec):
   |  99.00th=[ 2200],  99.90th=[ 6500],  99.99th=[17000]

Як використовувати: Запустіть ту саму роботу на bench_lz4 і bench_zstd, порівняйте p99.9 і CPU.
Рішення: Якщо ZSTD підвищує пропускну здатність, але значно погіршує p99.9, LZ4 — безпечніший за замовчуванням для VM-міксу.

Рецепт B: База-даних-подібні випадкові записи 8K з fsync-пресією

cr0x@server:~$ fio --name=db-sync --directory=/tank/bench_lz4 --rw=randwrite --bs=8k --iodepth=1 --numjobs=4 --size=8G --time_based --runtime=120 --fsync=1 --direct=1 --group_reporting
db-sync: (g=0): rw=randwrite, bs=(R) 8192B-8192B, (W) 8192B-8192B, (T) 8192B-8192B, ioengine=libaio, iodepth=1
...
  write: IOPS=4200, BW=32.8MiB/s (34.4MB/s)
  clat percentiles (usec):
   |  95.00th=[ 1800],  99.00th=[ 4200],  99.90th=[12000]

Як використовувати: Тут домінують sync і якість SLOG. Стиснення може допомогти, зменшуючи байти, але CPU також може мати значення.
Рішення: Якщо вибір кодека мало рухає числа, припиніть сперечатися про стиснення і подивіться на шлях sync (SLOG, затримки, черги).

Рецепт C: Послідовні записи великими блоками (ціль для резервних копій, логи)

cr0x@server:~$ fio --name=seqwrite --directory=/tank/bench_zstd --rw=write --bs=1M --iodepth=8 --numjobs=2 --size=64G --time_based --runtime=120 --direct=1 --group_reporting
seqwrite: (g=0): rw=write, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=8
...
  write: IOPS=680, BW=680MiB/s (713MB/s)
  cpu          : usr=78.2%, sys=8.5%, ctx=12000, majf=0, minf=400

Як використовувати: Великі послідовні записи можуть бути обмежені смугою. Стиснення може значно зменшити байти, якщо дані схожі на логи/текст.
Рішення: Якщо частка CPU user% різко зростає і BW перестає рости — знижуйте рівень ZSTD або використовуйте LZ4. Якщо BW зростає і CPU має запас — ZSTD може бути того вартий.

Рецепт D: Тест на «нестисувані дані» (саніті-тест, щоб виявити плацебо)

cr0x@server:~$ fio --name=incompressible --directory=/tank/bench_lz4 --rw=write --bs=128k --iodepth=16 --numjobs=4 --size=16G --time_based --runtime=60 --direct=1 --refill_buffers=1 --buffer_compress_percentage=0 --group_reporting
incompressible: (g=0): rw=write, bs=(R) 131072B-131072B, (W) 131072B-131072B, (T) 131072B-131072B, ioengine=libaio, iodepth=16
...
  write: IOPS=5200, BW=650MiB/s (682MB/s)

Як використовувати: Якщо дані нестисувальні, стиснення мало зменшить байти і може коштувати CPU.
Рішення: Якщо ви бачите «масовий прискорення» на нестисувальних записах після включення стиснення — ви вимірюєте щось інше (кеш, вирівнювання або артефакт тесту).

Другий жарт (останній, обіцяю): ZSTD level 19 на зайнятому гіпервізорі — як дати всім у офісі стійкі робочі столи.
Пози покращуються; нарікання стають голоснішими.

Плейбук швидкої діагностики

Коли хтось каже «Стиснення змінило продуктивність», у вас нема часу на тижневу лабораторну реконструкцію. Тріажуйте як SRE.
Мета — швидко ідентифікувати клас вузького місця: диск, CPU, шлях sync або ілюзія кешу.

Перше: визначте, чи ви I/O-залежні або CPU-залежні

  1. Перевірте насичення CPU під навантаженням. Використайте mpstat і дивіться на завантажені ядра та зростання system time.
    Якщо CPU упирається, рівень стиснення — реальна ручка з реальними наслідками.
  2. Перевірте використання пристроїв і часи очікування. Використайте zpool iostat -v 1 і iostat -x 1.
    Якщо диски завантажені і час очікування росте з пропускною здатністю — ви I/O-залежні і стиснення часто допомагає.

Друге: з’ясуйте, чи бенчмарк — просто ARC

  1. Спостерігайте пропуски ARC. Якщо miss% під час читань маленький — ви в основному в RAM.
    «Прискорення» читань через стиснення тут може бути ефектом кеша, а не швидкості диска.
  2. Збільшіть розмір робочого набору. Якщо ваш датасет вміщується в ARC, ви не можете робити висновки про диск.

Третє: підтвердіть, що шлях sync/записів не є справжнім обмежувачем

  1. Перевірте властивість sync і поведінку навантаження зі sync. Для баз даних sync часто домінує.
  2. Спостерігайте затримки під час тривалих sync-записів. Стиснення може зменшити байти, але затримки SLOG і поведінка TXG все одно можуть домінувати.

Четверте: впевніться, що ви не змінили інші змінні

  1. Зафіксуйте властивості датасету: recordsize, atime, xattr, primarycache, logbias, redundant_metadata.
  2. Зафіксуйте властивості/макет пулу: ashift, RAIDZ vs mirrors, special vdevs, SLOG, L2ARC.

Типові помилки: симптом → корінна причина → виправлення

1) «Стиснення увімкнено, пропускна здатність зросла вдвічі» (але лише при другому запуску)

Симптом: Перший запуск повільніший, другий — блискавичний, стиснення «перемагає».

Корінна причина: ARC прогрівся. Ви бенчмаркували RAM, а не диски. Стиснення також може підвищувати ефективність ARC, перебільшуючи ефект.

Виправлення: Використовуйте робочий набір більший за ARC, застосовуйте унікальні файли на кожен запуск і порівнюйте steady-state після прогріву. Слідкуйте за ARC miss%.

2) «ZSTD повільніший за LZ4, отже ZSTD поганий»

Симптом: Більші хвости затримки та менше IOPS після переходу на ZSTD.

Корінна причина: Насичення CPU або вузьке місце на одному ядрі. Рівень ZSTD занадто високий для наявного паралелізму та запасу CPU.

Виправлення: Тестуйте ZSTD на нижчих рівнях; вимірюйте CPU. Якщо не можете дозволити CPU, оберіть LZ4 і рухайтесь далі.

3) «compressratio 2.5x, отже ми повинні отримати 2.5x продуктивність»

Симптом: Чудові коефіцієнти стиснення, але мало або взагалі ніякої зміни продуктивності.

Корінна причина: Навантаження не обмежене смугою; воно заточене під латентність, sync або CPU в іншому місці (контрольні суми, метадані, блокування застосунку).

Виправлення: Використовуйте процентилі затримок і vdev wait для пошуку справжнього обмежувача. Стиснення зменшує байти, але не обов’язково IOPS/латентність.

4) «Ми бенчмаркували з sync=disabled, щоб побачити максимум»

Симптом: Нереальні числа записів, потім «продакшн не відповідає».

Корінна причина: Ви зняли гарантії на стійкість і повністю змінили шлях запису.

Виправлення: Бенчмаркуйте з продакшн-символікою sync. Якщо мусите тестувати sync=disabled, позначте це як «unsafe mode» і не приймайте рішення на його основі.

5) «Стиснення зробило знімки дорожчими»

Симптом: Обсяг, зайнятий снапшотами, виглядає більшим після зміни стиснення.

Корінна причина: Невідповідність recordsize і патернів перезапису. Великий recordsize + малі перезаписи збільшує хаос; стиснення змінює фізичне розміщення і може впливати на фрагментацію.

Виправлення: Вирівняйте recordsize/volblocksize під розмір перезапису. Для VM-образів і баз даних менші блоки частіше більш стабільні.

6) «Ми вкл. ZSTD і тепер scrub став повільнішим»

Симптом: Час scrub зростає і конкурує з робочим навантаженням.

Корінна причина: Scrub читає і перевіряє блоки; декомпресія додає нагрузку на CPU під час scrub на завантажених системах.

Виправлення: Плануйте scrubs на періоди низької активності, обмежуйте вплив scrub і уникайте високих рівнів стиснення на системах без резерву CPU.

Три короткі історії з практики

Міні-історія 1: Інцидент, спричинений хибним припущенням

Команда розгорнула новий кластер VM і стандартизувала ZSTD, бо в staging цифри виглядали чудово. Вони зробили те, що роблять усі:
скопіювали «золотий» образ, провели кілька тестів копіювання файлів і вже святкували перемогу.

Хибне припущення було тонким: вони вважали, що «хорошо стискається» означає «працює швидко». Їхній золотий образ був переважно ОС-файлами — сильно стискувальні,
головним чином читальні і дуже дружні до кеша. Продакшн же був мішаним: сервіси з великими логами і кілька розмовних middleware з постійними малими записами.

В понеділок вранці кластер вийшов на пік. Графіки CPU пішли вгору, але не в приємному сенсі. Затримки зросли, потім почали скакати.
Команда гіпервізора звинувачувала мережу. Мережевики звинувачували сховище. Сховище звинувачувало «шумних сусідів».
Тим часом пейджер не дбав про організаційну структуру.

Справжня проблема: ZSTD на агресивному рівні стиснув потік дрібних випадкових записів. Пул не насичував диски; насичувався CPU.
Навантаження мало достатню конкуренцію, щоб потоки стиснення стали вузьким місцем, і хвостова латентність покарала всіх.

Виправлення було нудним: повернутися на LZ4 для VM-датасетів і залишити ZSTD для резервних/архівних даних, де пропуск має більше значення, ніж мікролатентність.
Також вони змінили набір тестів, додавши FIO VM-mix з p99.9 як гейт.

Міні-історія 2: Оптимізація, що відкотилась

Інша компанія вирішила «оптимізувати» стиснення, вимкнувши його для датасету бази даних. Аргумент звучав переконливо:
«Сторінки БД вже компактні; стиснення — марна трата CPU.» Декілька графіків показували незначне падіння CPU.

Через місяць індикатори зносу SSD почали гіршати швидше, а нічні вікна техобслуговування — рости.
Нічого не горіло, але усе відчувалося важчим — ніби пул набрав ваги.

Розслідування показало, що хоча сторінки БД не були сильно стискувальні, вони були досить стискувальні, щоб зменшити фізичні записи помітно.
Вимкнення стиснення збільшило кількість фізичних байтів, що підвищило ампліфікацію записів і наблизило пул до смугового потолка.
«Економія» CPU була реальною, але не тим ресурсом, що обмежував систему.

Ще гірше, відключення стиснення зменшило ефективну ємність ARC для того датасету. Зросла читальна ампліфікація,
і база почала частіше пропускати кеш у пікові періоди.

Вони повернули LZ4, а не ZSTD, тому що він давав більшість зменшення записів з мінімальною вартістю CPU.
Оптимізація провалилася, бо оптимізувала неправильний ресурс. Сховище — це система; одного компоненту не налаштуєш у відриві.

Міні-історія 3: Нудна, але правильна практика, що врятувала день

Одна команда платформи підтримувала просте внутрішнє правило: будь-яке твердження про продуктивність має супроводжуватися «маніфестом бенчмарку».
Не гігантським документом — просто чеклістом у тікеті: обладнання, макет пулу, ashift, властивості датасету, рівень заповнення, профіль навантаження,
стан кеша (теплий/холодний) і точні команди.

Під час оптимізації витрат хтось запропонував перевести всі датасети на ZSTD високого рівня, щоб «заощадити місце».
Запит на зміну супроводжувався слайд-деком і одним числом: «2.1x компресія».
Маніфест змусив їх додати p99 затримку при змішаному навантаженні й використання CPU.

Маніфест також вимагав тест на пулі зі схожим заповненням і тією ж конфігурацією special vdev.
Там і з’явився сюрприз: запас CPU був нормальний на половині хостів, але вужчий на старих — які стали б слабким ланцюгом.

Вони розгорнули цільову зміну: ZSTD для архівних і логових датасетів, LZ4 для VM і заточених на латентність сервісів,
плюс явну примітку «не перевищувати ZSTD рівень X на класі хостів Y» в конфігураційному менеджменті.

Нічого драматичного після цього не сталося. Ось у чому суть. Нудна практика вберегла їх від дуже цікавої аварії.

Чеклісти / покроковий план

Покроково: бенчмарк стиснення, який ви зможете захистити в зміні

  1. Клонувати припущення середовища. Такий же тип пулу, подібне заповнення, схожий клас апаратури, та сама версія OS/OpenZFS.
  2. Створити два датасети, що відрізняються лише в compression (і опційно рівень ZSTD).
  3. Заблокувати властивості: recordsize/volblocksize, sync, atime, xattr, primarycache, logbias (якщо потрібно).
  4. Вибрати 2–3 моделі навантаження, що відображають реальність: VM mix, DB sync-записи, послідовний інгест/бекуп.
  5. Визначити критерії успіху до запуску: поріг p99.9, мінімальна пропускна здатність, максимум використання CPU і вимога до економії ємності.
  6. Запустити прогрів і потім вимірювання у steady-state. Захопити p95/p99/p99.9, CPU і vdev wait.
  7. Повторити запуски (принаймні 3) і порівняти варіативність. Якщо варіабельність велика — середовище недостатньо контрольоване.
  8. Записати маніфест: точні команди, властивості датасету, стан пулу, рівень заповнення і спостережуване вузьке місце.
  9. Приймати рішення за класами датасету (VM, DB, логи, бекапи), а не «один кодек для всього».

Операційний чекліст: перед зміною стиснення в продакшні

  • Підтвердити запас CPU під піковим навантаженням (не о 2 ранку).
  • Підтвердити стискуваність робочого набору (тестувати на реальних даних, а не на синтетичних нулях).
  • Підтвердити план відкату: змінити властивості легко; відкат регресій продуктивності в зайнятому кластері — ні.
  • Підтвердити моніторинг: панелі p99 затримки, CPU і device wait мають існувати і дивитисься.
  • Підтвердити радіус ураження: змінюйте спочатку один клас датасету або одну групу хостів.

FAQ

1) Чи варто вмикати стиснення на ZFS за замовчуванням?

Так — LZ4 для більшості загальних датасетів — розумний вибір за замовчуванням. Він зазвичай економить місце і зменшує фізичні записи з мінімальною вартістю CPU.
Ставте «без стиснення» як виняток, який ви можете обґрунтувати вимірюваннями.

2) Чи завжди ZSTD кращий за LZ4?

Ні. ZSTD може давати кращі коефіцієнти, але коштує більше CPU і може погіршити хвостову латентність при дрібних випадкових записах з високою конкуренцією.
Використовуйте ZSTD там, де економія ємності важлива і є запас CPU; тримайте LZ4 там, де латентність — король.

3) Чому compressratio виглядає чудово, але продуктивність не покращується?

Бо ваше вузьке місце може бути не в смузі. Латентність, синхронні операції, конкуренція за метадані, CPU або блокування на рівні застосунку можуть домінувати.
Стиснення зменшує байти; воно не знімає інші обмеження стеку.

4) Чи може стиснення покращити читання?

Так, особливо якщо ви обмежені смугою або кешем. Якщо стиснені блоки означають менше байтів з диска, читання може пришвидшитись.
Також ARC може ефективніше тримати більше логічних даних, коли блоки добре стискаються.

5) Чи треба перепресовувати старі дані після зміни властивості?

Стиснення ZFS застосовується до нових записів. Існуючі блоки зберігають свій стан стиснення, поки їх не перезаписати.
Якщо потрібно перепресувати старі дані, зазвичай їх перезаписують (наприклад, send/receive на новий датасет або копіювання).

6) Чи валідно бенчмаркувати за допомогою dd if=/dev/zero?

Це валідно для перевірки пікової поведінки на тривіально стискувальних даних, але це жахливий проксі для продакшну.
Видає ефектні, але оманливі результати.

7) Як обрати рівень ZSTD?

Почніть з дефолтного zstd (який передбачає розумний рівень залежно від імплементації) або явно оберіть низький рівень (наприклад, zstd-3 або zstd-5)
для систем, чутливих до продуктивності. Підвищуйте лише якщо доведете наявність запасу CPU і реальну користь по ємності.

8) Чи впливає recordsize на результати стиснення?

Так. Стиснення застосовується по record block. Більший recordsize може покращити коефіцієнт для великих послідовних даних, але може шкодити при overwrite-важких навантаженнях.
Налаштовуйте recordsize під робоче навантаження спочатку, а потім оцінюйте рівні стиснення.

9) Чому мої результати FIO так змінюються між запусками?

Типові причини: стан ARC, фонова scrub/resilver, інші орендарі, дрейф рівня заповнення пулу і масштабування частоти CPU.
Якщо варіабельність велика, у вас неконтрольоване середовище — не приймайте незворотні рішення на його основі.

10) Чи взаємодіє стиснення з шифруванням?

Так. Стиснення відбувається перед шифруванням у пайплайні ZFS (тому стиснення все ще працює), але шифрування додає вартість CPU теж.
Тестуйте з обома ввімкненими, якщо продакшн використовує обидва — бо конкуренція за CPU сумується.

Наступні кроки, які ви можете зробити цього тижня

Якщо ви хочете отримати результати стиснення, які зможете захистити на дизайн-рев’ю (і не пожалкувати в інцидент-рев’ю), зробіть це:

  1. Створіть два тестових датасети ідентичні, крім стиснення (lz4 vs zstd на обраному рівні).
  2. Запустіть три FIO профайли: VM mix (4K randrw), sync-heavy (8K + fsync), послідовний інгест (1M writes).
  3. Для кожного запуску збирайте: p99/p99.9 затримку, CPU (mpstat), vdev wait (zpool iostat), ARC miss% (arcstat), і фізичні vs логічні записи.
  4. Рішення приймайте для кожного класу датасету. Не стандартизуйте один кодек лише тому, що він виграв в одному синтетичному тесті.
  5. Розгортайте поступово з моніторинговими гейтами. Якщо p99.9 пішов у неправильному напрямку — швидко відкатуйте без сорому.

Стиснення — не релігія. Це компроміс. Виміряйте його чесно — і ви отримаєте економію місця та вигоди по продуктивності, які вам обіцяли — без плацебо.

← Попередня
Ubuntu 24.04 «Network is unreachable»: сироватка правди таблиці маршрутизації (і виправлення)
Наступна →
ZFS проти btrfs: знімки, RAID, відновлення — що болить менше?

Залишити коментар