Компресія ZFS lz4: коли вона «безкоштовна», а коли ні

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

Про компресію ZFS compression=lz4 говорять як про універсальний купон: увімкни — і все стане швидше й менше. У продакшені часто буває саме так — до першої проблеми. Підступ у тому, що «безкоштовно» залежить від того, на чому у вас вузьке місце, як сформовані блоки та що ви уявляєте під капотом ZFS.

Цей матеріал написаний з перспективи людини, яка дивилася графіки сховища опівночі, сперечалася з планувальниками CPU і навчилася (на власних помилках), що компресія — це і функція продуктивності, і архітектурне рішення. Ми розглянемо, коли lz4 фактично безкоштовна, коли вона вам коштує і як швидко діагностувати ситуацію без забобонів.

Що насправді означає «безкоштовно» у компресії ZFS

У системах «безкоштовно» ніколи не означає нульової вартості. Це означає, що витрати лягають туди, де у вас є надлишок: вільні цикли CPU, невикористана пропускна здатність пам’яті, запас у конвеєрі або просто менше болю, ніж альтернатива. У компресії ZFS базовий компроміс такий:

Витрачайте CPU, щоб зекономити I/O. Збережений I/O може означати пропускну спроможність диска, IOPS, глибину черги або час очікування на паритетні обчислення й латентність пристрою. Якщо ваше сховище — вузьке місце, компресія може прискорити систему, передаючи менше байтів через найповільнішу ділянку стеку. Якщо ж CPU — ваш обмежувач, компресія може уповільнити систему, додаючи роботи на критичному шляху.

lz4 популярний тому, що його декомпресія надзвичайно швидка й передбачувана, а компресія достатньо швидка, щоб у багатьох реальних системах, залежних від диска, виглядати «безкоштовною». Але ZFS не компресує файл; він компресує блоки (records), і продуктивність залежить від розміру блоку, схеми доступу, ентропії даних і того, як ваше навантаження потрапляє в ARC і на диски.

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

Жарт №1: Компресія — як гоління: коли все добре, відчуваєш гладкість, а коли погано — кровоточиш і прикидаєшся, що це «невеличка подряпина».

Цікаві факти та історичний контекст

Декілька контекстних пунктів, які зазвичай прояснюють рішення про компресію:

  • ZFS compression налаштовується на dataset і онлайн. Ви можете увімкнути його без перезапису існуючих блоків; це застосовується до нових записів.
  • lz4 став дефолтним у багатьох ZFS інсталяціях тому, що він розроблений для швидкості й низької витрати CPU, а не для максимального ступеня стиснення.
  • ZFS зберігає блоки на диску вже стиснутими, і (важливо) ці блоки також можуть зберігатися стиснутими в ARC; декомпресія відбувається за потреби.
  • «Копії» й «паритет» робляться після компресії. Зазвичай ви хочете спочатку стиснути, щоб зберегти менше байтів у mirror/паритеті (точний шар залежить від реалізації, але результат: менше байтів записується).
  • Контрольні суми охоплюють логічні дані. ZFS перевіряє цілісність незалежно від компресії; декомпресія не «на око».
  • Не всі дані стискаються. Шифровані, вже стиснені медіа та багато сучасних колонкових форматів часто майже нестисні на рівні блоків.
  • Recordsize важливіше, ніж думають більшість. Компресія працює на записі; невідповідність між recordsize і схемою I/O може домінувати над продуктивністю.
  • Стиснутий ARC може бути прихованою перемогою. Якщо робочий набір стискається, ARC фактично утримує більше логічних даних на ГБ RAM.
  • Історично gzip-рівні використовували для економії місця коли диски були дорогими і CPU — вільний; сьогодні профіль витрат інший, але звичка лишилася.

Як насправді працює компресія ZFS (важливі частини)

Компресія — це рішення на шляху запису

Компресія ZFS відбувається під час запису блоків. Для dataset з compression=lz4 ZFS намагається стиснути кожен блок і зберігає стиснену версію тільки якщо вона менша за поріг (реалізація специфічна, але думайте «не морочитися, якщо економія мінімальна»). Якщо блок не стискається помітно, він зберігається ненапружено. Це важливо, бо «включена компресія» не означає «все стискається».

Блоки, recordsize і чому інтуїція про «файл» вводить в оману

ZFS — транзакційна файловa система з copy-on-write. Файли представлені як набір блоків (records). Для типової dataset recordsize може бути 128K. Це означає, що послідовні записи можуть збиратися в 128K записи, і кожна 128K запис отримує спробу стиснення. Рандомні I/O можуть порушити цю історію.

Якщо ваш застосунок робить 8K рандомні записи в межах 128K record, ZFS все одно має керувати записом (часто через read-modify-write на якомусь шарі). Компресія тоді може стати частиною більшої проблеми «посилення малих рандомних записів». Це не вина компресії, але це означає додаткові CPU-цикли поверх вже дорогої операції.

ARC зберігає стиснені дані (і це велика річ)

Коли ZFS читає стиснений блок з диска, він може зберегти його в ARC у стиснутому вигляді. Це означає, що та сама RAM кешує більше логічних даних, покращуючи хіт-рейт для стискуваних навантажень. Якщо ви колись бачили систему «чарівно» кращу після увімкнення lz4, часто причина саме в цьому: диск виконує менше роботи, і ARC фактично більший.

Декомпресія зазвичай дешева; вартість компресії залежить від навантаження

Декомпресія lz4 відома своєю швидкістю. У багатьох навантаженнях декомпресія губиться на фоні затримки диска. Компресія під час записів може бути більшою статтею витрат, особливо для систем з великим об’ємом записів, де CPU вже зайнятий контрольними сумами, шифруванням (якщо увімкнено), мережею або потоками застосунку.

Компресія взаємодіє з шифруванням, dedup та спеціальними vdev

Три взаємодії, які варто тримати в голові:

  • Шифрування: compress before encrypt. Шифрування до компресії руйнує стискуваність. Більшість стеків ZFS стискає, а потім шифрує, коли обидва увімкнені, що і бажано.
  • Dedup: dedup хоче ідентичні блоки. Компресія може допомогти або завадити залежно від даних; але dedup сама по собі зазвичай більший ризик у продакшені.
  • Спеціальні vdev (метадані/малі блоки): стиснення малих блоків може змінити мікс I/O і патерни метаданих. Іноді це добре; іноді це перекладає тиск на special vdev.

Коли lz4 фактично безкоштовна (а іноді й швидша)

lz4 здається безкоштовною, коли CPU не є обмежувачем, а I/O — так. Це часто трапляється, бо сховище повільне, латентність агресивна, і багато навантажень передають надмірну кількість повторюваних байтів.

1) Навантаження, обмежені сховищем: повільні диски, завантажені масиви, висока латентність

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

Я бачив випадок, коли шумний аналітичний вузол перестав постійно відставати і став «переважно нормальним» просто після включення lz4 на scratch dataset. Не тому, що lz4 магічний, а тому, що scratch-дані були дуже повторювані й диски були вузьким місцем.

2) Стискувані дані: текст, логи, JSON, CSV, VM-образи з нулями

Компресія виграє, коли в даних є патерни: повторювані рядки, пробіли, мітки часу, нульові сторінки і загалом низька ентропія. Логи й текстові формати — головні кандидати. VM-образи часто містять великі регіони нулів або мало використовуваного простору, який добре стискається навіть якщо гість вважає його «виділеним».

У таких випадках компресія може зменшити write amplification і покращити щільність кешування при читанні. Ваш ARC hit ratio може покращитися, бо кешовані блоки представляють більше логічних даних.

3) Обмеження мережею або реплікацією: send/receive і бекапи

Якщо ви реплікуєте через zfs send або відправляєте сніпшоти мережею, зберігання блоків у стиснутому вигляді може зменшити передані байти (залежно від прапорів і реалізації send). Навіть коли send-потік не містить «сирі стиснуті блоки» у вашій конфігурації, загальний ефект часто зводиться до меншого обсягу даних, бо менше байтів записується спочатку.

4) Коли «CPU дешевий» — це справді правда

На багатьох серверах CPU недовантажений більшу частину дня. Сховище ж навантажене. Якщо ви працюєте на сучасному CPU з запасом, lz4 зазвичай хороший компроміс: витрачайте трохи CPU, щоб зекономити дорогий I/O час і знос пристроїв.

5) Ефективність ARC і покращення хвостової латентності

Компресія може покращити довгий хвіст латентності. Коли ARC тримає більше, більше читань обслуговуються з пам’яті. Ви не лише зменшуєте середню латентність; ви зменшуєте ті «іноді потрапляє на диск і все зависає» моменти, що псують p99. Це тонко, але важливо для експлуатації.

Коли lz4 не є безкоштовною (і може боляче вдарити)

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

1) Системи, обмежені CPU: високий PPS мережі, TLS, завантажені бази, важкий контроль сум

Якщо ваша система вже насичена CPU — потоки застосунку працюють на межі, є сплески переривань, шифрування перевантажує, патерни з великою кількістю контрольних сум — додавання компресії може підштовхнути вас за край. Симптом часто такий: диски не завантажені, але пропускна здатність погана і CPU високий.

На деяких вузлах можна спостерігати, як один датасет під час масового запису перетворює систему з «нормальної» на «таємниче повільну». Вимкніть компресію — і пропускна здатність зростає, не тому що сховище стало швидше, а тому що CPU перестав робити додаткову роботу.

2) Нестискувані дані: вже стиснене медіа, зашифровані бінарні масиви

JPEG, H.264/H.265 відео, MP3/AAC, багато сучасних бекап-архівів і зашифровані дані часто виглядають випадковими для компресора. ZFS спробує стиснути блоки і часто збереже їх ненапружено. Сама спроба має накладні витрати.

lz4 швидкий, тож «податок на спробу» зазвичай малий, але при високій швидкості запису він реальний. Якщо ви пишете десятки ГБ/с на швидкі NVMe, навіть невелика накладна на блок може накопичуватися.

3) Малі рандомні записи і невідповідність recordsize

Якщо у вас навантаження з 4K–16K рандомними записами у великі записи, ви, можливо, вже платите податок read-modify-write. Компресія додає CPU і може ускладнити те, як швидко ZFS може зібрати, просумувати контрольні суми і зафіксувати transaction groups.

Виправлення може бути не «вимкнути компресію», а «встановити відповідний recordsize або volblocksize для конкретного dataset/zvol», бо ви втрачаєте значно більше від посилення, ніж втратите через lz4.

4) Коли ви неправильно читаєте compressratio і оптимізуєте не те

compressratio — привабливе число. Люди женуться за ним як за KPI. Але це не прямий показник вигод по всім шляхам, і він може вводити в оману при змішаних навантаженнях, сніпшотах і блоках, записаних до ввімкнення компресії.

5) Чутливість до латентності на гарячому шляху при write-heavy навантаженнях

Деякі системи цінують затримку запису більше за пропускну здатність: синхронні записи, застосунки з fsync, певні конфігурації БД. Компресія додає CPU-роботу перед фіксацією блоків. Якщо ви вже тонко налаштовані, lz4 може підвищити p99 записів.

Жарт №2: Єдина річ більш стиснута, ніж ваші ZFS-блоки, — це ваш графік під час інциденту.

Рекомендації по навантаженнях: бази даних, VM, логи, бекапи, медіа

Бази даних (PostgreSQL/MySQL та інші)

Бази даних — це місце, де люди нервують, і іноді вони мають рацію. Результат залежить від патерну I/O, розміру блоку і чи вже CPU є вузьким місцем.

  • Читано-орієнтовані при обмеженні зі сторони сховища: lz4 часто допомагає. Менше read I/O, краща щільність ARC.
  • Write-heavy, CPU-обмежені: lz4 може нашкодити. Слідкуйте за CPU, поведінкою txg commit і латентністю.
  • Рандомний I/O: встановіть recordsize розумно (часто 16K для деяких DB-файлів) і не використовуйте один dataset для всього.

Операційна нотатка: багато сторінок бази частково стискуються (повторювані структури, нулі). lz4 може дати скромні коефіцієнти, але значущу економію I/O.

VM-образи та слої контейнерів

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

Головна підводка — volblocksize для zvols: оберіть його відповідно до патернів гостьового I/O. Компресія не врятує вас від патологічного вибору розміру блоку.

Логи, метрики, трейси

Текстова телеметрія — найпростіший вибір: lz4 зазвичай виграє. Ви здебільшого write-heavy, і дані добре стискаються. Це дає зменшення зносу диска і швидший replay/читання для свіжих даних.

Бекапи та архіви

Якщо ви зберігаєте бекапи, які вже стиснені (багато хто так робить), lz4 не дасть великого ефекту. Але включення lz4 не є безглуздим: ZFS збереже нестиснені блоки ненапружено, і накладні витрати зазвичай малі. Проте при екстремально високих швидкостях ingest (вікно бекапу на NVMe) варто бенчмаркувати.

Медіа-репозиторії

Для фото/відеоколекцій lz4 рідко значно економить місце. Цінність зазвичай у єдиній політиці («компресія на всьому»), і періодичних виграшах для мініатюр, метаданих, субтитрів чи сайдкар-файлів. Якщо ваші медіа-сервери обмежені по CPU і віддають високі потоки, розгляньте вимкнення компресії для таких dataset.

Три корпоративні міні-історії

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

Команда зберігання розгорнула новий ZFS-backed NFS-tier для змішаного навантаження: артефакти збірки, логи і pipeline експорту бази. Хтось зробив розумне на перший погляд припущення: «lz4 безкоштовна; увімкніть скрізь». Це проходило рев’ю змін як низькоризикова дія — зрештою, це властивість dataset.

Через два тижні під час квартального закриття pipeline експорту почав пропускати вікно. Спрацювали алерти на затримки застосунку, а не на сховище. Перші реагувальники подивилися на пул: диски не були завантажені, латентність не була страшною, ARC — здоровий. Не було запаху проблем зі сховищем.

Але CPU на storage head був високим і стрибкоподібним, особливо в kernel time. Записуване навантаження було переважно нестискуваним (вже стиснені експорти), але ZFS все одно намагався стиснути кожен запис. Гірше, pipeline писав великі послідовні потоки на високій швидкості, що підкидувало спроби компресії на гарячий шлях саме тоді, коли CPU вже обслуговував NFS-потоки й контрольні суми.

Виправлення було не героїчним: відключили компресію на export dataset, залишили її для логів та артефактів. Вікно повернулося. Урок не був «компресія погана». Це було «компресія — це рішення для конкретного навантаження, а не віра». І так, постмортем містив фразу «безкоштовно, поки не стане не безкоштовним», що одразу вказало на інженерів авторів.

Міні-історія №2: Оптимізація, що обернулась проти

Команда платформ вирішила зменшити приріст зберігання. У них був пул, що виглядав здоровим, але графіки заповнення росли. Прагматичний спринт вирішив посилити компресію: перейти з lz4 на gzip-9 на кількох dataset, бо «кращий коефіцієнт = менше витрат». Зміну зробили після невеликого тесту на спокійному стенді.

У продакшені місце дійсно заощадили — без дискусій. Але через тиждень розробники почали скаржитися на періодичні уповільнення: CI джоби таймаутилися, витягання контейнерів гальмувало, «випадкові» сплески латентності. Графіки показали підвищення CPU, особливо під час пікових операцій запису і активності сніпшотів. Пул не вичерпав IOPS; він вичерпав терпіння.

Провал приходив з двох джерел. По-перше, gzip-9 значно важчий за lz4, особливо при записах. По-друге, навантаження не було холодними даними. Це були активні дані з частими записами і читаннями, тож CPU-платіж був весь день, а не одноразово. В результаті: вони заощадили диск, але заплатили продуктивністю і операційним шумом.

Команда відкотилася до lz4 для «гарячих» dataset, залишила сильнішу компресію тільки для холодних/архівних dataset, де латентність не важлива, і задокументувала правило: оптимізуйте під реальний вузький місце. «Диск дорогий» — правда; «CPU безкоштовний» — не універсальний закон.

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

Інша організація вела флот ZFS з нудною політикою, що ніколи не потрапляла в презентації: у кожного dataset були явні властивості (compression, recordsize, atime, logbias де релевантно), і кожна велика зміна супроводжувалася бенчмарком «до/після» з production-подібними I/O патернами. Це не було модно; це було послідовно.

Одного дня розгорнули новий застосунок, що писав telemetry з високою частотою. Усі очікували, що все буде OK — текст же добре стискається, правда? Але інженери зробили нудну річ: створили окремий dataset, встановили compression=lz4, підходящий recordsize і провели контрольований тест навантаження з iostat та профілюванням CPU.

Тест виявив сюрприз: не компресор, а синхронні записи і неправильно налаштований варіант монтирования клієнта домінували над латентністю. Оскільки у них були базові показники, вони не витратили два дні на дискусії про компресію. Виправили поведінку sync де це було релевантно, залишили lz4 і випустили застосунок без драм.

У рев’ю інциденту компресія майже не згадувалася — саме в цьому сенс. Практика, що врятувала день, була дисципліноване вимірювання. У продакшені нудне перемагає розумне, коли дзвонить пейджер.

Практичні завдання: команди, виводи, інтерпретація

Нижче реальні завдання, які ви можете виконати на системі ZFS. Мета не в запам’ятовуванні команд; а у зв’язуванні спостережень з рішеннями.

Завдання 1: Перевірити налаштування компресії dataset

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

Інтерпретація: Підтверджує, що встановлено і чи успадковано. Якщо ваш dataset успадковує від батька, про якого ви забули, ви гнатиметеся за привидами.

Завдання 2: Знайти dataset, що відрізняються від політики

cr0x@server:~$ zfs get -r -s local,inherited compression tank | sed -n '1,20p'
NAME              PROPERTY     VALUE     SOURCE
tank              compression  lz4       local
tank/db           compression  lz4       inherited from tank
tank/media        compression  off       local
tank/backups      compression  lz4       inherited from tank
tank/exports      compression  off       local

Інтерпретація: Ви хочете свідомих винятків (наприклад, media або вже стиснені експорти), а не випадкових.

Завдання 3: Подивитись ефективність стиснення по dataset

cr0x@server:~$ zfs get -o name,used,logicalused,compressratio -r tank | sed -n '1,12p'
NAME         USED   LOGICALUSED  RATIO
tank         1.20T  1.85T        1.54x
tank/db      420G   560G         1.33x
tank/logs    180G   620G         3.44x
tank/media   300G   305G         1.02x

Інтерпретація: logicalused проти used підказує реальні заощадження. Media майже не стискається; логи сильно стискаються.

Завдання 4: Зрозуміти, що існуючі дані не «ретрокомпресуються»

cr0x@server:~$ zfs set compression=lz4 tank/olddata
cr0x@server:~$ zfs get -o name,compressratio tank/olddata
NAME          RATIO
tank/olddata  1.01x

Інтерпретація: Якщо ви змінили компресію на dataset, заповнений старими блоками, коефіцієнт не зміниться доки не відбудуться нові записи.

Завдання 5: Виміряти I/O та латентність на рівні пулу

cr0x@server:~$ zpool iostat -v 1 5
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
tank        6.30T  2.70T    820   1300   110M   240M
  raidz2-0  6.30T  2.70T    820   1300   110M   240M
    sda         -      -    110    180  14.0M  30.1M
    sdb         -      -    105    175  13.8M  29.8M
    sdc         -      -    115    185  14.2M  30.4M
    sdd         -      -    108    182  14.0M  30.2M
    sde         -      -    112    178  14.1M  30.0M

Інтерпретація: Якщо диски завантажені й пропускна здатність висока, компресія може допомогти, зменшивши байти. Якщо диски прості, але продуктивність погана, вузьке місце — в іншому місці (CPU, sync-записи, мережа, застосунок).

Завдання 6: Спостерігати I/O по dataset з ZFS iostat

cr0x@server:~$ zfs iostat -r -v tank 1 5
                 capacity     operations     bandwidth
dataset          used  avail   read  write   read  write
---------------  ----  -----  -----  -----  -----  -----
tank             1.20T 2.70T    420    610  52.0M  90.0M
  tank/db         420G 2.70T    210    240  18.0M  22.0M
  tank/logs       180G 2.70T     20    280   1.2M  38.0M
  tank/media      300G 2.70T    180     40  32.0M   6.0M

Інтерпретація: Визначте, хто насправді створює I/O. Потім приймайте рішення про компресію по dataset, замість абстрактних суперечок.

Завдання 7: Перевірити recordsize і чому це важливо

cr0x@server:~$ zfs get -o name,property,value recordsize tank/db tank/logs tank/media
NAME        PROPERTY    VALUE
tank/db     recordsize  16K
tank/logs   recordsize  128K
tank/media  recordsize  1M

Інтерпретація: DB dataset використовує менші записи (часто краще для рандомного I/O). Логи за замовчуванням 128K. Media використовує 1M для переваги послідовної пропускної здатності. Компресія працює на рівні record.

Завдання 8: Перевірити volblocksize з для VM-дисків

cr0x@server:~$ zfs get -o name,property,value volblocksize,compression tank/vmdata/vm-101
NAME                 PROPERTY      VALUE
tank/vmdata/vm-101   volblocksize  16K
tank/vmdata/vm-101   compression   lz4

Інтерпретація: Volblocksize для zvol встановлюється при створенні і формує I/O. Виграш від компресії обмежується, якщо розмір блоку створює ампліфікацію.

Завдання 9: Спостерігати насичення CPU під час write-heavy подій

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

12:00:01 PM  CPU   %usr  %sys  %iowait  %idle
12:00:01 PM  all  62.10  22.40    0.80   14.70
12:00:02 PM  all  68.90  24.10    0.30    6.70

Інтерпретація: Низький iowait при низькому idle часто означає CPU-обмеження. Якщо продуктивність погана, але iowait близький до нуля, відключення компресії може допомогти — якщо навантаження нестискуване або мета — пропускна здатність запису.

Завдання 10: Оцінити стискуваність швидким зразком (безпечно)

cr0x@server:~$ dd if=/tank/logs/app.log bs=1M count=256 2>/dev/null | lz4 -q -c | wc -c
41234567

Інтерпретація: Порівняйте з оригінальними байтами (256 MiB). Якщо стиснені байти значно менші, lz4, ймовірно, варто увімкнути. Це груба перевірка; ZFS працює на рівні record і може відрізнятися.

Завдання 11: Підтвердити, що реально стиснено на диску (опосередковано)

cr0x@server:~$ zdb -DD tank/logs | sed -n '1,20p'
DDT-sha256-zap-duplicate: 0 entries, size 0 on disk, 0 in core

Dataset tank/logs [ZPL], ID 54, cr_txg 12345, ...
Blocks  Logical   Physical  compress%  copies%  dedup%  checksum
------  -------   --------  ---------  -------  ------  --------
  ...   620G      180G      70.9%      100.0%   100.0%  100.0%

Інтерпретація: Такий вивід варіюється за платформою, але суть: облік фізичних vs логічних блоків. Використовуйте обережно; інструмент сильний, але гострий.

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

cr0x@server:~$ zfs create -o compression=off tank/bench_off
cr0x@server:~$ zfs create -o compression=lz4 tank/bench_lz4
cr0x@server:~$ fio --name=write_test --directory=/tank/bench_off --rw=write --bs=128k --size=8g --numjobs=4 --iodepth=16 --direct=1
...
WRITE: bw=820MiB/s (860MB/s), iops=6560, runt=10000msec
cr0x@server:~$ fio --name=write_test --directory=/tank/bench_lz4 --rw=write --bs=128k --size=8g --numjobs=4 --iodepth=16 --direct=1
...
WRITE: bw=980MiB/s (1027MB/s), iops=7840, runt=10000msec

Інтерпретація: Якщо lz4 покращує пропускну здатність, ймовірно ви були I/O-обмежені і дані стискаються. Якщо вона зменшує пропускну здатність при незавантажених дисках, ви платите CPU за малий виграш.

Завдання 13: Перевірити статистику ARC для ефектів кешування (Linux)

cr0x@server:~$ arcstat 1 5
    time  read  miss  miss%  dmis  dm%  pmis  pm%  mmis  mm%  arcsz     c
12:01:01  1200   180     15    20   2%   160  13%     0   0%   64G   96G
12:01:02  1180   140     12    18   2%   122  10%     0   0%   64G   96G

Інтерпретація: Пониження miss rate після увімкнення компресії може бути реальною перемогою. Компресія може зробити ARC «більшим» для стискуваних dataset.

Завдання 14: Перевірити, чи dataset робить sync-записи (і чому це важливо)

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

Інтерпретація: Якщо ваша проблема — latency синхронних записів, компресія може не бути основним важелем. Стан SLOG, налаштування sync і поведінка fsync застосунку можуть домінувати.

Швидкий план діагностики

Ось послідовність дій, яку я використовую, коли хтось каже «ZFS стало повільним після включення lz4» або «lz4 не допоміг, як обіцяли в інтернеті». Мета — швидко знайти вузьке місце, а не сперечатися про ідеологію.

По-перше: Визначте клас вузького місця (CPU vs I/O vs джерело латентності)

  1. CPU насичений? Перевірте CPU по всій системі і чергу завдань. Якщо CPU зашкалює і iowait низький — ви не диск-обмежені.
  2. Диски насичені? Подивіться на використання пулу/пристроїв, латентність, чергування. Якщо диски завантажені, компресія може допомогти (якщо дані стискаються).
  3. Чи це latency від синхронних записів? Якщо навантаження fsync-heavy, перевірте SLOG, sync-настройки і розподіл латентності.

По-друге: Підтвердіть стискуваність і звідки виходять байти

  1. Перевірте logicalused vs used та compressratio для ураженого dataset.
  2. Визначте топ- I/O dataset за допомогою zfs iostat або метрик по dataset.
  3. Перевірте тип даних (логи vs медіа vs зашифровані блоби). Якщо дані нестискувані — не чекайте див.

По-третє: Валідність розміру блоку і патерну доступу

  1. Перевірте recordsize (файлові системи) або volblocksize (zvols). Невідповідність призводить до ампліфікації, що затьмарює ефекти компресії.
  2. Визначте, чи I/O рандомний або послідовний за допомогою знань навантаження або відтворення fio. Рандомні малі записи + великі record — класична пастка.

По-четверте: Прийміть найменшу безпечну зміну

  1. Якщо CPU-обмеження і дані нестискувані: вимкніть компресію для цього dataset, а не для всього пулу.
  2. Якщо I/O-обмеження і дані стискаються: залиште lz4, розгляньте покращення recordsize і перевірте поведінку ARC.
  3. Якщо результати неоднозначні: виділіть тестовий dataset і проведіть бенчмарк з репрезентативним I/O.

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

Помилка 1: Увімкнули lz4 і очікували, що старі дані зменшаться

Симптом: Ви встановили compression=lz4, але compressratio залишається близько 1.0x і місце не зменшилося.

Чому: Існуючі блоки не переписуються. ZFS стискає нові записи.

Виправлення: Перепишіть дані (наприклад, zfs send | zfs receive в новий dataset або копіювання з увагою). Робіть це свідомо; не робіть «rm -rf і recopy» у продакшені без плану.

Помилка 2: Використовувати compressratio як метрику продуктивності

Симптом: Dataset показує 2.0x ratio, але продуктивність гірша; або ratio 1.1x, але продуктивність покращується.

Чому: Ratio вимірює облік зберігання, а не латентність/пропускну здатність. Ефекти ARC, патерни I/O і CPU можуть домінувати.

Виправлення: Вимірюйте те, що вам важливо: пропускну здатність, p95/p99 латентність, CPU, iowait, завантаження пристроїв, ARC hit/miss.

Помилка 3: Припускати, що нестискувані дані не мають витрат

Симптом: CPU зростає під час великих записів медіа/архівів; диск не завантажений; пропускна здатність трохи падає.

Чому: ZFS все одно пробує стиснути блоки перед тим, як вирішити, що це не варто робити.

Виправлення: Вимкніть компресію на цьому dataset, якщо CPU цінний і навантаження доведене нестискуване. Або залиште її для простоти політики, якщо накладні витрати прийнятні.

Помилка 4: Один dataset для всього

Симптом: Ви не можете налаштувати recordsize, sync або compression без шкоди для частини навантаження.

Чому: Властивості dataset — це межа налаштування. Змішування VM, БД, логів і медіа в одному dataset змушує робити компроміс.

Виправлення: Розділіть dataset за класом навантаження і явно встановіть властивості.

Помилка 5: Ігнорувати recordsize/volblocksize і звинувачувати компресію

Симптом: Рандомне write-навантаження повільне; вмикання/вимкнення компресії мало змінює; латентні сплески лишаються.

Чому: Невідповідність розміру блоку призводить до read-modify-write і фрагментації, що перекриває ефект компресії.

Виправлення: Використовуйте відповідні блок-розміри (часто менші для БД, адекватні для VM). Бенчмаркуйте і перевіряйте.

Помилка 6: Перехід на важку компресію для «гарячих» даних

Симптом: CPU росте, p99 латентності стає жорстким, користувачі кажуть «все гальмує», але місце зекономлено.

Чому: gzip-рівні — не lz4. Ви обміняли диск на CPU на гарячому шляху.

Виправлення: Використовуйте lz4 для гарячих даних. Потужнішу компресію застосовуйте лише для холодних/архівних dataset, де латентність не важлива.

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

Чекліст A: Вирішити, чи вмикати lz4 для dataset

  1. Ідентифікуйте тип навантаження (логи, DB, VM, медіа, бекапи, зашифровані блоби).
  2. Перевірте поточне вузьке місце (CPU vs диск vs мережа vs sync-латентність).
  3. Оцініть стискуваність (зразок даних, попередні коефіцієнти, знання).
  4. Підтвердіть розмір блоку: recordsize для файлів, volblocksize для zvols.
  5. Увімкніть compression=lz4 спочатку на тестовому dataset, якщо ризик значущий.
  6. Бенчмаркуйте з репрезентативним I/O і вимірюйте перцентилі латентності, а не лише пропускну здатність.
  7. Розгорніть у продакшені з явними винятками і моніторингом.

Чекліст B: Безпечне розгортання lz4 у продакшені

  1. Задокументуйте поточні властивості dataset: zfs get -r all (відфільтруйте при потребі).
  2. Виберіть канарний dataset і увімкніть там lz4 першим.
  3. Моніторте CPU, pool iostat, ARC miss rate і p95/p99 латентність застосунку протягом принаймні одного бізнес-циклу.
  4. Розширюйте на інші dataset за класом навантаження (логи першими — зазвичай без драми).
  5. Тримайте явні винятки (media, export blobs) замість глобального відключення.
  6. Майте план відкату: знайте команду і очікувану поведінку («впливає тільки на нові записи»).

Чекліст C: Якщо продуктивність погіршилася після ввімкнення lz4

  1. Підтвердіть часову кореляцію (чи погіршилася продуктивність саме коли почалися нові записи з компресією?).
  2. Перевірте насичення CPU; порівняйте з базою до зміни, якщо вона є.
  3. Перевірте стискуваність dataset: якщо ratio ≈ 1.0x, lz4, ймовірно, робить роботу даремно.
  4. Визначте, який dataset гарячий; не вимикайте компресію всюди.
  5. Перевірте recordsize/volblocksize; виправте велику проблему першою.
  6. Вимкніть компресію на ураженому dataset, якщо CPU-обмеження і нестискуваність доведені, і повторно протестуйте.

FAQ

1) Чи слід увімкнути compression=lz4 на кожному dataset ZFS?

Зазвичай так для загальних dataset, але «кожному» має включати свідомі винятки. Медіа-репозиторії і вже стиснені експортні бінарні файли часто кандидатура на compression=off, якщо CPU обмежений.

2) Чому lz4 іноді робить усе швидше?

Тому що ви зменшуєте I/O: менше байтів на запис/читання означає менше операцій диска і кращу щільність кешу. Якщо ви сховище-обмежені, витрачати трохи CPU, щоб зменшити I/O, може покращити пропускну здатність і хвости латентності.

3) Чому lz4 іноді гальмує?

Якщо ви обмежені CPU, компресія додає роботу в критичний шлях. Також, якщо дані нестискувані, ви платите за спробу без економії I/O.

4) Чи стискаються існуючі файли при увімкненні компресії?

Ні. Це застосовується до нових записаних блоків. Існуючі блоки залишаються як є, доки не будуть переписані.

5) Чи є compressratio найкращим показником успіху?

Це підказка, але не остаточна істина. Використовуйте її разом з латентністю застосунку, завантаженням пулу/пристроїв, навантаженням CPU і поведінкою ARC.

6) А що щодо compression=zstd?

zstd може дати кращі коефіцієнти при прийнятних швидкостях залежно від рівня і підтримки платформи. Але тут йшлося про «дефолтну перемогу» lz4. Якщо розглядаєте zstd — бенчмаркуйте з production-подібним I/O і розглядайте рівень компресії як налаштування, а не перемикач.

7) Чи допомагає компресія ARC?

Так, часто. Стиснені блоки можуть кешуватися в стисненому вигляді, дозволяючи ARC зберігати більше логічних даних на одиницю RAM — якщо дані стискаються.

8) Чи зменшить компресія знос SSD?

Може, зменшуючи кількість фізичних байтів записаних при стискуваності даних. Реальний вплив залежить від навантаження і наскільки стискується потік записів.

9) Чи варто вимикати компресію для зашифрованих dataset?

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

10) Якщо lz4 такий хороший, чому не використовувати gzip скрізь для економії місця?

Тому що на гарячому шляху важливий CPU і латентність. gzip добре підходить для холодних архівів; для активних навантажень він — збирач податків. Використовуйте інструмент під температуру dataset.

Висновок

Компресія ZFS lz4 «безкоштовна», коли вона обмінює надлишкові CPU-цикли на дефіцитний I/O і ваші дані дійсно стискаються. Вона не безкоштовна, коли CPU вже є обмежувачем, дані нестискувані або коли розмір блоку та патерни доступу — справжні злочинці, які ховаються за компресією.

Переможний підхід у продакшені — це нудний і повторюваний процес: розділяйте dataset за навантаженням, ставте властивості свідомо, вимірюйте правильні сигнали вузького місця і тримайте невеликий план діагностики, який можна виконати о 2-й ночі. Коли ви робите так, lz4 перестає бути фольклором і стає тим, чим він є насправді: практичним важелем, який можна тягнути з упевненістю.

← Попередня
Debian 13: PostgreSQL шторм підключень — pooler проти налаштування: що справді працює (випадок №37)
Наступна →
Майнерські версії: найкращі та найгірші ідеї, які постачала індустрія

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