Налаштування ZFS для SSD-пулів: уникнення краху через збір сміття

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

Першою ознакою ніколи не буває «пул помирає». Це повідомлення в Slack: «Чому база даних раптом зависає?»
Потім панель моніторингу: p99 латентність перетворюється на модерністську абстракцію. CPU в порядку. Мережа в порядку. ZFS каже «ONLINE».
І все ж кожні кілька хвилин ваш SSD-only пул ніби забуває, як писати.

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

Як виглядає «колапс через збір сміття» на ZFS

SSD не перезаписують сторінки на місці. Вони пишуть у свіжі сторінки й пізніше очищують старі. Та ця очистка — garbage collection (GC),
зазвичай прихована прошивкою контролера та резервною областю. ZFS теж не перезаписує на місці — це copy-on-write.
Отже маємо два рівні «запиши десь інде, почисть пізніше», накладені один на один. Коли умови співпадають (або не співпадають),
ці рівні синхронізуються у періодичне пекло.

У продакшні ви побачите:

  • Скачки латентності без очевидного вузького місця в CPU. Система «байдужа», а сховище плавиться.
  • Колапс записних IOPS після тривалих випадкових записів, особливо якщо пул високо заповнений.
  • Раптові зупинки для sync-важких навантажень (бази даних, NFS, гіпервізори), навіть якщо «це NVMe».
  • zpool iostat показує величезні wait, хоча пропускна здатність виглядає непримітно.
  • Піки корелюють з комітами transaction group (TXG) або періодичними сплесками дрібних записів.

Ключ: GC-collapse — це не «ZFS повільний». Це системний петльовий зворотний зв’язок: ZFS генерує патерн записів; прошивка SSD реагує;
латентність росте; ZFS чергує більше; прошивка має менше вільного часу; латентність знову росте. Це як затор, в якому всі одночасно
вирішили перестроїтися.

Жарт №1: SSD garbage collection — це як прибирати квартиру, засовуючи все в одну шафу — доки шафа не почне брати плату за зберігання.

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

  • TRIM не завжди був само собою зрозумілим. Ранні розгортання SSD часто працювали без ефективного TRIM/discard, тож диски «забували», які сторінки вільні, і продуктивність погіршувалась з часом.
  • ZFS copy-on-write передував поширенню SSD. ZFS розроблявся для цілісності даних і великих сховищ; поведінка флеш-пам’яті стала предметом налаштувань пізніше, коли флеш став за замовчуванням.
  • Write amplification — це подвійний податок. SSD вже перезаписують внутрішні структури; CoW-файлові системи можуть збільшувати внутрішні переміщення, якщо робоче навантаження фрагментує вільний простір.
  • Enterprise SSD — це не просто «швидше». Зазвичай вони постачаються з більшою overprovisioning, кращою поведінкою у steady-state і прошивкою, налаштованою під змішані навантаження.
  • Захист від втрати живлення (PLP) змінив історію SLOG. Коли ви покладаєтесь на sync-семантику, накопичувачі з конденсаторами стають вимогою конструкції, а не розкішшю.
  • Помилки вирівнювання 4K — древні і все ще трапляються. Індустрія перейшла від 512-байтних секторів до 4K фізичних, і невірне вирівнювання досі тихо руйнує продуктивність.
  • NVMe вирішив проблему черг, а не фізику. Глибокі черги і низькі оверхеди допомагають, але блоки стирання флеш і GC все ще існують; стрибки латентності реальні.
  • Стиснення ZFS стало інструментом продуктивності на SSD. З LZ4 менше NAND для запису може означати більшу пропускну здатність і менший тиск на GC, а не лише економію місця.
  • Special vdev — сучасна «зброя з подвійною кромкою». Вони можуть бути чудовими для метаданих/дрібних блоків, але домен відмови і помилки у розмірах повалили не один «швидкий» пул.

Ментальна модель, яка справді прогнозує збої

1) ZFS записує в transaction groups, а не в такт вашого застосунку

Застосунки роблять записи. ZFS збирає брудні дані в пам’яті (ARC) і скидає їх транзакційними групами (TXG).
Ці коміти мають сплесковий характер. Сплескові записи нормально для SSD доки у SSD вистачає чистих сторінок і він не мусить запускати GC
саме в момент, коли ви його навантажуєте. Тоді сплеск перетворюється на stall.

2) SSD мають «steady-state», якого маркетинг не друкує

Продуктивність «з коробки» — це не steady-state. Після заповнення та перезаписів write amplification зростає і
тривала випадкова записна продуктивність може впасти драматично. Пул при 80–90% заповнення — класичний сценарій: менше вільного простору означає
менше простих варіантів для алокатора ZFS і FTL SSD.

3) Sync-записи — найкращий друг латентності (на жаль)

Якщо ваше навантаження вимагає sync-записів, ZFS має підтвердити збереження на стійкому сховищі перед відправленням підтвердження.
Без окремого лог-пристрою (SLOG), який може приймати низьколатентні записи безпечно, основні пристрої пулу отримують удар.
Якщо ці пристрої одночасно зайняті GC, ваш p99 стає вашим іміджем.

4) Фрагментація — це не лише «файли розкидані»

У ZFS фрагментація стосується сегментації вільного простору і патернів алокації блоків. На SSD це поєднується з власним мапінгом FTL.
Висока фрагментація ускладнює запис великих суцільних сегментів; SSD змушений робити більше внутрішнього копіювання під час GC.
Результат класичний: «але це SSD, чому послідовний запис лише 150 MB/s?»

5) Стиснення може бути елементом налаштування SSD

Стиснення — не лише для економії місця. Якщо ви стискаєте, то записуєте менше байтів, що може знизити write amplification.
На сучасних CPU LZ4 часто дає чистий приріст пропускної здатності і латентності. Виняток — вже стиснені дані або коли CPU є вузьким місцем.

Парафразована ідея від Werner Vogels (CTO Amazon): «Усе ламається весь час — проектуйте так, щоб витримувати це.»
ZFS на SSD — саме про це: припускайте, що є стрибки латентності, і проектуйте навколо них.

Швидкий план діагностики (перший/другий/третій)

Перший: підтвердіть, що симптом — затримка сховища, а не CPU чи мережа

  • Перевірте розклад латентності застосунку (час коміту БД, час fsync, латентність диска гостьової VM).
  • Перевірте системне завантаження vs iowait: високе навантаження з низьким використанням CPU часто означає блокування на IO.
  • Підтвердіть, що чекають на ZFS, а не на віддалений мережевий монтувальний ресурс чи окрему насичену чергу NIC.

Другий: визначте, чи проблема в sync, фрагментації чи steady-state пристрою

  • Якщо зупинки збігаються з fsync/commit: підозрюйте шлях sync-записів (SLOG, налаштування sync, PLP).
  • Якщо продуктивність погіршується зі заповненням пулу: підозрюйте вільний простір + фрагментацію і тиск GC на SSD.
  • Якщо «випадкові записи колапсують через хвилини»: підозрюйте steady-state SSD і недостатнє overprovisioning/TRIM.

Третій: ізолюйте, який шар колаборує

  • Шар ZFS: таймінги TXG, ліміти брудних даних, mismatch recordsize/volblocksize, насичення special vdev.
  • Пристрій: firmware GC, thermal throttling, помилки носія, насичення внутрішніх черг.
  • Топологія: занадто широкий RAIDZ на SSD для вашого профілю IOPS, або вузьке вузьке місце на одному vdev.

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

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

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

cr0x@server:~$ zpool status -v ssdpool
  pool: ssdpool
 state: ONLINE
  scan: scrub repaired 0B in 00:12:19 with 0 errors on Sun Dec 22 03:10:11 2025
config:

        NAME                        STATE     READ WRITE CKSUM
        ssdpool                     ONLINE       0     0     0
          mirror-0                  ONLINE       0     0     0
            nvme-SAMSUNG_MZVLB1T0HBLR-00000  ONLINE       0     0     0
            nvme-SAMSUNG_MZVLB1T0HBLR-00001  ONLINE       0     0     0

errors: No known data errors

Що це означає: Якщо ви маєте degraded або resilvering, дані про продуктивність забруднені. Mirrors поводяться інакше ніж RAIDZ.

Рішення: Не налаштовуйте, поки пул не стабільний і scrubs чисті. Якщо топологія RAIDZ і навантаження sync-random write важке, перегляньте дизайн.

Завдання 2: Слідкуйте в реальному часі за латентністю і чергуванням по vdev

cr0x@server:~$ zpool iostat -v ssdpool 1
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
ssdpool                      620G   280G    120   1800  12.3M   210M
  mirror-0                   620G   280G    120   1800  12.3M   210M
    nvme0n1                     -      -     60    900  6.1M   105M
    nvme1n1                     -      -     60    900  6.2M   105M
--------------------------  -----  -----  -----  -----  -----  -----

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

Рішення: Якщо один vdev відстає або нерівномірний, підозрюйте прошивку/термічні проблеми або некоректний шлях.

Завдання 3: Перевірте детальну латентність по пристрою (OpenZFS на Linux)

cr0x@server:~$ cat /proc/spl/kstat/zfs/vdev_queue | head -n 25
nthreads                            4
vdev_queue_max_active               1000
vdev_queue_min_active               1
vdev_queue_max_pending              1000
vdev_queue_min_pending              1
vdev_queue_max_threads              8
vdev_queue_min_threads              1
vdev_queue_agg_lim                  131072
vdev_queue_read_gap_limit           32768
vdev_queue_write_gap_limit          65536
...

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

Рішення: Не починайте з крутіння внутрішніх черг. Спочатку підтвердіть, що ви не викликаєте GC-collapse через простір, sync і розмір блоків.

Завдання 4: Підтвердіть стан autotrim і ввімкніть його за потреби

cr0x@server:~$ zpool get autotrim ssdpool
NAME     PROPERTY  VALUE     SOURCE
ssdpool  autotrim  off       default

Що це означає: Якщо autotrim вимкнено, звільнені блоки можуть не повідомлятися SSD вчасно. Деякі диски впораються; інші повільно деградують у steady-state.

Рішення: Для SSD-only пулів вмикайте autotrim, якщо тільки у вас немає відомих проблем з прошивкою/queued TRIM.

cr0x@server:~$ sudo zpool set autotrim=on ssdpool

Завдання 5: Перевірте, чи discard/TRIM дійсно доходить до пристрою (Linux)

cr0x@server:~$ lsblk -D
NAME        DISC-ALN DISC-GRAN DISC-MAX DISC-ZERO
nvme0n1            0      4K       2G         0
nvme1n1            0      4K       2G         0

Що це означає: DISC-GRAN показує гранулярність discard. Якщо воно 0B, стек/пристрій може не підтримувати discard.

Рішення: Якщо discard не підтримується, autotrim не допоможе. Плануйте більше overprovisioning і тримайте нижчий рівень заповнення пулу.

Завдання 6: Перевірте заповнення пулу та фрагментацію

cr0x@server:~$ zpool list -o name,size,alloc,free,cap,frag,ashift,health ssdpool
NAME     SIZE  ALLOC  FREE  CAP  FRAG  ASHIFT  HEALTH
ssdpool  932G   620G  312G   66%   18%      12  ONLINE

Що це означає: CAP і FRAG корелюють з болем алокатора і тиском GC. На SSD-only пулах проблема зазвичай починає проявлятися при високому CAP.

Рішення: Розглядайте 80%+ як «зону ризику» для змішаних випадкових записів. Якщо треба працювати на межі — використовуйте диски з суттєвим OP і проектуйте під це.

Завдання 7: Визначте датасети з невідповідним recordsize для навантаження

cr0x@server:~$ zfs get -r recordsize,compression,atime ssdpool/app
NAME          PROPERTY     VALUE     SOURCE
ssdpool/app   recordsize   128K      default
ssdpool/app   compression  lz4       local
ssdpool/app   atime        off       local

Що це означає: 128K recordsize підходить для стримінгового IO і багатьох загальних навантажень. Для дрібних випадкових оновлень (БД) воно може підвищувати write amplification.

Рішення: Для datafile баз даних або образів VM розгляньте recordsize 16K (іноді 8K) або відповідний volblocksize для zvol, потім бенчмарк і спостерігайте.

Завдання 8: Для zvol перевірте volblocksize і очікування вирівнювання

cr0x@server:~$ zfs get volblocksize,compression,logbias,sync ssdpool/vmstore
NAME           PROPERTY      VALUE     SOURCE
ssdpool/vmstore  volblocksize 8K        local
ssdpool/vmstore  compression  lz4       inherited from ssdpool
ssdpool/vmstore  logbias      latency   default
ssdpool/vmstore  sync         standard  default

Що це означає: volblocksize фіксується при створенні. Zvol з 8K часто підходить для рандомного IO VM, але може збільшити метадані і накладні IO залежно від навантаження.

Рішення: Підбирайте volblocksize під файлову систему/розмір сторінки БД гостя і навантаження. Якщо помилились — доведеться пересоздавати zvol.

Завдання 9: Перевірте, чи домінують sync-записи

cr0x@server:~$ sudo zpool iostat -v ssdpool 1 5
                              capacity     operations     bandwidth
pool                        alloc   free   read  write   read  write
--------------------------  -----  -----  -----  -----  -----  -----
ssdpool                      620G   280G    100   2400  10.1M   140M
  mirror-0                   620G   280G    100   2400  10.1M   140M
    nvme0n1                     -      -     50   1200  5.0M    70M
    nvme1n1                     -      -     50   1200  5.1M    70M
--------------------------  -----  -----  -----  -----  -----  -----

cr0x@server:~$ sudo cat /proc/spl/kstat/zfs/zil | head
zil_commit_count            98122
zil_commit_writer_count     98050
zil_commit_waiter_count     72
zil_commit_time             19123456789
zil_commit_lwb_count        220001

Що це означає: Активний ZIL (ZFS Intent Log) вказує на багато синхронних операцій. Це нормально для певних навантажень.

Рішення: Якщо потрібна синхронна стійкість, розгляньте окремий SLOG-пристрій з PLP. Якщо ні — виправте налаштування експорту/монтування на рівні застосунку, а не обманюйте ZFS.

Завдання 10: Підтвердіть наявність SLOG і чи він дійсно допомагає

cr0x@server:~$ zpool status ssdpool
  pool: ssdpool
 state: ONLINE
config:

        NAME          STATE     READ WRITE CKSUM
        ssdpool       ONLINE       0     0     0
          mirror-0    ONLINE       0     0     0
            nvme0n1   ONLINE       0     0     0
            nvme1n1   ONLINE       0     0     0
        logs
          nvme2n1     ONLINE       0     0     0

Що це означає: Лог-пристрій є. Це не означає, що він безпечний або швидкий. Якщо йому бракує PLP, втрата живлення може перетворити «швидкий sync» у «креативну корупцію».

Рішення: Використовуйте SLOG лише на пристроях із захистом від втрати живлення. Якщо не можете це гарантувати — прийміть вищу sync-латентність або переархітектуруйте систему.

Завдання 11: Проаналізуйте поведінку брудних даних (тиск TXG)

cr0x@server:~$ cat /proc/spl/kstat/zfs/arcstats | egrep 'dirty|txg|memory_throttle' | head -n 12
dirty_data                      1879048192
dirty_over_target                  0
memory_throttle_count              12
txg_syncing                      5321
txg_synced                       5320

Що це означає: Якщо memory_throttle_count зростає, ZFS змушує писачів чекати через надто багато брудних даних.
Це часто корелює з тим, що сховище не встигає (або з періодичними стaллами).

Рішення: Якщо TXG sync повільний через зупинки пристроїв, спочатку виправте причини на стороні пристрою/GC. Налаштування лімітів dirty може замаскувати симптоми, але не вилікує хворобу.

Завдання 12: Перевірте термічний тротлінг NVMe (німій убивця продуктивності)

cr0x@server:~$ sudo nvme smart-log /dev/nvme0n1 | egrep 'temperature|warning|critical'
temperature                         : 77 C
warning_temp_time                   : 143
critical_comp_time                  : 0

Що це означає: warning_temp_time вказує, що контролер проводив час вище порогу попередження. Тротлінг часто виглядає як «рандомний GC-collapse».

Рішення: Виправте повітряний потік, радіатори, розміщення у шасі. Якщо ви налаштовуєте ZFS, щоб компенсувати перегрів — ви по суті пишете політику охолодження з додатковими кроками.

Завдання 13: Перевірте знос NAND і запасні блоки (проксі для steady-state)

cr0x@server:~$ sudo smartctl -a /dev/nvme0n1 | egrep 'Percentage Used|Data Units Written|Available Spare'
Available Spare:                    100%
Available Spare Threshold:          10%
Percentage Used:                    3%
Data Units Written:                 18,442,118

Що це означає: Високий «Percentage Used» сам по собі не викликає GC-collapse, але зношені диски поводяться інакше, і низький запас може зменшити продуктивний запас.

Рішення: Відстежуйте ці показники з часом. Якщо бачите нестабільність продуктивності й знос високий, замініть диск, а не дебагайте прошивку в п’ятницю ввечері.

Завдання 14: Виміряйте sync-латентність напряму (швидко і грубо)

cr0x@server:~$ sudo zfs create -o sync=standard -o compression=off ssdpool/testsync
cr0x@server:~$ cd /ssdpool/testsync
cr0x@server:~$ /usr/bin/time -f "%e seconds" bash -c 'for i in {1..5000}; do dd if=/dev/zero of=f bs=4k count=1 conv=fsync oflag=dsync status=none; done'
12.84 seconds

Що це означає: Це не бенчмарк-світового рівня; це канарка. Якщо це раптово стає 60+ секунд під навантаженням — ваш sync-шлях нездоровий.

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

Налаштувальні ручки, які важливі (і ті, що здебільшого ні)

Тримайте вільний простір серйозно

SSD-only пулам подобається мати запас вільного простору. Алокатор ZFS потребує місця, і SSD потребує місця.
Якщо ваш пул регулярно вище 80% і ви робите випадкові записи, ви просите GC-collapse.
Mirrors витримують це краще, ніж RAIDZ, але не зазнавайте небезпеки.

Що робити:

  • Цільове завантаження 60–75% використання для змішаних навантажень з випадковими записами, які вас турбують.
  • Використовуйте більші диски, ніж вам «потрібно» і розглядайте зайві гігабайти як резерв продуктивності.
  • Розгляньте явний overprovisioning (залишайте незапартиційований простір), якщо прошивка SSD виграє від цього.

Autotrim: вмикайте, але перевіряйте, чи не шкодить

На сучасних стеках autotrim зазвичай варто вмикати для SSD-only пулів. Воно допомагає підтримувати steady-state продуктивність, інформуючи пристрій про вільні блоки.
Але TRIM не безкоштовний: це додатковий IO і може погано взаємодіяти з деякими споживчими дисками або багатими на помилки прошивками.

Операційна позиція:

  • Увімкніть autotrim і спостерігайте латентність під час інтенсивних видалень.
  • Якщо TRIM викликає піки, розгляньте планові вікна trim (де підтримується) або інші SSD.

Recordsize/volblocksize: припиніть вдавати, що один розмір підходить усім

За замовчуванням recordsize (128K) підходить для багатьох файлових робіт. Для баз даних з 8K сторінками та частими оновленнями
128K може збільшити write amplification: ZFS переписує більші логічні записи і зачіпає більше метаданих.

Суб’єктивні рекомендації:

  • Бази даних на datasets: спробуйте recordsize 16K (іноді 8K), compression lz4, atime off.
  • Образи VM на datasets: часто recordsize 64K або 128K підходить, якщо IO частково послідовний; якщо він випадковий — менший розмір може допомогти.
  • Образи VM на zvol: встановіть volblocksize 8K або 16K згідно з IO гостя і прийміть, що треба пересоздавати для зміни.

Стиснення: LZ4 за замовчуванням не випадково

Стиснення зменшує байти для запису. Менше байтів — менше роботи NAND, менший тиск на GC і іноді вища пропускна здатність.
На SSD-only пулах стиснення часто — фішка продуктивності під прикриттям економії місця.

Використовуйте compression=lz4 широко, якщо немає виміряної причини не робити цього. Якщо CPU стане вузьким місцем, ви помітите це швидко.

Поведінка sync: найшвидше безпечно — це «standard»

Є три поширені вибори, про які сперечаються люди:

  • sync=standard: ZFS виконує запити застосунку. Це значення за замовчуванням і зазвичай правильне.
  • sync=disabled: ZFS «бреше» про sync. Продуктивність зростає. Зростає й ризик.
  • sync=always: примушує sync для всього, часто використовується для тестів або специфічних вимог, і воно швидко виявляє слабкі SLOG/пристрої пулу.

Якщо ви запускаєте бази даних, гіпервізори або NFS для серйозних застосунків: не використовуйте sync=disabled як «крок налаштування».
Це не налаштування. Це зміна контракту на стійкість даних. Іноді прийнятно в тимчасових середовищах; у продакшні — це пояснення фінансам, що означає «підтверджено, але не надійно».

SLOG: тільки коли потрібно і тільки на правильному пристрої

SLOG допомагає latency sync-записів коли ваше навантаження видає багато sync-запитів і основна латентність пулу — вузьке місце.
Воно не прискорює звичайні async-записи. Воно не рятує переповнений і фрагментований пул. І воно обов’язково має мати захист від втрати живлення.

Правила проєктування SLOG, що переживуть реальність:

  • PLP обов’язковий для SLOG в будь-якому середовищі, де можливий відрив живлення (тобто: у всіх).
  • Мірроруйте його, якщо його втрата спричинить операційну біль; втрата SLOG не зіпсує основні дані, але може викликати простої і стрес.
  • Не переобладовуйте: потрібно достатньо для сплесків; величезні SLOG мало що дають.

Ashift: зробіть правильно при створенні або платіть вічно

ashift встановлює показник розміру сектора пулу. На SSD з 4K фізичними секторами типово ashift=12.
Неправильний ashift викликає read-modify-write цикли і зайвий write amplification. Це одна з помилок, яка здається дрібницею, доки ви не побачите графік латентності.

Special vdev: фантастичний інструмент з гострими краями

Відміщення метаданих і дрібних блоків на special vdev може зменшити IO-ампліфікацію і покращити латентність. Але він також може стати єдиною точкою відмови, якщо не має надмірності,
і може сильно нагріватися, якщо розмір занадто малий. Людям подобається, бо графіки продуктивності ростуть відразу. Ненавидять, коли приходить час відновлення.

Якщо ви впроваджуєте special vdev:

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

Чого не варто одразу обсессивно налаштовувати

Люди люблять крутити те, що легко змінити: розміри ARC, дивні параметри модулів, планувальники IO.
Іноді це має значення. Більшість часу GC-collapse спричинений тиском простору, синхронними семантиками, невідповідністю розміру блоків,
невідповідністю класів дисків або термічним тротлінгом. Виправте великі важелі перш ніж танцювати з ядром.

Жарт №2: Якщо ви вирішили GC-collapse, змінюючи дванадцять sysctl-ів, вітаю — ви винайшли новий режим простою з гарною назвою.

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

Інцидент через хибне припущення: «NVMe означає, що sync безкоштовний»

Середня SaaS-компанія мігрувала з змішаного HDD/SSD у повністю NVMe ZFS-пул. План був простий:
«Швидші диски, та сама архітектура, всі в плюсі». Їхня основна база даних працювала на ZVOL, представлених VM.
Міграція була за вихідні. В більшості випадків все працювало. Потім настала понеділок.

Латентні скачки виникали кожні кілька хвилин. Ноди застосунку тайм-аутували транзакції. Інженери бачили NVMe з величезними заявленими IOPS і вважали,
що база даних не може чекати сховища. Вони ганялися за проблемами мережі. Вони шукали блокування. Вони масштабували поди застосунку.
Стало гірше, бо база даних робила більше повторів, а це — більше sync-записів.

Хибне припущення було в тому, що NVMe означає завжди низьку латентність, і що ZFS на NVMe поводиться як «raw NVMe з контрольними сумами».
Насправді навантаження було sync-важким, пул швидко заповнювався, а споживчі NVMe диски мали посередню steady-state випадкову записну продуктивність.
GC виконував довгі внутрішні копіювання під час сплесків TXG flush.

Виправлення не було екзотичним: перемістити лог бази на правильний PLP SLOG-пристрій, тримати контроль заповнення пулу і вирівняти volblocksize з розміром сторінки БД.
Також покращили охолодження; диски підходили до тротлінгу під піковим трафіком.
Інцидент завершився не героїчним патчем ядра, а запитом на закупівлю й планом ємності.

Оптимізація, що відбилася: «Давайте вимкнемо sync заради швидкості»

Інша компанія мала build-farm на NFS-експортах з ZFS SSD-only пулу. Часи білду важили, і розробники вміли голосно скаржитися.
Хтось помітив, що NFS-клієнти роблять багато синхронних записів. Хтось інший побачив блог, що радив sync=disabled.
Ви вже здогадуєтеся, що сталося далі.

Часи білду покращилися миттєво. Графіки виглядали чудово. Команда оголосила перемогу і перейшла далі.
Через тижні удар по живленню торкнувся стійки. UPS покрив частково й вузол зберігання перезапустився. Пул імпортувався чисто.
Файлова система змонтувалась. Всі зітхнули. Потім кеш білду почав повертати ушкоджені артефакти в тонкі способи: неправильні об’єктні файли, неузгоджені контрольні суми,
переривчасті помилки тестів, що виглядали як флейкі-байти.

Постмортем був неприємний, бо нічого «очевидно не зламалося». Система зробила саме те, що їй наказали:
підтвердити записи без очікування стійкості на диску. Саме це означає sync=disabled. Вони пожертвували стійкістю заради швидкості без документування
і без обмеження впливу.

Виправлення було нудним і трохи принижуючим: повернути sync=standard, додати правильний SLOG, призначений для sync-навантаження,
і налаштувати опції NFS-експорту/клієнта, щоб зменшити непотрібні sync-семантики для кешевих шляхів, які це допускають.
Вони також ввели політику: зміни продуктивності, що змінюють гарантії стійкості, вимагають погодження й плану відкату.

Нудна, але правильна практика, що врятувала день: «Ми тримали 30% вільних і слідкували за зносом»

Фінансова команда запустила аналітичну платформу на ZFS поверх SSD-дзеркал. Робоче навантаження було жорстким: сплески інжесту, важкий компакшин,
багато видалень і періодичні відновлення похідних наборів. Це той профіль IO, що перетворює оптимістичні проєкти у генератори інцидентів.
Їхньою секретною зброєю не був хитрий трюк. Це була дисципліна.

Вони мали жорстке правило: не перевищувати поріг заповнення пулу. Коли бізнес просив «трохи більше», вони купували ємність.
Також вони відстежували температуру дисків, індикатори зносу і щотижневу поведінку trim. Не тому, що любили дашборди, а тому, що це дозволяло помітити
«це погіршується» до того, як стало «це впало».

Одного кварталу обсяг інжесту підскочив. Латентність почала повільно зростати під час нічних задач. On-call помітив це рано: фрагментація зросла, трими займалися довше,
і вільне місце опустилося нижче лінії комфорту. Вони розширили пул і перерозподілили навантаження до наступного тижня.
Ніякого простою. Ніякої драми. Просто тикет закритий з нотаткою: «Резерв ємності відновлено.»

Урок неприємно неекзотичний: найкращий захист від GC-collapse — не жити вплотну до межі простору, плюс моніторинг, що каже, коли ви рухаєтесь до розриву.
Більшість перемог у надійності виглядають як «нічого не сталося», тому люди намагаються замінити їх на щось цікавіше.

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

1) Симптом: періодичні багатосекундні зупинки, особливо при комітах бази даних

  • Причина: sync-важке навантаження, що вражає пристрої пулу (без SLOG), ускладнене GC SSD під час TXG flush.
  • Виправлення: додати PLP SLOG (і замірорювати, якщо потрібно), тримати нижче рівень заповнення, перевірити охолодження, підігнати recordsize/volblocksize під дрібні оновлення.

2) Симптом: продуктивність чудова після розгортання, потім повільно деградує протягом тижнів

  • Причина: відсутній TRIM/autotrim, високий churn видалень, SSD втрачають знання про вільні сторінки і переходять у steady-state write amplification.
  • Виправлення: увімкнути autotrim, перевірити підтримку discard, розглянути планові trim-и, залишити більше вільного простору / OP, або інший клас дисків.

3) Симптом: рандомні write IOPS колапсують при ~85–90% заповнення пулу

  • Причина: алокатор і FTL одночасно голодують за чистим простором; фрагментація зростає; GC має менше варіантів.
  • Виправлення: розширити ємність, мігрувати холодні дані, застосувати квоти/резервації, встановити поріг політики і алерт до критичного стану.

4) Симптом: один NVMe пристрій показує гіршу продуктивність, пул виглядає «грудкуватим»

  • Причина: термічний тротлінг, відмінності прошивки, проблеми PCIe link або диск у іншому режимі GC.
  • Виправлення: перевірити NVMe SMART температури, підтвердити швидкість/ширину PCIe, оновити прошивку у контрольованому вікні, покращити повітряний потік.

5) Симптом: налаштування «попрацювало», а після перезавантаження стало гірше

  • Причина: зміни застосовані ad hoc (параметри модулів, sysctl) без управління конфігурацією; відкат не реальний.
  • Виправлення: закодизуйте налаштування, використовуйте change control, записуйте базові метрики і тестуйте під навантаженням з тими ж версіями ядра/модулів.

6) Симптом: special vdev швидко зношується і стає вузьким місцем

  • Причина: special_small_blocks встановлено занадто високо, special vdev занадто малий, гарячий набір метаданих/дрібних блоків сконцентрований там.
  • Виправлення: мірроруйте і розмірюйте special vdev відповідно, перегляньте поріг special_small_blocks, відстежуйте знос і пропускну здатність окремо.

7) Симптом: «Ми вимкнули sync і стало швидко», потім загадкова корупція після втрати живлення

  • Причина: змінено контракт стійкості; підтверджені записи фактично не були стійкими.
  • Виправлення: повернути sync=standard, використовувати SLOG для справжнього прискорення sync і документувати ділянки, де асинхронна стійкість допустима (якщо взагалі).

Контрольні списки / покроковий план

Покроково: стабілізація SSD-only ZFS-пулу з симптомами GC-collapse

  1. Зберіть докази під час піку. Запустіть zpool iostat -v 1, перевірте температури NVMe, і зафіксуйте заповнення пулу та frag.
  2. Підтвердіть sync-тиск. Перевірте активність ZIL і чи справді навантаження sync-важке (коміти БД, семантика NFS, налаштування гіпервізора).
  3. Перевірте TRIM/autotrim. Увімкніть autotrim, якщо вимкнено, підтвердіть підтримку discard, і стежте за TRIM-індукованою латентністю під час періодів видалення.
  4. Приведіть заповнення під контроль. Звільніть простір, розширте або перемістіть дані. Якщо ви вище ~80% з випадковими записами — вважайте це первинною проблемою.
  5. Виправте термічні та прошивкові базові речі. NVMe throttling виглядає як «настрій сховища». Не ігноруйте це.
  6. Підігнати розміри блоків. Скоригуйте recordsize для датасетів (або пересоздайте zvol з правильним volblocksize). Не копіюйте 128K скрізь.
  7. Використовуйте стиснення. Віддавайте перевагу lz4, якщо немає вимірюваних причин проти.
  8. Якщо потрібна sync-продуктивність — додайте правильний SLOG. PLP, бажано в mirror. Потім виміряйте знову.
  9. Переперевірте під реальним навантаженням. Синтетичний послідовний бенчмаркінг — не доказ для БД-навантаження.
  10. Встановіть алерти. CAP %, FRAG %, температури NVMe, write latency і ZFS throttle counters. Проблеми легше попередити, ніж героїчно вирішувати.

Контрольний список при проєктуванні SSD-only ZFS-пулу, що не підведе

  • Топологія: Mirrors для латентності і IOPS-важких навантажень; обережно з широким RAIDZ для випадкових записів.
  • Диски: Віддавайте перевагу enterprise SSD з передбачуваним steady-state і PLP там, де потрібно.
  • Запас простору: Плануйте ємність так, щоб тримати здоровий вільний простір без щомісячних благань про бюджет.
  • ashift: Підтвердьте 4K вирівнювання і правильний ashift при створенні пулу.
  • autotrim: Увімкніть і валідируйте з моделлю/прошивкою вашого диска.
  • Відповідність навантаженням: Налаштовуйте recordsize/volblocksize для кожного dataset/zvol, а не «взагалі для пулу».
  • Моніторинг: Перцентилі латентності, температури, індикатори зносу, ZFS throttle counters і тренди заповнення.
  • Контроль змін: Будь-яка зміна, що змінює стійкість (sync, write cache), вимагає рев’ю і плану відкату.

FAQ

1) Чи є garbage collection collapse проблемою «поганого SSD» чи «поганого ZFS»?

Зазвичай ні. Це проблема взаємодії: CoW-файлова система плюс прошивка SSD під тиском простору і сплесковими записами.
Кращі SSD (більше OP, краща прошивка) розширюють безпечну зону. Кращий дизайн ZFS (запас простору, правильні розміри блоків, адекватний sync-шлях) запобігає обриву.

2) Чи завжди треба вмикати autotrim на SSD-only пулах?

У більшості сучасних середовищ — так. Але валідуйте підтримку discard і слідкуйте за регресіями під високим churn видалень.
Якщо ваша модель диска погано поводиться з постійним TRIM, може знадобитись плановий trim або інше обладнання.

3) Чи виправить додавання більше RAM (ARC) зависання SSD?

Воно може їх прикрити, поглинаючи сплески, але не змінить steady-state поведінку SSD.
Якщо пул не може скинути дані через зупинку пристрою, ви все одно зіткнетесь з throttling-ом у підсумку.

4) Коли SLOG вартий витрат?

Коли у вас є відчутне вузьке місце на sync-записах: бази даних, часті коміти, NFS з sync-семантикою, VM-навантаження з fsync-шаблонами.
SLOG — не загальний кеш записів. І без PLP це не оптимізація, а проблема надійності.

5) Чи можна змінити volblocksize після створення zvol?

Ні. Потрібно пересоздати zvol і мігрувати дані. Плануйте volblocksize заздалегідь і зафіксуйте, щоб майбутній ви не «оптимізував» у хаос.

6) Чи дійсно стиснення допомагає на швидкому NVMe?

Часто так. Менше даних для запису — менше роботи NAND і менше тиску на GC. LZ4 зазвичай достатньо дешевий, щоб покращити пропуск і латентність.
Вимірюйте для свого навантаження, особливо якщо ви CPU-зв’язаний або зберігаєте несжаті дані.

7) Чому ситуація погіршується зі заповненням пулу, навіть якщо SSD має запасні блоки?

Бо «запасні блоки» у SSD — не те ж саме, що «вільний простір» у ZFS. ZFS потребує вільних metaslabs; FTL SSD потребує вільних блоків.
Високе заповнення пулу і фрагментація зменшують гнучкість обох шарів.

8) Чи поганий RAIDZ для SSD-пулів?

Не завжди, але RAIDZ менш прощальний для дрібних випадкових записів і sync-важких навантажень. Mirrors зазвичай дають кращу латентність і консистентність IOPS.
Якщо ваше навантаження в основному послідовне і важлива ємність — RAIDZ може підійти, за умови розумного контролю заповнення.

9) Чи прийнятний sync=disabled коли-небудь?

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

10) Як зрозуміти, чи моя проблема — GC чи просто термічний тротлінг?

Перевірте SMART логи NVMe на температуру і warning time, корелюйте піки з нагрівом і підтвердіть стабільну поведінку PCIe link.
Тротлінг часто дає відтворювану деградацію «після X хвилин навантаження», що відновлюється при охолодженні.

Висновок: практичні наступні кроки

GC-collapse на SSD-only ZFS-пулах можна запобігти. Не магією — фундаментом:
тримайте вільний простір, підбирайте розміри блоків під навантаження, поважайте sync-семантику і не економте на пристроях, що визначають стійкість.

Наступні кроки, які принесуть користь цього тижня:

  1. Увімкніть і перевірте autotrim (та підтвердіть підтримку discard).
  2. Встановіть політику рівня заповнення і алерти перед перетином.
  3. Аудит recordsize/volblocksize для датасетів/zvol-ів, що важливі.
  4. Якщо ви sync-важкі — впровадьте правильний PLP SLOG і виміряйте знову.
  5. Перевірте температури NVMe під піковим навантаженням і покращіть повітряний потік перед налаштуванням.
  6. Запишіть ваш контракт стійкості (sync-настройки, очікування застосунку), щоб робота з продуктивністю не стала ставкою на втрату даних.

Сховище — це та частина системи, яка пам’ятає ваші помилки. Налаштовуйте так, ніби воно буде свідчити пізніше.

← Попередня
Docker «No route to host» у контейнерах: виправлення маршрутизації й iptables, що зберігаються
Наступна →
Debian 13: ZRAM/Zswap — коли вони рятують систему і коли шкодять

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