Першою ознакою ніколи не буває «пул помирає». Це повідомлення в 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
- Зберіть докази під час піку. Запустіть
zpool iostat -v 1, перевірте температури NVMe, і зафіксуйте заповнення пулу та frag. - Підтвердіть sync-тиск. Перевірте активність ZIL і чи справді навантаження sync-важке (коміти БД, семантика NFS, налаштування гіпервізора).
- Перевірте TRIM/autotrim. Увімкніть autotrim, якщо вимкнено, підтвердіть підтримку discard, і стежте за TRIM-індукованою латентністю під час періодів видалення.
- Приведіть заповнення під контроль. Звільніть простір, розширте або перемістіть дані. Якщо ви вище ~80% з випадковими записами — вважайте це первинною проблемою.
- Виправте термічні та прошивкові базові речі. NVMe throttling виглядає як «настрій сховища». Не ігноруйте це.
- Підігнати розміри блоків. Скоригуйте recordsize для датасетів (або пересоздайте zvol з правильним volblocksize). Не копіюйте 128K скрізь.
- Використовуйте стиснення. Віддавайте перевагу lz4, якщо немає вимірюваних причин проти.
- Якщо потрібна sync-продуктивність — додайте правильний SLOG. PLP, бажано в mirror. Потім виміряйте знову.
- Переперевірте під реальним навантаженням. Синтетичний послідовний бенчмаркінг — не доказ для БД-навантаження.
- Встановіть алерти. 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-семантику і не економте на пристроях, що визначають стійкість.
Наступні кроки, які принесуть користь цього тижня:
- Увімкніть і перевірте autotrim (та підтвердіть підтримку discard).
- Встановіть політику рівня заповнення і алерти перед перетином.
- Аудит recordsize/volblocksize для датасетів/zvol-ів, що важливі.
- Якщо ви sync-важкі — впровадьте правильний PLP SLOG і виміряйте знову.
- Перевірте температури NVMe під піковим навантаженням і покращіть повітряний потік перед налаштуванням.
- Запишіть ваш контракт стійкості (sync-настройки, очікування застосунку), щоб робота з продуктивністю не стала ставкою на втрату даних.
Сховище — це та частина системи, яка пам’ятає ваші помилки. Налаштовуйте так, ніби воно буде свідчити пізніше.