ZFS — це система, яка чемно брешуть вам — недовго й заради вашого добра. Більшість записів спочатку потрапляє в RAM, а потім ZFS перетворює їх на реальність на диску великими, ефективними партіями. Це пакетування — особливість: так ZFS забезпечує сильну узгодженість і хорошу пропускну здатність, не перетворюючи кожен невеликий запис на маленьку катастрофу.
Проблема в тому, що «великі ефективні партії» можуть виглядати як сплески: диски мовчать, а потім раптом загоряються; графіки затримок виглядають як серцебиття; бази даних скаржаться кожні кілька секунд; і хтось запитує, чому сховище «заїкається». Частим підозрюваним є txg_timeout: таймер, який підштовхує ZFS закрити та синхронізувати групу транзакцій (TXG). Ця стаття пояснює, що насправді відбувається, як це довести в продакшні і як згладити затримки без втрати безпеки.
Ментальна модель: TXG, не «записи»
Якщо ви запам’ятаєте одну річ — запам’ятайте це: ZFS не виконує «запис», коли додаток викликає write(2). ZFS будує нову версію файлової системи в пам’яті й потім фіксує цю версію на диску. Ці фіксації відбуваються групами транзакцій (TXG).
TXG — це вікно пакетування. Упродовж цього вікна ZFS збирає модифіковані метадані та буфери даних (dirty data). Коли TXG закривається, ZFS починає його синхронізацію: виділяє блоки, записує дані й метадані, оновлює MOS (Meta Object Set) і, зрештою, записує uberblocks, які роблять новий стан довготривалим. Ключове: закриття TXG дешеве; синхронізація — це справжня робота.
Три TXG одночасно
Зазвичай у ZFS одночасно «в польоті» три TXG:
- Open TXG: приймає нові зміни (накопичується dirty data).
- Quiescing TXG: закривається, фіксує набір змін.
- Syncing TXG: записує цей зафіксований набір на диск.
Цей конвеєр пояснює, чому ZFS може продовжувати приймати записи, поки синхронізує попередню партію. Це також пояснює, чому ви можете спостерігати періодичні сплески: фаза синхронізації — це коли сховище бачить реальну роботу, і вона часто зосереджена в короткому інтервалі.
Жарт №1: ZFS не втрачає ваші дані, він просто «тимчасово misplaces» їх у RAM — як ви робите з ключами, але з контрольними сумами.
Що txg_timeout насправді робить (і чого не робить)
txg_timeout зазвичай описують як «як часто ZFS скидає/фіксує». Це досить близько, щоб бути корисним, і достатньо неправильно, щоб зіпсувати вихідні вихідні.
У більшості реалізацій OpenZFS txg_timeout — це максимальний час (у секундах), протягом якого TXG може залишатися відкритим, перш ніж ZFS змусить його quiesce і почне синхронізацію. За замовчуванням це часто 5 секунд. Це значення не випадкове: воно є компромісом між розподілом витрат на метадані (пакетування) і обмеженням обсягу роботи на коміт (латентність).
Що змінюється при зміні txg_timeout
- Зниження зазвичай створює частіші, менші TXG. Це може зменшити розмір пікового «комітного» сплеску, але збільшити накладні витрати (більше комітів, більше метаданих, потенційно нижча пропускна здатність).
- Підвищення зазвичай створює рідші, більші TXG. Це може поліпшити пропускну здатність для потокових записів, але підсилити періодичні стрибки латентності (і збільшити обсяг dirty data в пам’яті).
Чого txg_timeout НЕ виправляє
Воно не перетворить випадкові синхронні операції на плавний низьколатентний I/O, якщо ваші пристрої не справляються, ваш SLOG неправильно підібраний, пул фрагментований або навантаження примушує синхронні семантики. Таймінг TXG — це барабанщик; ваші диски все одно залишаються гуртом.
Чому з’являється патерн «кожні 5 секунд»
Якщо ви бачите піки латентності з регулярним інтервалом — часто близько 5 секунд — ваша перша гіпотеза має бути: «комітні сплески TXG». txg_timeout — це годинник, який може робити цей ритм видимим. Але сплески також можуть бути спричинені лімітами dirty data (throttling), тиском синхронних записів та поведінкою кешу пристрою.
Чому записи йдуть пакетами
У продакшні «пакетні записи» рідко мають одну кореневу причину. Зазвичай це кілька адекватних механізмів, що приходять в піки одночасно. Ось найпоширеніші з них.
1) Пакетування — це суть
ZFS — copy-on-write. Для багатьох навантажень ефективний шлях такий: записати нові блоки в інше місце, потім атомарно переключити вказівники. Якби ZFS мусив робити цю хореографію вказівників для кожного 4 KB запису, ви б отримали коректність і страждання. TXG дозволяють ZFS робити цю хореографію раз за пакет.
2) Ліміти dirty data і throttling створюють «урвища»
ZFS дозволяє певну кількість dirty data в пам’яті. Коли ви досягаєте порогу dirty data, ZFS починає стримувати (throttle) писачів, щоб запобігти неконтрольованому росту пам’яті і змусити синхронізацію надолужити. Такий «подія throttling» часто виглядає як урвище: латентність нормальна, поки раптом не стає неприйнятною.
3) Sync-записи можуть вимагати негайної активності ZIL
Синхронні записи (O_SYNC, fsync(), налаштування WAL у базах, NFS зі синхронною семантикою) не чекають, доки TXG синхронізується в основний пул. Натомість вони чекають, поки ZIL (ZFS Intent Log) зафіксує намір. На пулі без виділеного SLOG цей журнал знаходиться на тих самих дисках, що й усе інше, і запис журналу може серіалізуватися або конкурувати в неприємний спосіб.
4) Малі блоки + посилення метаданих
Навантаження, що записує багато малих випадкових блоків, записує не тільки дані. Воно генерує оновлення метаданих: вказівники блоків, непрямі блоки, spacemap оновлення, контрольні суми тощо. ZFS із цим справляється добре, але робота ще залишається. Під час коміту TXG метадані I/O можуть стати короткою, інтенсивною бурею.
5) Поведінка нижчого рівня пристроїв теж бустрова
SSD мають внутрішній GC; HDD-масиви — скидання write cache; RAID-контролери переставляють I/O; NVMe-прошивка має свої черги й обслуговування. ZFS-сплески можуть синхронізуватися з цими пристроєвими сплесками і робити графіки схожими на важке дихання.
6) ZFS чесно відображає зворотний тиск
Деякі стекі зберігання ховають конкуренцію за рахунок нескінченного буферування, поки не впадуть. ZFS зазвичай відштовхує, коли потрібно. Це краща інженерія — і гірша оптика на дашборді.
Факти й історія, які варто знати
- ZFS створювали в Sun з думкою про корпоративне сховище, де пакетування й атомарні коміти кращі за «записувати все без винятків».
- TXG старші за термін «DevOps»; цей стиль транзакційного пакетування походить із класичних ідей файлових систем і баз даних, а не сучасного хмарного маркетингу.
- За замовчуванням «5 секунд» для таймінгу TXG зустрічається в кількох лініях ZFS, бо часто балансує латентність і пропускну здатність для загального використання.
- ZIL — не кеш записів. Це журнал намірів для синхронних семантик; більшість записів у ньому відкидаються після коміту TXG.
- SLOG потрібен тільки для синхронних записів. Якщо ваше навантаження асинхронне, SLOG не допоможе і може навіть стати ще одним доменом відмов.
- Copy-on-write робить часткові записи безпечнішими, але перекладає складність на виділення простору і оновлення метаданих, які оплачується під час синхронізації TXG.
- ZFS перевіряє контрольні суми всього (метадані й дані), що чудово для цілісності і має нетривіальні витрати на CPU й пам’ять під час важких комітів.
- OpenZFS розійшовся по платформах (Illumos, FreeBSD, Linux) і налаштування трохи відрізняються; концепції залишаються, але точні імена параметрів і значення за замовчуванням можуть відрізнятися.
- Піки латентності часто корелюють із записами uberblock, бо це фінальний крок «публікації» TXG; іноді ви можете побачити ритм у низькорівневих трасах.
Розпізнавання затримок у формі TXG
Біль, пов’язаний із TXG, має певний запах:
- Регулярний ритм: піки приблизно кожні 5 секунд (або відповідно до вашого
txg_timeout). - Переважно з боку записів: затримка читань може залишатися нормальною, доки система не насититься.
- Моменти «все призупиняється»: не повна зупинка, але додатки повідомляють про джиттер — особливо бази даних і хости віртуальних машин.
- CPU не завантажений: система може бути I/O-зв’язана з достатньою кількістю вільного CPU, або CPU завантажений для контрольних сум/сжаття так, що це не виглядає як 100% user CPU.
- Осциляція dirty data: використання пам’яті та лічильники dirty data ростуть, потім різко падають під час коміту.
Жарт №2: Якщо ваші графіки стрибають кожні п’ять секунд, вітаю — ви відкрили, що час це плоске коло, а ваше сховище — це коло.
Швидкий план діагностики
Це послідовність «вхід у аварійний міст». Вона впорядкована так, щоб швидко відповісти на одне запитання: чи бачимо ми тиск через коміт TXG, тиск логування синхронних записів, чи ж просто насиченість пристрою?
Спочатку: підтвердьте, що симптом періодичний і пов’язаний із записами
- Перевірте каденс латентності: чи збігається він з ~5 секундами?
- Відокремте читання від записів: чи стрибає саме латентність записів, поки читання виглядають нормально?
- Перевірте, чи навантаження синхронне (бази даних, NFS, VM storage з бар’єрами).
По-друге: визначте, чи тригер — синхронні записи
- Перегляньте активність ZIL/SLOG: чи домінують записі журналу?
- Перевірте налаштування dataset
syncіlogbias. - Перевірте здоров’я і латентність SLOG (якщо є).
По-третє: визначте, чи тригер — dirty data TXG + throttling
- Подивіться лічильники dirty data та стан TXG (залежить від платформи, але їх можна спостерігати).
- Перевірте, чи писачі стримуються (високий час очікування, блокування в I/O шляхах ядра).
- Корелюйте з пропускною здатністю пулу: чи досягаєте ви меж пристрою під час вікон синхронізації?
По-четверте: підтвердьте поведінку нижчого рівня пристроїв
- Перевірте латентність по vdev: один повільний пристрій може визначати час коміту пулу.
- Перевірте глибину черги і насиченість.
- Перевірте лічильники помилок і повторних спроб; «повільний» іноді означає «повторюється».
Практичні завдання (команди + інтерпретація)
Команди нижче припускають середовище Linux + OpenZFS. Якщо ви на FreeBSD/Illumos — транслюйте намір; робочий процес усе ще застосовний.
Завдання 1: Ідентифікуйте датасети та ключові властивості (sync, logbias, recordsize)
cr0x@server:~$ sudo zfs list -o name,used,avail,recordsize,compression,sync,logbias -r tank
NAME USED AVAIL RECORDSIZE COMPRESS SYNC LOGBIAS
tank 8.21T 5.44T 128K lz4 standard latency
tank/vm 4.92T 5.44T 16K lz4 standard latency
tank/db 1.10T 5.44T 16K lz4 standard latency
tank/backups 2.04T 5.44T 1M lz4 disabled throughput
Інтерпретація: Якщо скарги на латентність надходять від tank/db або tank/vm, малий recordsize і logbias=latency вказують, що вам важлива поведінка sync. Якщо датасет встановлений у sync=disabled, ви могли «вирішити» проблему латентності, видаливши довговічність — корисно для бенчмарків, небезпечно для робочих навантажень.
Завдання 2: Спостерігайте I/O на рівні пулу і помітні періодичні сплески
cr0x@server:~$ sudo zpool iostat -v tank 1
capacity operations bandwidth
pool alloc free read write read write
---------- ----- ----- ----- ----- ----- -----
tank 8.21T 5.44T 210 980 28.1M 210M
mirror 2.73T 1.81T 105 490 14.0M 105M
nvme0n1 - - 52 245 7.0M 52.5M
nvme1n1 - - 53 245 7.0M 52.4M
mirror 2.73T 1.81T 105 490 14.1M 105M
nvme2n1 - - 52 245 7.0M 52.6M
nvme3n1 - - 53 245 7.1M 52.4M
Інтерпретація: Шукайте записну пропускну здатність, яка переходить від «помірної» до «максимальної» з певною каденцією, з різким зростанням операцій. Сплески, що вирівнюються з таймером TXG, — це підказка, а не остаточний вирок.
Завдання 3: Додайте колонки латентності, щоб виявити повільний vdev
cr0x@server:~$ sudo zpool iostat -v tank 1 -l
operations bandwidth total_wait disk_wait
pool read write read write read write read write
-------------------------- ---- ----- ----- ----- ----- ----- ----- -----
tank 210 980 28.1M 210M 1ms 18ms 0ms 15ms
mirror 105 490 14.0M 105M 1ms 18ms 0ms 15ms
nvme0n1 52 245 7.0M 52.5M 1ms 20ms 0ms 17ms
nvme1n1 53 245 7.0M 52.4M 1ms 19ms 0ms 16ms
mirror 105 490 14.1M 105M 1ms 18ms 0ms 15ms
nvme2n1 52 245 7.0M 52.6M 1ms 18ms 0ms 15ms
nvme3n1 53 245 7.1M 52.4M 1ms 40ms 0ms 38ms
Інтерпретація: Один пристрій (nvme3n1) показує значно вищий час очікування запису. Один «відстаючий» може розтягнути час синхронізації TXG і створити видимі сплески. Тут припиняєте налаштовувати і починаєте питати: «чи вмирає пристрій або неправильно сконфігурований?»
Завдання 4: Перевірте здоров’я пулу та лічильники помилок
cr0x@server:~$ sudo zpool status -v tank
pool: tank
state: ONLINE
status: One or more devices has experienced an unrecoverable error.
action: Replace the device or clear the errors.
scan: scrub repaired 0B in 03:12:55 with 0 errors on Sun Dec 22 02:12:19 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
mirror-1 ONLINE 0 5 0
nvme2n1 ONLINE 0 0 0
nvme3n1 ONLINE 0 5 0
errors: No known data errors
Інтерпретація: Помилки запису на одному члені vdev можуть перетворюватись на повторні спроби і таймаути, що виглядає як сплески латентності. Якщо бачите помилки — розглядайте їх як проблему продуктивності і надійності.
Завдання 5: Підтвердьте txg_timeout і суміжні налаштування
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_txg_timeout
5
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_dirty_data_max
17179869184
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_dirty_data_max_percent
10
Інтерпретація: За замовчуванням 5 секунд і щедрий dirty data max можуть створити поведінку «тихо — потім буря». Але не чіпайте це одразу — спочатку виміряйте, потім змінюйте по одному параметру.
Завдання 6: Спостерігайте тиск ARC (бо пам’ять формує dirty data)
cr0x@server:~$ grep -E 'c_max|c_min|size|memory_throttle_count' /proc/spl/kstat/zfs/arcstats
c_max 4 137438953472
c_min 4 68719476736
size 4 92341784576
memory_throttle_count 4 0
Інтерпретація: Якщо ARC бореться за пам’ять, ZFS може стримуватися або поводитись агресивніше. Якщо memory_throttle_count зростає, ваші «сплески» можуть бути результатом бракання RAM, а не просто каденсу TXG.
Завдання 7: Визначте, чи навантаження синхронне
cr0x@server:~$ sudo zfs get -H -o name,property,value sync tank/db
tank/db sync standard
cr0x@server:~$ sudo iostat -x 1
avg-cpu: %user %nice %system %iowait %steal %idle
12.00 0.00 8.00 18.00 0.00 62.00
Device r/s w/s r_await w_await aqu-sz %util
nvme3n1 55.0 280.0 0.9 35.0 9.8 98.0
Інтерпретація: Високий w_await і практично 100% завантаження пристрою корелюють з комітним тиском. Щоб вирішити, чи це пов’язано із ZIL, треба подивитись на наявність і поведінку SLOG.
Завдання 8: Перевірте, чи є виділений лог-пристрій (SLOG)
cr0x@server:~$ sudo zpool status tank | sed -n '1,120p'
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
nvme0n1 ONLINE 0 0 0
nvme1n1 ONLINE 0 0 0
mirror-1 ONLINE 0 0 0
nvme2n1 ONLINE 0 0 0
nvme3n1 ONLINE 0 0 0
logs
mirror-2 ONLINE 0 0 0
nvme4n1 ONLINE 0 0 0
nvme5n1 ONLINE 0 0 0
Інтерпретація: Існує дзеркальний SLOG. Добре. Тепер треба підтвердити, що він має низьку латентність і захист від втрати живлення, або він перетворить синхронні записи на заручника продуктивності.
Завдання 9: Виміряйте латентність SLOG побічно через wait по vdev
cr0x@server:~$ sudo zpool iostat -v tank 1 -l | sed -n '1,40p'
pool read write read write read write read write
-------------------------- ---- ----- ----- ----- ----- ----- ----- -----
tank 210 980 28.1M 210M 1ms 18ms 0ms 15ms
mirror-0 105 490 14.0M 105M 1ms 18ms 0ms 15ms
nvme0n1 52 245 7.0M 52.5M 1ms 20ms 0ms 17ms
nvme1n1 53 245 7.0M 52.4M 1ms 19ms 0ms 16ms
logs
mirror-2 0 320 0.0K 19.2M 0ms 2ms 0ms 1ms
nvme4n1 0 160 0.0K 9.6M 0ms 2ms 0ms 1ms
nvme5n1 0 160 0.0K 9.6M 0ms 2ms 0ms 1ms
Інтерпретація: Час запису log vdev близько 1–2 ms — пристойно для багатьох синхронних навантажень. Якщо тут бачите 10–50 ms, ваш SLOG не є SLOG; це повільний журнал.
Завдання 10: Перевірте dataset logbias для відповідності наміру
cr0x@server:~$ sudo zfs get -H -o name,property,value logbias tank/db tank/vm
tank/db logbias latency
tank/vm logbias latency
Інтерпретація: logbias=latency віддає перевагу використанню журналу для синхронних записів (добре для баз даних). throughput може зменшити трафік журналу в деяких випадках, але може збільшити записи в основний пул для синхронних операцій. Зміна без розуміння навантаження — класична пастка.
Завдання 11: Спостерігайте kstats, пов’язані з TXG (dirty data і час синхронізації)
cr0x@server:~$ ls /proc/spl/kstat/zfs/ | head
arcstats
dbufstats
dmu_tx
vdev_queue
vdev_mirror
zfetchstats
cr0x@server:~$ cat /proc/spl/kstat/zfs/dmu_tx
13 1 0x01
name type data
dmu_tx_assigned 4 12800451
dmu_tx_delay 4 21438
dmu_tx_error 4 0
dmu_tx_memory_reserve 4 0
dmu_tx_memory_reclaim 4 0
Інтерпретація: На Linux видимість TXG іноді опосередкована. Зростання dmu_tx_delay вказує на затримки писачів — часто через ліміти dirty data і тиск синхронізації. Ви шукаєте кореляцію: коли стрибки латентності, чи підскакують ці лічильники?
Завдання 12: Використайте perf, щоб підтвердити, що ви блокуєтесь в ZFS I/O шляхах (не CPU)
cr0x@server:~$ sudo perf top -g
12.4% [kernel] [k] __schedule
8.9% [kernel] [k] io_schedule
6.1% [kernel] [k] blk_mq_get_tag
5.7% zfs [k] zio_wait
4.8% zfs [k] zio_execute
4.1% zfs [k] vdev_queue_io
Інтерпретація: Бачити час у io_schedule і ZFS функціях zio_* означає, що система чекає на сховище, а не витрачає CPU на стиснення або контрольні суми. Це відсуває вас від налаштування compression або масштабування CPU і спрямовує до латентності vdev, SLOG і поведінки TXG.
Завдання 13: Швидка перевірка насичення на рівні пристрою і черг
cr0x@server:~$ sudo nvme smart-log /dev/nvme3n1 | sed -n '1,25p'
Smart Log for NVME device:nvme3n1 namespace-id:ffffffff
critical_warning : 0
temperature : 47 C
available_spare : 100%
percentage_used : 3%
media_errors : 0
num_err_log_entries : 0
warning_temp_time : 0
critical_comp_time : 0
Інтерпретація: Це не тест продуктивності; це перевірка на тривожні знаки. Якщо media errors або error log entries зростають, ваша «проблема TXG» може бути апаратною проблемою в масці ZFS.
Завдання 14: Тимчасово безпечно змініть txg_timeout і спостерігайте
cr0x@server:~$ sudo sh -c 'echo 3 > /sys/module/zfs/parameters/zfs_txg_timeout'
cr0x@server:~$ cat /sys/module/zfs/parameters/zfs_txg_timeout
3
Інтерпретація: Це тест, а не спосіб життя. Якщо ваші піки латентності змінять каденс і зменшать амплітуду, ви підтвердили, що таймінг TXG — частина проблеми. Якщо нічого не зміниться — припиніть крутити і дивіться на синхронні записи або латентність vdev.
Три міні-історії з корпоративного світу
Міні-історія 1: Інцидент, спричинений неправильною гіпотезою
Платформна команда отримала тікет: «База даних зависає кожні п’ять секунд». Це скарга, яка спочатку змушує підозрювати систему моніторингу. Але графіки були чесні: латентність записів вривалася метрономічно.
Хтось висунув акуратну гіпотезу: «ZFS флашить кожні п’ять секунд, отже база мусить змушувати флаші». Ця гіпотеза набрала обертів, бо звучала як фізика. Вони зосередилися на базі даних, налаштували чекпоінти, сперечалися про WAL і змінювали батчинг додатків. Піки латентності до них були байдужі.
Реальна причина була простішою і дратівливішою: один член дзеркала в пулі почав довше завершувати записи — без явних збоїв, просто іноді зупинки. ZFS, будучи коректним, чекала на найповільніший компонент, щоб виконати зобов’язання TXG. Час коміту TXG розтягнувся, dirty data накопичились, і система потрапила під throttling. П’ятисекундний патерн був лише таймером, який оголював вузьке місце, яке й так би завдавало шкоди.
Вони довели це, додавши колонки wait-time до zpool iostat. Один пристрій постійно показував підвищений disk_wait під час сплесків. SMART виглядав «нормально», бо SMART часто «нормальний» аж до моменту, коли вже ні. Заміна пристрою усунула періодичні зависання без жодного втручання в налаштування ZFS.
Урок, який лишився: періодична поведінка не завжди означає «проблема таймеру». Іноді це «один повільний компонент спричиняє періодичний біль, коли система змушена синхронізуватися». TXG просто роблять синхронізацію видимою.
Міні-історія 2: Оптимізація, що відкотилася
Інша організація мала ферму VM на zvol. Вони хотіли плавнішу латентність для записів гостьових ОС. Хтось прочитав, що зниження txg_timeout може зменшити розмір сплеску, тому вони агресивно зменшили його по всьому флоту.
Графіки покращились — десь на день. Потім пропускна здатність обрушилась під час нічних завдань. Вікна бекапів розтягнулись. Ноди зберігання не стали «повільніші» класично; вони працювали більше роботи на одиницю даних. Більше TXG означало частіші коміти метаданих, більше хаосу і менше часу в ефективному steady-state.
Гірше, менші TXG підвищили відносну частку накладних витрат для навантажень з багатьма дрібними синхронними записами. SLOG почав хапатися частіше з-за більшої кількості fsync-шаблонів, і раніше «нормальний» лог-пристрій став обмеженням. Їхня латентність стала ще більш нерівною під навантаженням.
Вони відкотили зміни і застосували більш селективний підхід: розділити датасети, правильно встановити volblocksize для zvol, додати належний дзеркальний low-latency SLOG для синхронно-важких орендарів і обмежити dirty data відповідно до пам’яті. Остаточне налаштування було нудним і поступовим — і саме таке налаштування хочеться мати поруч із критичними грошовими робочими навантаженнями.
Урок: txg_timeout — це важіль, а не панацея. Сильне його втягання міняє пакетність на накладні витрати, що часто проявляється гіршою хвостовою латентністю, коли система реально зайнята.
Міні-історія 3: Нудна, але правильна практика, що врятувала день
SaaS-компанія працювала multi-tenant сховище з суворими SLO. Вони навчилися важким шляхом: налаштовувати ZFS без спостережуваності — це астрологія. Тому вони зробили нудну річ: відстеження латентності по vdev, дашборди каденсу TXG і рутинні scrub-розклади. Ніхто цього не любив. Всі отримували вигоду.
Одного дня латентність записів почала набирати ледь помітний, але зростаючий 5-секундний ритм. Ще не інцидент — просто «хмм», який хороші інженери на дзвінку помічають, роблячи вигляд, що не помітили. Дашборди показували, що сплески, пов’язані з комітом, повільно гіршають, але лише на одному пулі.
Оскільки вони вже мали гістограми wait по vdev, вони помітили один пристрій з ростучою довгоперіодною хвостовою латентністю. Він не падав повністю. Він просто іноді брав стільки часу, щоб розтягнути синхронізацію TXG. Вони евакуювали орендарів з того пулу і під час звичайного вікна обслуговування замінили пристрій. Ніяких надзвичайних змін, ніяких героїчних дій о 3 ранку, ніяких «перезавантажень — і стало краще».
Після заміни ритм TXG зник. Команда виглядала як генії, що було несправедливо — вони просто були підготовлені. Нудна практика була не про налаштування; вона полягала в тому, щоб помітити проблему достатньо рано, щоб налаштування не знадобилось.
Урок: найкращий спосіб «згладити латентність» — запобігти умовам, які створюють великі вікна синхронізації — особливо тиху деградацію пристроїв і непомічені насичення.
Стратегія налаштування: що змінювати і в якому порядку
Коли люди кажуть «налаштуйте ZFS», вони часто мають на увазі «змініть параметр, поки графік не стане краще». Це не налаштування; це виконання бажання графіка. Ось безпечніший порядок дій.
Крок 1: Визначте, чи ваша проблема — синхронна латентність чи латентність коміту TXG
Якщо ваше навантаження домінують синхронні операції, найшвидший шлях зменшити хвостову латентність — належний SLOG (дзеркальний, захищений від втрати живлення, з низькою латентністю) і налаштування dataset, що відповідають навантаженню (sync, logbias). Якщо це переважно асинхронний трафік, важливіше поводження TXG і dirty data.
Крок 2: Виправте очевидне вузьке місце спочатку (апаратне та топологія)
- Замініть або видаліть повільні пристрої.
- Переконайтеся, що vdev збалансовані; один повільний vdev задає темп.
- Підтвердіть налаштування контролера, прошивку, глибину черг і що ви не боретесь із неправильно налаштованим HBA.
Крок 3: Використовуйте керовані набори даних перед глобальними параметрами
Властивості dataset є зворотними і прив’язаними до конкретних наборів даних. Глобальні параметри модуля поділяються пулом і можуть створити зв’язки між орендарями.
recordsizeдля файлових систем: підберіть під розмір I/O (бази даних часто люблять 16K; бекапи — 1M).volblocksizeдля zvol: встановлюється при створенні; підберіть під шаблон блоків гостя/файлової системи.logbias: обирайтеlatencyдля синхронних низьколатентних навантажень; розгляньтеthroughputдля потокових шаблонів.sync: тримайтеstandard, якщо лише ви не готові явно поступитися довговічністю.
Крок 4: Тільки потім розглядайте таймінг TXG і ліміти dirty data
txg_timeout впливає на каденс. Налаштування dirty-data регулює, скільки роботи може накопичитися перед тим, як ZFS змусить зворотний тиск. Разом вони формують «амплітуду сплеску» і «частоту сплесків».
Практичні евристики, що працюють у реальному середовищі:
- Помірно знизьте
txg_timeout(наприклад, 5 → 3), якщо бачите періодичні сплески і система не вже домінує метаданими. - Будьте обережні при підвищенні; більші TXG можуть дати гіршу хвостову латентність і довший час відновлення після транзиторних затримок.
- Регулюйте dirty data max лише тоді, коли розумієте запас пам’яті і поведінку ARC. Занадто високо — більші сплески; занадто низько — постійне throttling.
Крок 5: Перевіряйте на реальному навантаженні, а не на синтетичному тріумфі
Запускайте саме те навантаження, яке болить: вашу базу даних, ферму VM, NFS трафік. Послідовні write-бенчмарки можуть «довести», що будь-яка погана ідея — добра, якщо підібрати розмір блоку і ігнорувати хвостову латентність.
Поширені помилки, симптоми та виправлення
Помилка 1: Ставитися до txg_timeout як до «інтервалу флашу», як у чекпоінтах баз даних
Симптом: Ви знижуєте txg_timeout, і сплески змінюють частоту, але хвостова латентність залишається поганою під навантаженням.
Що відбувається: Система обмежена пристроєм або синхронністю; ви змінили ритм, а не пропускну здатність.
Виправлення: Виміряйте wait по vdev; перевірте SLOG; перевірте швидкість синхронних записів; замініть повільні пристрої або додайте ємність перед подальшим налаштуванням.
Помилка 2: Відключення sync, щоб «вирішити» латентність
Симптом: Латентність виглядає дивовижно, а потім після відключення живлення ви відновлюєтеся з бекапів, пояснюючи «це був тест».
Що відбувається: sync=disabled перетворює синхронні запити на асинхронні. Це покращує продуктивність, змінюючи контракт.
Виправлення: Використайте належний SLOG; налаштуйте logbias; виправте латентність пристрою; залишайте sync у standard, якщо не готові до втрати даних.
Помилка 3: Додавання дешевого SSD як SLOG
Симптом: Синхронна латентність погіршується; час від часу стають гіршими зависання; іноді бачите таймаути лог-пристрою.
Що відбувається: SLOG потребує низької, передбачуваної латентності і захисту від втрати живлення. Диск із волатильним write cache і непередбачуваним GC перетворює fsync на рулетку.
Виправлення: Використовуйте enterprise-клас, PLP-спроможний пристрій; дзеркальте його; підтвердіть латентність через zpool iostat -l.
Помилка 4: Надмірне збільшення dirty data «бо RAM безкоштовна»
Симптом: Довші, некрасивіші сплески; іноді багатосекундні зависання під навантаженням.
Що відбувається: Більші dirty буфери означають більші коміти. Ви збільшили амплітуду сплеску. Під транзиторними уповільненнями ви також збільшили обсяг роботи, який має стікати, перш ніж писачі будуть звільнені.
Виправлення: Підійміть ліміти dirty під пропускну здатність пристрою і прийнятну хвостову латентність; підтримуйте ARC у здоровому стані; уникайте конкуренції за пам’ять з іншими сервісами.
Помилка 5: Ігнорування дисбалансу по vdev
Симптом: «Пул в цілому швидкий» за агрегованими метриками, але сплески латентності зберігаються.
Що відбувається: Один vdev або один пристрій повільний; синхронізація TXG чекає на найповільніший I/O. Середня пропускна здатність ховає хвостову поведінку.
Виправлення: Використовуйте zpool iostat -v -l, замінюйте викидного; перевіряйте контролер/прошивку на відповідність.
Помилка 6: Зміна п’яти налаштувань одночасно
Симптом: Система відчувається «іншою», але ви не можете сказати чому, і відкат лякає.
Що відбувається: Ви втратили здатність мислити причинно-наслідково.
Виправлення: Одна зміна, одна перевірка, один план відкату. Запишіть, що ви змінили і чому.
Контрольні списки / покроковий план
Контрольний список A: Підтвердьте, що бачите сплески комітів TXG
- Побудуйте графік латентності записів і подивіться, чи піки вирівнюються з ~5 секундами.
- Запустіть
zpool iostat -v 1 -lпід час сплесків; визначте, чи часи очікування стрибують з тією ж каденцією. - Перевірте
/sys/module/zfs/parameters/zfs_txg_timeoutі порівняйте з спостереженим каденсом. - Підтвердіть, чи log vdev (якщо є) показує збільшений wait під час сплесків.
- Перевірте
dmu_tx_delayабо еквівалентні лічильники; шукайте зростання під час подій.
Контрольний список B: Згладьте латентність, не порушуючи довговічності
- Підтвердіть апарат спочатку: замініть повільні/помилкові пристрої; підтвердіть уніформність прошивки.
- Підтвердіть поведінку sync: ідентифікуйте, які datasets/apps синхронно важкі; тримайте
sync=standard. - Виправте SLOG, якщо потрібно: дзеркальний, PLP-спроможний, з низькою латентністю; перевірте wait через
zpool iostat -l. - Налаштування dataset: встановіть
recordsizeвідповідно; перевіртеlogbias. - Тест невеликого txg_timeout: тимчасово змініть 5→3; спостерігайте хвостову латентність і пропускну здатність.
- Перевірка dirty data: переконайтесь, що ліміти dirty не створюють велетенських комітів або постійного throttling.
- Відкат, якщо зміни шкодять: віддавайте перевагу стабільній пропускній здатності і передбачуваній хвостовій латентності над гарнішим середнім.
Контрольний список C: Керування змінами, яке не мучитиме вас потім
- Зберіть «до» метрики: per-vdev wait, latency додатків, швидкість sync-записів.
- Змінюйте один параметр за раз з визначеним вікном спостереження.
- Документуйте гіпотезу («зменшення txg_timeout зменшить амплітуду сплесків коміту»).
- Визначте критерії успіху (p99 write latency, а не тільки середнє).
- Майте готову і перевірену команду відкату.
Питання й відповіді
1) Чи txg_timeout причина, що мої записи стрибають кожні п’ять секунд?
Воно може бути видимим метрономом, але не завжди причиною. Причина зазвичай у тому, що робота синхронізації TXG концентрується в часі — часто тому, що пристрої або лог синхронізації не можуть плавно поглинати навантаження. Перевірте кореляцію сплесків з wait по vdev і лічильниками зворотного тиску TXG.
2) Чи варто знизити txg_timeout, щоб згладити латентність?
Іноді — помірно. Зниження з 5 до 3 секунд може зменшити розмір сплеску, але також знизити пропускну здатність і збільшити накладні витрати. Якщо вузьке місце — повільний vdev або повільний SLOG, зниження txg_timeout лише змінить темп страждань.
3) Чи SLOG виправить TXG-сплески?
SLOG допомагає з латентністю синхронних записів (fsync‑важкі навантаження). Він не усуває роботу коміту TXG у основний пул. Багато середовищ мають обидві проблеми: SLOG виправляє «fsync-біль», а налаштування TXG/апарат — «комітний біль».
4) Чому стає гірше, коли пул майже повний?
Коли пулик заповнюється, виділення ускладнюється, фрагментація зростає, і оновлення метаданих можуть стати дорожчими. Синхронізація TXG може тривати довше, що збільшує час очікування dirty data, що підвищує throttling і відчутну пакетність.
5) Чи пов’язаний recordsize з txg_timeout?
Опосередковано. recordsize змінює форму I/O і накладні витрати на метадані. Менші записи можуть збільшити роботу метаданих і вимогу IOPS, роблячи синхронізацію TXG інтенсивнішою. Таймер не змінюється, але обсяг роботи за TXG — змінюється.
6) Який найнадійніший спосіб «згладити латентність» для баз даних на ZFS?
Тримайте sync=standard, використовуйте належний дзеркальний PLP SLOG для WAL/fsync-робочих навантажень, встановіть recordsize відповідно (часто 16K для багатьох баз даних) і потім усуньте викиди латентності по vdev. Розгляньте невеликі зміни txg_timeout лише після вирішення базових речей.
7) Якщо мені байдуже до довговічності, чи можна просто встановити sync=disabled?
Можете, але ставтесь до цього як до зняття пасків безпеки — вони можуть зіпсувати ваш одяг. Це може бути прийнятним для тимчасових даних, кешів або тестових середовищ з очевидним допуском втрати. Не застосовуйте для даних, які вам шкода втратити після збою.
8) Як зрозуміти, чи мене стримує ліміт dirty data?
Шукайте зростання затримок писачів (напр., dmu_tx_delay), і латентність додатка, що корелює з осциляцією dirty data і вікнами синхронізації TXG. Зазвичай бачите I/O сплески, а потім періоди, коли писачі блокуються більше, ніж зазвичай.
9) Чи може стиснення допомогти з TXG-сплесками?
Іноді. Стиснення може зменшити кількість байтів для запису, а отже і час пристрою під час синхронізації TXG, але воно збільшує навантаження на CPU і змінює шаблон I/O. Якщо ви обмежені пристроєм і маєте запас CPU, стиснення може зменшити амплітуду сплеску. Якщо ви вже обмежені CPU — може погіршити хвостову латентність.
10) Чи ця проблема специфічна для Linux?
Ні. TXG фундаментальні для ZFS на різних платформах. Що відрізняється — видимість (які лічильники доступні) і точні імена налаштувань і значення за замовчуванням.
Висновок
txg_timeout не робить ZFS «пакетним»; воно виявляє пакетування, яке ZFS використовує, щоб бути швидким і коректним. Ці сплески записів часто нормальні — доки вони не стають проблемою. Коли вони перетворюються на проблему латентності, зазвичай це тому, що фаза синхронізації TXG розтягується через повільний пристрій, тиск синхронних записів або накопичення dirty data, що запускає throttling.
Згладжування латентності переважно про повагу до конвеєра: переконайтеся, що найповільніший vdev не є таємно повільним, дайте синхронним навантаженням належний шлях низької латентності (SLOG), налаштуйте датасети під форму I/O і тільки потім регулюйте таймінг TXG і ліміти dirty data — обережно, вимірювально і з готовим відкатом. Мета не в тому, щоб зовсім позбутися сплесків; мета — зробити їх настільки малими, щоб додатки їх не помічали — і щоб ваш on-call не виробив Павлову реакцію на п’ятисекундні інтервали.