Ви не помічаєте write hole, коли все горить зеленим. Ви помічаєте його, коли індекс бази даних не відновлюється, файлову систему VM не вдається змонтувати
або тарбол одного клієнта не проходить перевірку контрольної суми — через місяці після «невинного» стрибка живлення.
Write hole — це еквівалент відсутньої свідчення: система «пам’ятає» частину операції запису, забуває іншу,
і впевнено повертає вам історію, якої ніколи не було. ZFS відома тим, що так не робить. Ось чому.
Write hole: що це (і чого це не стосується)
Класичний «write hole» — це проблема консистентності парності RAID, спричинена перерваним записом. Не «диск помер», не «контролер здох».
Це тонше: викид живлення, kernel panic, скидання контролера, проблеми з кабелем або збій прошивки влучають посеред оновлення стріпа.
Дані й блоки парності більше не збігаються.
Парний RAID (думайте RAID5/RAID6 і родичів) зберігає надмірність, обчислюючи парність над набором блоків у стріпі. Для простого RAID5-припасу
парність — це XOR блоків даних. Якщо ви оновлюєте блок даних, потрібно також оновити парність. Це означає кілька фізичних записів для одного логічного запису.
Якщо система впаде після одного з них, але до інших — ви створили стріп, який структурно виглядає валідним, але математично неконсистентний.
Цю неконсистентність не завжди помічають відразу. Насправді в цьому і полягає проблема: масив все ще стартує, файлову систему можна змонтувати,
і корупція сидить як міна, поки ви не прочитаєте «неправильний» блок (або поки не знадобиться реконструювати після виходу диска).
Чого write hole не є:
- Не те ж саме, що bit rot (пошкодження носія з часом). Bit rot може відбуватися без аварій; write hole потребує перерваного шляху запису.
- Не означає «ваш RAID марний». Це конкретний режим відмови, спричинений частковими оновленнями в системах з парністю.
- Не вирішується лише «більшою парністю». RAID6 зменшує ризик під час відновлення, але часткові оновлення парності все ще можуть створювати неконсистентні стріпи.
Ключове питання просте: коли ви записуєте, чи буває так, що на диску залишається суміш старих і нових даних/парності, яку система не може пізніше узгодити?
Якщо так — ризик write hole є.
Хто уразливий: RAID-рівні та стекі, які під ризиком
Write hole найчастіше асоціюють із RAID5, але правильна класифікація така: системи з парністю, де один логічний запис перетворюється на кілька фізичних записів,
і стек не гарантує атомарності між ними.
Ймовірно вразливі (якщо не застосовано пом’якшувальні заходи)
- RAID5 / RAID6 у багатьох програмних та апаратних RAID-реалізаціях, особливо без батарейного або флеш-підтримуваного кеша запису.
- mdadm RAID5/6 на Linux може бути вразливим, якщо бар’єри/флаші налаштовані неправильно, кеш писань підводить або відбувається крах під час оновлення.
- Класичні файлові системи поверх парного RAID (ext4, XFS, NTFS тощо) зазвичай не знають стану парності стріпа. Вони припускають, що блочний пристрій чесний.
- Сторонні сховища, які «оптимізують» перестановку записів без наскрізної цілісності можуть посилити проблему.
Менш вразливі за дизайном
- Міри (RAID1, RAID10): запис дублюється; на рівні блоків можуть бути torn writes, але зазвичай це не отруює обчислення парності.
- ZFS на мірах: додає контрольні суми + copy-on-write, отже не поверне «порваний» блок як «добрий».
- ZFS RAIDZ: парність вбудована в транзакційний copy-on-write механізм; це уникає класичної поведінки write hole.
«Але в мого контролера є кеш»
Кеш допомагає, якщо він стійкий після втрати живлення і якщо контролер коректно виконує flush та порядок записів.
Батарейний кеш запису (BBWC) чи флеш-підтримуваний кеш (FBWC) можуть сильно знизити ризик.
«Write-back кеш без стійкості» — це швидкість з азартною звичкою.
Жарт №1: RAID-контролер без реального захисту від втрати живлення — як парашут, зшитий з оптимізму: ви дізнаєтесь, що він несправжній, лише раз.
Чому це трапляється: часткові стріпи, парність та «порвані записи»
Уявімо RAID5 масив з трьома дисками: два блоки даних і один блок парності на стріп. Оновіть один 4K блок у стріпі.
Масив має оновити парність, щоб вона відповідала двом блокам даних.
Є два класичні методи оновлення парності:
- Read-modify-write (RMW): читають старий блок даних і стару парність, обчислюють нову парність = old_parity XOR old_data XOR new_data, потім записують нові дані і нову парність.
- Reconstruct-write: читають всі інші блоки даних у стріпі, обчислюють парність заново, записують оновлені дані й нову парність.
У будь-якому випадку відбувається кілька читань і кілька записів. Це добре, якщо послідовність завершується. Write hole з’являється, коли ні.
Найгірше те, що крах може перервати процес у багатьох місцях:
- Нові дані записано, парність залишилась старою.
- Парність записано, дані залишились старими.
- Півсектора оновлено («torn write»), залежно від семантики пристрою і поведінки кеша.
- Записи переставлено кешем/контролером/прошивкою, всупереч очікуванням ОС.
Після перезавантаження масив не має пам’яті того, що відбувалося в польоті. Стріп — це просто стріп.
На рівні RAID немає постійного журналу транзакцій, який би сказав «оновлення парності було в процесі; будь ласка, владнайте це».
Багато стеків просто довіряють парності й рухаються далі.
Найгірший час для виявлення неконсистентного стріпа — під час відновлення, коли ви втрачаєте диск і парність стає вашим джерелом істини.
Якщо парність неправильна, реконструкція дасть сміття — і дасть його впевнено.
Чому ZFS уникає цього: COW, контрольні суми і транзакційні коміти
ZFS не «магічно» долає фізику. Воно використовує модель зберігання, яка робить класичний сценарій write hole структурно складним для виникнення і
простим для виявлення. Три опори мають значення: copy-on-write, наскрізні контрольні суми і транзакційна семантика комітів.
1) Copy-on-write (COW): ніколи не перезаписуйте живі дані на місці
Традиційні файлові системи часто перезаписують метадані і дані на місці (журналювання лише зменшує, але не усуває, деякі ризики).
ZFS підходить інакше: коли змінюється блок, виділяється новий блок десь ще, записується новий вміст туди, і лише потім
оновлюються вказівники на новий блок.
Це оновлення вказівників також робиться через COW, піднімаючись вгору по дереву аж до верхнього метаданого блоку (uberblock).
Стара версія залишається недоторканою на диску, поки нова не буде закомічена. Якщо ви падаєте під час запису, ви зазвичай повертаєтесь до попереднього
консистентного дерева. Ви не опиняєтесь з «напів старими, напів новими» метаданими, які вдають собою цілісну файлову систему.
2) Накрізні контрольні суми: виявляють брехню і корупцію
ZFS зберігає контрольну суму для кожного блока в його батьківських метаданих. Коли ви читаєте блок, ZFS перевіряє контрольну суму. Якщо вона не сходиться,
ZFS знає, що блок неправильний. Це не «можливо помилковий». Це математично неконсистентний блок.
На редундантних vdev (міри, RAIDZ) ZFS може потім відновити: читає альтернативну копію/реконструкцію парності, знаходить версію, яка збігається
з очікуваною контрольною сумою, повертає добрі дані і за потреби лікує погану копію. Контрольна сума робить корупцію виявною; надмірність — виправною.
3) Транзакційні групи (TXG): атомарні (у сенсі файлової системи) зміни стану
ZFS пакує зміни в транзакційні групи. В пам’яті воно акумулює «брудні» дані і метадані, потім періодично синхронізує TXG на диск.
«Точка коміту» — це новий uberblock, записаний на диск, який вказує на нове дерево. Uberblock-и записуються циклічно, тож ZFS може вибрати
найновіший валідний при імпорті.
Це важливо для write hole, бо ZFS не потребує робити in-place оновлення парності існуючих блоків, щоб «поправити» поточну істину.
Воно записує нові блоки, нову парність і лише потім переключає кореневий вказівник. Якщо крах відбувся до переключення, нові блоки стають сиротами
і пізніше прибираються; стара консистентна версія все ще на диску.
Там, де був write hole, ZFS ставить замкнені двері
Класичний write hole: «Я оновив дані, але не парність, тепер стріп неконсистентний і ніхто про це не знає».
Модель ZFS: «Я записав нові дані+парність у нові місця. Якщо я не закінчив, активне дерево все ще вказує на старі консистентні блоки».
ZFS все ще може зазнати перерваних записів, але має дві переваги:
- Консистентність активного дерева: стан на диску, обраний при імпорті, є когерентною знімкою файлової системи.
- Виявність: якщо блок пошкоджений, ZFS дізнається через контрольну суму; воно не приймає його мовчки за добрий.
Одне зауваження варте стікера поряд із вашим стійком:
«Сподівання — не стратегія.» — генерал Gordon R. Sullivan
Особливості RAIDZ: змінна ширина стріпа і що змінюється
RAIDZ — це парний RAID ZFS, але це не «RAID5 вбудований у ZFS» в наївному розумінні. RAIDZ інтегрований у модель розподілу простору і транзакцій ZFS.
У класичному RAID5 логічний запис малого блоку часто змушує read-modify-write цикл по всьому стріпу. Саме тут часткові оновлення стріпів
стають небезпечними, особливо якщо система перезаписує існуючі блоки на місці.
RAIDZ робить інакше: він використовує змінну ширину стріпа. ZFS може пакувати дані по дисках залежно від фактичного розміру запису і розташування блоків,
замість того, щоб примусово дотримуватись фіксованих меж стріпа. Тут теж є парність, але розподіл координується з COW-оновленнями.
Ключова операційна наслідок: парність RAIDZ обчислюється для щойно виділених блоків і записується в рамках синку TXG.
Вона не ретрофітиться у вигляді in-place оновлень парності на живих стріпах так, як це робить класичний RAID5 RMW.
Це не означає, що RAIDZ непереможний. Ви все ще можете втратити дані, якщо втратите більше пристроїв, ніж дозволяє парність, або якщо система «бреше»
щодо flush-ів і ви втрачаєте найновіший(і) commit-uberblock(и). Але класичний «тихий розбіг парності, створений посеред запису і пізніше використаний як істина» — не типовий режим відмови.
Побічний квест: кеші, бар’єри й «я клянусь, воно записалось»
Дискусія про write hole завжди тягне за собою тему кешів, бо більшість реальних корупцій — це не «помилка ZFS» чи «помилка mdadm».
Це «стек зберігання сказав, що це на стабільному носії, але насправді воно лежало в леткому кеші».
Ваша ОС використовує flush-і (FUA/flush/barriers), щоб сказати: «запишіть це на нестираючий носій перед підтвердженням». Якщо пристрій/контролер ігнорує це,
все вище працює на фантазійному таймлайні.
ZFS не імунний до брехні. ZFS припускає, що коли воно посилає flush і отримує підтвердження, дані надійні. Якщо ваше обладнання каже «гаразд»,
а потім втрачає живлення, ви можете загубити останні TXG(и). Зазвичай це означає втрату останніх записів, а не корупцію старих закомічених даних, але радіус ураження залежить від того, наскільки «нечесний» пристрій.
Саме тут у розмову вступають SLOG, налаштування sync і захист від втрати живлення. Якщо ви ставите sync=disabled заради бенчмарків,
ви добровільно повертатиметесь до уроків про те, чому бази даних наполягають на fsync.
Жарт №2: Поставити sync=disabled в продакшені — це як зняти детектор диму, бо він пищить, коли є дим.
Цікаві факти та історичний контекст
- Факт 1: Write hole обговорювали в RAID-літературі задовго до SSD; воно пов’язане з семантикою оновлення парності, а не з особливостями флешу.
- Факт 2: Постачальники апаратних RAID просували BBWC частково тому, що це робить багатозаписні оновлення «достатньо атомарними» під час втрати живлення.
- Факт 3: Ранні корпоративні масиви впроваджували «журнал парності» або журналювання на шарі RAID, щоб узгоджувати незавершені оновлення стріпів після краху.
- Факт 4: Журналювання файлової системи (наприклад ext3/ext4) захищає метадані файлової системи, але зазвичай не може виправити mismatch парності внизу.
- Факт 5: Накрізне контрольне сумування ZFS було прямою реакцією на тиху корупцію в стеках зберігання — контролери, прошивка і навіть DMA можуть помилятися.
- Факт 6: Концепція uberblock ZFS дає кілька недавніх точок коміту; при імпорті ZFS може вибрати найновішу валідну.
- Факт 7: Репутація RAID5 погіршилася з ростом розмірів дисків; вікно відновлення стало довшим, тож зросла ймовірність натрапити на невідновлювану помилку читання.
- Факт 8: «Write hole» — це не лише втрата живлення; будь-яке переривання посеред операції (panic, скидання HBA, flapping лінку) може дати такий самий результат.
- Факт 9: Деякі SSD раніше підтверджували flush-и зарано через баги прошивки; інженери з надійності навчились не довіряти дешевим «enterprise» маркуванням флешу.
Три короткі історії зі світу корпоративних інцидентів
Міні-історія 1: Інцидент через хибне припущення
Середня SaaS-компанія тримала основний кластер PostgreSQL на Linux software RAID6 з «респектабельним» HBA в IT-режимі.
Припущення було простим: RAID6 = «безпечно», журнал ext4 = «консистентно». У стійці були UPS, тому події з живленням вважалися теоретичними.
Потім сталося техобслуговування будівлі. Живлення не впало повністю; воно просіло, підстрибнуло, і сервери перезавантажились. Логи UPS показали короткий перехід,
потім другий. Хости повернулись. Моніторинг був зеленим. Всі перейшли далі.
Через два тижні вийшов диск — нормальна подія. Під час відновлення mdadm почав викидати помилки читання, які не відповідали показникам SMART.
Потім PostgreSQL почав проходити перевірку контрольних сум у таблиці, яка не змінювалася з моменту стрибка живлення. Файли даних виглядали «добре»,
поки раптом — ні.
Постмортем вказав на ймовірну неконсистентність стріпа: деякі стріпи мали парність, яка відображувала нові блоки даних, тоді як інші — стару парність і нові дані.
Журнал файлової системи зробив своє, але він не мав шансів: він працював поверх блочного шару, який повертав пошкоджені блоки як валідні.
Хибне припущення було не в тому, що «RAID6 поганий». Хибним було вірити, що блочний шар завжди зберігає порядок записів і атомарність під час оновлення парності.
Вони мігрували до ZFS на мірах для баз даних і резервували RAIDZ2 для великого об’єктного сховища, де scrubs і контрольні суми давали виявлення і виправлення.
Міні-історія 2: Оптимізація, що повернулась бумерангом
Інша організація — внутрішня аналітична платформа, багато пакетних завдань — працювала на OpenZFS з RAIDZ2. Продуктивність була прийнятною, доки не завантажили нове навантаження:
сервіс інжестії з чергою, що робив багато малих синхронних записів. Латентність підскочила.
Хтось зробив те, що завжди роблять: прогуглив «ZFS slow sync writes», побачив чарівну фразу sync=disabled і застосував її до dataset.
Графіки заспокоїлись. Квитки припинились. Переможний круг.
Через місяці відбувся kernel panic під час рутинного оновлення драйвера. Після перезавантаження пул імпортувався чисто. Помилок не було.
Але інжестуючий сервіс почав повторно відправляти повідомлення, які вважалися закоміченими, але фактично не були на стабільному носії.
Система прийняла дублікати й перезаписала похідні таблиці. Нічого не вибухнуло голосно; просто стало тихо неправильним.
Повернення бумеранга не було корупцією ZFS. Це була семантична корупція: контракт стійкості програми було порушено. Вимкнувши sync, вони перетворили
«підтвердження після надійного коміту» на «підтвердження після того, як RAM відчула себе добре». Виправлення було нудним: повернути sync=standard,
додати правильний SLOG з захистом від втрати живлення і налаштувати батчинг у застосунку, щоб не fsync-ити кожне чихання.
Міні-історія 3: Нудна, але коректна практика, що врятувала день
Команда фінансових сервісів тримала ZFS на мірах для VM datastore і RAIDZ2 для бекапів. Їхній інженер зі зберігання був агресивно неромантичний:
щомісячні scrubs, тести SMART і алертинг навіть на одиничні checksum-помилки. Вони також відмовлялися купувати SSD без зрозумілого захисту від втрати живлення.
Одного кварталу нова партія дисків почала повертати час від часу checksum-помилки під час scrub. Не помилки читання. Не провали SMART. Просто mismatch контрольної суми,
який ZFS коригував за надмірністю. Дашборди показували «самовиліковні» події і повільне зростання виправлених помилок.
Закупівля хотіла ігнорувати це, бо навантаження працювало нормально. Інженер наполіг: бюджет помилок — це не ощадний рахунок. Вони вивели пул цієї партії
протягом кількох вікон техобслуговування, замінили диски і відправили на RMA.
Через два місяці інша команда в тій самій будівлі (інший постачальник) отримала неприємний інцидент: тиха корупція виявилась під час тесту відновлення.
Команда фінансових сервісів навіть не мала інциденту. Їхня нудна, але правильна практика виявила невидиму корупцію в планову роботу замість раптового збою.
Практичні завдання: команди, виходи та рішення
Це реальні кроки оператора. Кожне завдання містить команду, що означає її вихід і що робити далі.
Приклади припускають OpenZFS на Linux, але більшість перекладаються на illumos/FreeBSD з незначними змінами.
Завдання 1: Підтвердити стан pool і чи є у вас вже checksum-помилки
cr0x@server:~$ zpool status -v tank
pool: tank
state: ONLINE
status: One or more devices has experienced an unrecoverable error.
action: Determine if the device needs to be replaced, and clear the errors
scan: scrub repaired 0B in 02:13:44 with 2 errors on Sun Dec 22 03:10:18 2025
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
ata-WDC_WD80EFAX-00A0 ONLINE 0 0 2
ata-WDC_WD80EFAX-00A1 ONLINE 0 0 0
ata-WDC_WD80EFAX-00A2 ONLINE 0 0 0
ata-WDC_WD80EFAX-00A3 ONLINE 0 0 0
errors: Permanent errors have been detected in the following files:
tank/vmstore:vm-102-disk-0
Що це означає: ZFS виявив погані дані через контрольну суму і не зміг повністю їх відновити (permanent error), або відновив, але все одно записав уражені файли.
Рішення: Трактуйте це як інцидент у продукції. Ідентифікуйте уражене навантаження, відновіть конкретний файл/том з бекапу або репліки,
і заплануйте заміну диска, якщо помилки продовжують зростати.
Завдання 2: Перевірити, чи ваш vdev-ланцюг оснований на парності (де класичний write hole має значення)
cr0x@server:~$ zpool status tank
pool: tank
state: ONLINE
scan: none requested
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
Що це означає: RAIDZ2 парний vdev. ZFS уникатиме класичних шаблонів write hole, але вам все ще треба піклуватись про чесність flush-ів і sync-семантику.
Рішення: Якщо пул містить бази даних з великим числом синхронних записів, оцініть необхідність SLOG і налаштувань dataset. Якщо це bulk storage — орієнтуйтесь на частоту scrub і здоров’я дисків.
Завдання 3: Підтвердити політику sync і чи хтось «оптимізував» стійкість
cr0x@server:~$ zfs get -o name,property,value -s local,default sync tank tank/db tank/vmstore
NAME PROPERTY VALUE
tank sync standard
tank/db sync standard
tank/vmstore sync disabled
Що це означає: tank/vmstore підтверджує синхронні записи без гарантії на стабільний носій.
Рішення: Якщо тут розміщені VM, бази даних або щось, що очікує, що fsync щось гарантує, поверніть sync=standard.
Якщо ви залишаєте disabled, документуйте вікно втрати даних і отримайте явне погодження.
Завдання 4: Перевірити наявність SLOG і що це за пристрій
cr0x@server:~$ zpool status tank
pool: tank
state: ONLINE
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
logs
nvme0n1p2 ONLINE 0 0 0
Що це означає: Існує окремий лог-пристрій. Він може прискорювати синхронні записи якщо має захист від втрати живлення і низьку латентність.
Рішення: Перевірте, що NVMe — корпоративний пристрій з PLP. Якщо ні, ви можете торгувати латентністю за цілісність при втраті живлення.
Завдання 5: Підтвердити ashift (некоректне вирівнювання може видаватися як «повільна парність ZFS»)
cr0x@server:~$ zdb -C tank | grep -E "ashift|vdev_tree" -n | head
35: vdev_tree:
52: ashift: 12
89: ashift: 12
126: ashift: 12
Що це означає: ashift=12 = 4K сектора. Хороший дефолт для сучасних дисків/SSD.
Рішення: Якщо ви бачите ashift=9 на 4K дисках — очікуйте проблем. Виправлення вимагає створення vdev заново (без можливості змінити in-place).
Завдання 6: Перевірити recordsize/volblocksize щодо навантаження (малі записи + RAIDZ можуть боліти)
cr0x@server:~$ zfs get -o name,property,value recordsize,volblocksize tank/db tank/vmstore tank/vmstore/vm-102
NAME PROPERTY VALUE
tank/db recordsize 128K
tank/vmstore recordsize 128K
tank/vmstore/vm-102 volblocksize 8K
Що це означає: Датасети використовують recordsize. ZVOL-и використовують volblocksize. Невідповідність може спричиняти write amplification.
Рішення: Для VM zvol-ів оберіть volblocksize, що відповідає гостьовій ОС і навантаженню (часто 8K або 16K). Для баз даних на датасетах розгляньте менший recordsize, якщо IO випадковий і малий, але виміряйте наслідки.
Завдання 7: Спостерігати реальний IO і зрозуміти, чи ви обмежені sync
cr0x@server:~$ zpool iostat -v tank 1 5
capacity operations bandwidth
pool alloc free read write read write
-------------------------- ----- ----- ----- ----- ----- -----
tank 3.21T 11.3T 112 980 22.4M 18.1M
raidz2-0 3.21T 11.3T 112 980 22.4M 18.1M
sda - - 18 160 3.7M 4.2M
sdb - - 19 165 3.8M 4.3M
sdc - - 18 162 3.7M 4.2M
sdd - - 18 158 3.6M 4.1M
nvme0n1p2 - - 0 510 0 6.1M
-------------------------- ----- ----- ----- ----- ----- -----
Що це означає: Лог-пристрій приймає багато записів. Це відповідає синхронному навантаженню, яке поглинає SLOG.
Рішення: Якщо латентність все ще висока, SLOG може бути повільним або насиченим. Розгляньте швидший PLP NVMe або зменшення частоти sync на рівні застосунку (батчинг).
Завдання 8: Перевірити поведінку TXG sync і чи ви трясе при синках
cr0x@server:~$ grep -E "txg_sync|spa_sync" /proc/spl/kstat/zfs/* 2>/dev/null | head
/proc/spl/kstat/zfs/dbgmsg:txg_sync_start txg 89213
/proc/spl/kstat/zfs/dbgmsg:spa_sync: syncing tank txg 89213
/proc/spl/kstat/zfs/dbgmsg:txg_sync_done txg 89213
Що це означає: Ви бачите цикли синку. Часті синки під навантаженням можуть означати, що маленькі синхронні записи примушують коміти.
Рішення: Корелюйте з fsync-паттернами застосунку. Якщо це БД, налаштуйте її чекпойнтинг/батчинг; якщо це NFS, перевірте mount-опції клієнта і семантику sync.
Завдання 9: Перевірити налаштування кешу запису дисків (і вирішити, чи відключати леткі кеші)
cr0x@server:~$ sudo hdparm -W /dev/sda
/dev/sda:
write-caching = 1 (on)
Що це означає: Кеш запису диска увімкнено. Це не автоматично погано, якщо flush-и виконуються і у вас є захист від втрати живлення.
Рішення: Якщо ви не довіряєте платформі (немає PLP, ненадійні SATA-expander-и, історія багів з flush), розгляньте відключення кеша:
hdparm -W0 (з тестуванням). Готуйтеся до падіння продуктивності.
Завдання 10: Підтвердити TRIM/autotrim для SSD-пулів (не про write hole, але запобігає деградації продуктивності)
cr0x@server:~$ zpool get autotrim tank
NAME PROPERTY VALUE SOURCE
tank autotrim on local
Що це означає: ZFS автоматично надсилатиме TRIM. Це допомагає стабільній продуктивності і розподілу зносу SSD.
Рішення: Тримайте його увімкненим для SSD, якщо тільки у вас немає пристрою, який погано поводиться під TRIM (рідко зараз, частіше в старі часи).
Завдання 11: Запустити scrub і зрозуміти, що означає «repaired» vs «errors»
cr0x@server:~$ sudo zpool scrub tank
cr0x@server:~$ zpool status tank
pool: tank
state: ONLINE
scan: scrub in progress since Thu Dec 26 01:12:40 2025
512G scanned at 1.21G/s, 96G issued at 233M/s, 3.21T total
0B repaired, 2.99% done, 0:21:34 to go
config:
NAME STATE READ WRITE CKSUM
tank ONLINE 0 0 0
raidz2-0 ONLINE 0 0 0
sda ONLINE 0 0 0
sdb ONLINE 0 0 0
sdc ONLINE 0 0 0
sdd ONLINE 0 0 0
errors: No known data errors
Що це означає: Scrub читає і перевіряє контрольні суми. «0B repaired» — добре. «No known data errors» — саме те, чого прагнете.
Рішення: Якщо scrubs регулярно виправляють дані, у вас проблема надійності (диски, кабелі, backplane, HBA, пам’ять).
Якщо scrubs знаходять permanent errors — починайте відновлення/рестор для уражених датасетів і досліджуйте апаратне забезпечення.
Завдання 12: Перевірити стиснення і його вплив на IO-патерни
cr0x@server:~$ zfs get -o name,property,value compression,compressratio tank/db
NAME PROPERTY VALUE
tank/db compression lz4
tank/db compressratio 1.78x
Що це означає: Стиснення увімкнено і ефективне. Це зменшує фізичні записи для того ж об’єму логічних записів.
Рішення: Зазвичай тримайте lz4. Якщо латентність домінує процесор (рідко на сучасних CPUs), виміряйте перед зміною.
Завдання 13: Перевірити поведінку ARC (бо люди звинувачують «ZFS write hole», коли це насправді тиск пам’яті)
cr0x@server:~$ arcstat 1 3
time read miss miss% dmis dm% pmis pm% mmis mm% arcsz c
01:22:10 845 47 5 12 26 35 74 0 0 58G 64G
01:22:11 812 55 6 14 25 41 75 0 0 58G 64G
01:22:12 901 50 5 13 26 37 74 0 0 58G 64G
Що це означає: Низький miss rate, ARC близький до цілі. Читання в основному обслуговуються з кешу.
Рішення: Якщо miss% високий і диски завантажені, розгляньте більше RAM або L2ARC (обережно). Але не використовуйте L2ARC як заміну вирішенню проблеми з sync-записами.
Завдання 14: Виявити «брехливий» пристрій: підтвердити кеш запису, підтримку flush і повідомлення ядра
cr0x@server:~$ dmesg | grep -iE "flush|fua|barrier|cache" | tail -n 8
[ 2.913244] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[ 2.914881] sd 0:0:0:1: [sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[ 2.916402] sd 0:0:0:2: [sdc] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[ 2.918103] sd 0:0:0:3: [sdd] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
Що це означає: «doesn’t support DPO or FUA» не є автоматично фатальним (flush-и можуть і далі працювати), але це підказка:
стек може покладатися на cache flush замість force-unit-access семантики.
Рішення: Якщо це синхронно-важливий pool, віддавайте перевагу пристроям/HBA, які передбачувано поводяться з flush-ами і мають PLP там, де потрібно.
Розгляньте міри для баз даних.
Швидкий план діагностики
Коли хтось каже «ZFS пошкоджено» або «ми влучили в write hole», ваше завдання — відокремити три речі:
(1) відмови цілісності даних, (2) семантика стійкості (durability), і (3) вузькі місця продуктивності. Швидко.
Перше: визначте, чи маєте ви зараз помилки даних
- Перевірте стан pool:
zpool status -v. Якщо бачите checksum або permanent errors — трактуйте як активний інцидент. - Перевірте останні scrubs: у рядку scan в
zpool status. Якщо scrubs недавно відновлювали дані — знайдіть джерело корупції. - Перевірте системні логи:
dmesgна предмет скидань лінків, помилок I/O, таймаутів HBA. Якщо транспорт ненадійний — все вище є жертвою.
Друге: підтвердьте, чи скарга — фактично втрачені підтверджені записи
- Перевірте sync-налаштування датасетів:
zfs get sync. Якщо бачитеdisabled, то швидше за все це порушення контракту стійкості, а не корупція. - Перевірте наявність/стан SLOG: секція logs в
zpool status. Якщо відсутній і навантаження синхронне — латентність очікувана. - Запитайте, що саме «зникло»: Чи відправив застосунок ack після fsync? Якщо так, досліджуйте налаштування
sync, PLP і чесність flush-ів.
Третє: знайдіть вузьке місце (CPU, диск, sync, фрагментація або заповненість)
- Огляд IO:
zpool iostat -v 1, щоб побачити, чи диски або SLOG насичені. - Тиск по місцю:
zfs listі відсоток алокації пулу. Пули, що майже повні, фрагментуються і уповільнюють записи. - Невідповідність навантаження: малі випадкові записи на RAIDZ без налаштувань можуть бути повільними; розгляньте міри для латентніших датасетів.
Поширені помилки: симптоми → корінь → виправлення
1) «Ми перезавантажилися і тепер база даних неконсистентна»
- Симптоми: БД повідомляє про відсутні нещодавні транзакції; застосунок відтворює повідомлення; немає checksum-помилок ZFS.
- Корінь проблеми:
sync=disabled(або застосунок покладався на fsync, а стек не надав цієї гарантії), або леткий кеш «заговорив неправду». - Виправлення: Встановіть
sync=standard; додайте SLOG з PLP при потребі; перевірте UPS і протестуйте поведінку при втраті живлення; переконайтесь, що обладнання виконує flush-і.
2) «Scrub постійно щомісяця виправляє дані»
- Симптоми:
zpool statusпоказує відновлені байти або зростаючі counters checksum; навантаження здаються нормальними. - Корінь проблеми: Тиха корупція від дисків, кабелів, backplane, HBA, прошивки або пам’яті (так, RAM має значення).
- Виправлення: Замініть підозрілі диски; перемістіть/замініть кабелі; оновіть прошивку HBA; запустіть memtest; тримайте scrubs; не ігноруйте виправлені помилки.
3) «RAIDZ повільний для VM-стореджу»
- Симптоми: Висока латентність, особливо на sync-записах;
zpool iostatпоказує багато малих записів; CPU здебільшого простий. - Корінь проблеми: Навантаження — малі випадкові записи; парний RAID має write amplification; невідповідність volblocksize/recordsize; відсутній SLOG для синхронних паттернів.
- Виправлення: Використовуйте міри для VM/БД; налаштуйте
volblocksize; додайте PLP SLOG для sync; залишайте RAIDZ для навантажень з пропускною здатністю.
4) «Після краху в декількох файлах є permanent errors»
- Симптоми:
zpool status -vперераховує конкретні файли з permanent errors; checksum-помилки на диску. - Корінь проблеми: Фактична корупція на диску, яку надмірність не змогла вилікувати (наприклад, перевищена парність, погані сектори на кількох дисках або застарілі репліки).
- Виправлення: Відновіть уражені файли/томи з бекапу/снапшот-репліки; замініть обладнання; запустіть scrub; перевірте бекапи через тест відновлення.
5) «Після додавання SSD-кеша стало гірше»
- Симптоми: Стрибки латентності; SSD завантажений на 100%; мало покращення у hit rate.
- Корінь проблеми: Неправильно використаний L2ARC або слабкий SSD; кешується неправильне навантаження; тиск пам’яті через метадані L2ARC.
- Виправлення: Видаліть або змініть розмір L2ARC; додавайте RAM насамперед; якщо проблема — sync-записи, фокусуйтесь на SLOG, а не на L2ARC.
Контрольні списки / покроковий план
Контрольний список: проектування ZFS, щоб не повернути біль, схожий на write hole
- Обирайте vdev під вимоги латентності: міри для БД/VM; RAIDZ2/3 для bulk storage і бекапів.
- Припускайте, що події з живленням трапляються: UPS допомагає, але тестуйте поведінку. Brownout-и і подвійні переключення реальні.
- Не вимикайте sync легковажно: якщо мусите — ізолюйте до одного датасету і документуйте ризик.
- Використовуйте PLP-пристрої там, де важлива стійкість: особливо для SLOG і метаданих-важких навантажень.
- Плануйте scrubs: щомісяця — звична практика; частіше для критичних пулів з великим обігом даних.
- Алертуйте на checksum-помилки: виправлені помилки — раннє попередження, а не привід для святкування.
- Перевіряйте бекапи через тест відновлення: «бекап пройшов» ≠ «вдалося відновити».
- Тримайте резерв по місцю: не доводьте пул майже до повного; фрагментація й тиск алокації вас покарають.
Покроково: розслідування «можливого write hole» в парному стеку
- Зберіть таймлайн: що упало, коли і що писало в той момент.
- Шукайте симптоми часткових записів: неконсистентності на рівні застосунку без відповідних помилок читання диска.
- Перевірте леткі кеші: кеш запису диска, режим кешу RAID-контролера, відсутність BBWC/FBWC.
- Валідуйте семантику flush-ів: логи ядра; налаштування контролера; віртуалізаційні прошарки.
- Запустіть перевірки консистентності: для ZFS — scrub; для mdadm — consider check/repair (обережно); для баз даних — внутрішні контрольні суми.
- Вирішіть ремедіацію: відновлення з відомо-правильних копій, реконструкція з реплік або міграція до дизайну з наскрізною цілісністю.
Часті питання
1) Чи повністю ZFS усуває write hole?
ZFS уникає класичного write hole парних RAID завдяки тому, що не робить in-place оновлень і комітить зміни транзакційно.
Але ZFS все ще покладається на апарат, який виконує flush-и чесно. Якщо пристрої «брешуть», ви можете втратити останні записи.
2) Чи RAIDZ — це просто RAID5 під іншим брендом?
Ні. RAIDZ інтегрований із системою алокації ZFS і поведінкою copy-on-write, підтримує змінну ширину стріпа і захищений наскрізними контрольними сумами.
Він поводиться інакше при часткових оновленнях, ніж класичні RMW-паттерни RAID5.
3) Якщо я використовую RAIDZ2/3, чи можу я пропустити бекапи?
Ні. Парність захищає від відмови диска, але не від видалення, ransomware, помилок застосунку або «я випадково перезаписав неправильний датасет».
ZFS snapshots допомагають, але вони не є зовнішніми бекапами, якщо не репліковані в інше місце.
4) Чому міри рекомендують для баз даних?
Через латентність. Міри мають простіший шлях запису і зазвичай краще IOPS для випадкових навантажень. RAIDZ добре для ємності та пропускної здатності,
але парність і write amplification можуть нашкодити малим випадковим sync-записам.
5) Чи потрібен мені SLOG-пристрій?
Лише якщо у вас значні синхронні записи і стандартні пристрої пулу занадто повільні для ваших цільових латентностей.
SLOG не є загальним кешем записів; він прискорює ZIL для sync-записів. Не додавайте дешевий SSD без PLP і не називайте це «безпечнішим».
6) Що означає «checksum error» у zpool status?
ZFS прочитав дані, які не збіглися з контрольною сумою в метаданих. Це доказ корупції в шляху читання або на носії.
З надмірністю ZFS часто може самовилікувати, читаючи добру копію.
7) Чи може журналювання ext4 запобігти корупції RAID5 write hole?
Журналування ext4 може зберегти консистентність метаданих файлової системи після краху, але воно не виправить mismatch парності в нижньому шарі.
Воно припускає, що блочний пристрій дає когерентні блоки за запитом.
8) Чи вирішує проблему UPS?
UPS знижує ризик, але не усуває його. Крахи, kernel panic, скидання контролера і баги прошивки все ще переривають послідовності записів.
Також багато «подій живлення» — це складні brownout-и і перемикання, а не чиста втрата живлення.
9) Якщо ZFS безпечний, чому люди все ще гублять дані?
Бо безпека — це властивість системи. Типові винуватці: втрата надмірності, погане обладнання, «брехливі» кеші, вимкнений sync,
ігнорування checksum-помилок та відсутність тестів відновлення.
10) Яка найпростішя операційна звичка покращує інтегритет ZFS?
Регулярні scrubs з алертингом на будь-які checksum-помилки. Scrub перетворює тиху корупцію на завдання, яке можна обробити у робочий час.
Практичні подальші кроки
Якщо ви використовуєте парний RAID поза ZFS, припускайте, що write hole існує, поки не доведено протилежне. Якщо ви працюєте на ZFS, припускайте, що ваше обладнання
все ще може брехати, а ваші оператори все ще можуть «оптимізувати» стійкість. Обидва припущення — здорові.
- Інвентаризація ризиків: ідентифікуйте парні масиви, режими кешу і чи є кеш запису стійким.
- Аудит властивостей ZFS: знайдіть будь-які датасети з
sync=disabledі вирішіть, чи це дійсно потрібно. - Розклад scrubs + алертинг: забезпечте запуск scrubs і виклик відповідаючого персоналу при checksum-помилках.
- Вирівняйте дизайн під навантаження: міри для латентності, RAIDZ для ємності/пропускної здатності, і не прикидайте, що вони взаємозамінні.
- Протестуйте сценарій втрати живлення: не витягайте штепсель у продакшені, але перевірте, що платформа виконує flush-и і що застосунки переносять crash-recovery.