MAATRIX / Блог / Барьеры записи и кеш диска, который врёт о сохранности ваших данных

Барьеры записи и кеш диска, который врёт о сохранности ваших данных

MAATRIX

Приложение вызывает fsync(), ядро говорит «записано», СУБД коммитит транзакцию клиенту — а через секунду пропадает питание, и после перезагрузки этой транзакции в данных нет. Формально всё было сделано правильно: и приложение, и файловая система, и ядро отработали по спецификации. Проблема в одном звене цепи, которое приложение вообще не видит, — в кеше записи самого диска или RAID-контроллера, который может подтвердить запись раньше, чем данные реально легли на энергонезависимый носитель.

Зачем диску собственный кеш записи

У жёсткого диска, SSD и RAID-контроллера есть небольшая быстрая память — обычно от нескольких десятков до нескольких сотен мегабайт DRAM, у контроллеров иногда с батарейным или конденсаторным резервированием. Она нужна не для того, чтобы «ускорить всё подряд», а чтобы устройство могло:

  • принять команду записи мгновенно и сразу освободить очередь для следующей, не дожидаясь физической записи на пластины или в NAND;
  • переупорядочить несколько команд записи так, чтобы головке HDD не пришлось прыгать туда-обратно (write reordering/coalescing);
  • объединить мелкие смежные записи в одну более крупную операцию, что особенно важно для SSD, где запись всегда идёт блоками, а не отдельными байтами.

Без такого кеша устройство ждало бы завершения каждой физической операции перед приёмом следующей команды, и случайная запись превращалась бы в мучение — именно поэтому производительность так проседает там, где кеш отключён или обойдён (write-through режим у RAID-контроллера с разряженной батарейкой BBU — характерный пример).

Ключевое слово — «быстрая», а не «энергонезависимая». Кеш диска в подавляющем большинстве случаев — это обычная DRAM. Пропадает питание — содержимое кеша исчезает раньше, чем контроллер успевает что-либо сделать. Единственное исключение — контроллеры с BBU (battery backup unit) или, чаще в современном железе, с суперконденсатором: там энергии хватает, чтобы досбросить содержимое кеша на носитель уже после отключения основного питания.

Что должен гарантировать fsync

Когда приложение (СУБД, файловый сервер, да что угодно, для чего важна сохранность данных) вызывает fsync() для файла или fdatasync(), оно просит у ОС одну конкретную гарантию: к моменту, когда системный вызов вернёт управление, все данные, записанные в этот файл до этого момента, физически находятся на энергонезависимом носителе и переживут внезапную потерю питания.

Путь запроса примерно такой:

  1. Приложение пишет данные через write() — они оседают в page cache ядра, физической записи на диск ещё не было.
  2. Приложение вызывает fsync().
  3. Файловая система сбрасывает на диск все «грязные» страницы этого файла и, если нужно, свои метаданные (в журнал — подробнее о том, что журнал спасает, а что нет, отдельная тема).
  4. Файловая система отправляет диску команду сброса кеша: для SATA это FLUSH CACHE (или FLUSH CACHE EXT), для NVMe — Flush, либо запись оформляется с флагом Force Unit Access (FUA), который просит диск гарантированно довести именно эту запись до носителя.
  5. Диск обязан выполнить команду, физически сбросить содержимое своего write cache на носитель и только после этого вернуть подтверждение.
  6. Только получив это подтверждение, ядро возвращает управление из fsync() приложению.

Если каждое звено честно выполняет свою часть, гарантия работает: fsync() вернулся — значит, данные переживут отключение питания. Проблема в шаге 5: команда сброса кеша — это просьба к устройству, а не физический закон. Устройство обязано её выполнить, но ничто не мешает прошивке ответить «готово» чуть раньше, чем данные реально покинули волатильную память.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Кеш, который лжёт: откуда берётся проблема

Ситуацию, когда диск или контроллер подтверждает FLUSH CACHE/Flush, не дождавшись реальной записи в энергонезависимую область, в англоязычной литературе называют write cache "lying" — устоявшегося русского термина нет. Смысл один: устройство сообщает ОС, что данные в сохранности, хотя они всё ещё в обычной DRAM. Причины разные:

  • Погоня за бенчмарками. Диск, который честно ждёт физической записи на каждый flush, показывает в синтетических тестах меньшие IOPS на мелкие случайные записи, чем диск, который отвечает сразу. Часть потребительских накопителей исторически оптимизировалась именно под тесты.
  • Экономия на дизайне контроллера. Честная реализация flush требует более сложной логики отслеживания состояния кеша и, для настоящей защиты от потери питания, конденсатора или батареи — это стоит денег на каждую единицу продукции.
  • Ошибки прошивки. Не всегда это осознанное «жульничество» — бывают баги, из-за которых команда flush обрабатывается как no-op в определённых режимах или для определённых типов накопителей.
  • RAID-контроллеры с write-back кешем без исправной защиты питания. Контроллер агрегирует запросы от нескольких дисков в своём кеше и подтверждает запись ОС сразу после попадания в этот кеш — это штатный режим write-back, и он безопасен ровно до тех пор, пока BBU/суперконденсатор исправны и способны досбросить кеш при аварии.

Обнаружить такое поведение снаружи, не имея специального стенда с управляемым обрывом питания, почти невозможно: с точки зрения ОС всё выглядит корректно — команда отправлена, ответ получен, всё быстро и как будто честно. Единственный надёжный тест — это реальный (или контролируемый) обрыв питания под нагрузкой и последующая проверка целостности данных, а не измерение задержки flush.

Барьеры записи: как гарантировать порядок, а не только факт записи

Отдельная задача, смежная с fsync, но не тождественная ей, — гарантировать не «данные записаны», а «эта запись физически произошла раньше вон той». Журналируемым файловым системам (ext4, XFS и другим) критично, чтобы запись в журнал транзакции физически предшествовала записи изменённых данных поверх старых значений — иначе при сбое между этими двумя записями журнал опишет транзакцию, которая на диске завершена лишь частично, и восстановление после сбоя (journal replay) само может испортить данные вместо того, чтобы их спасти.

Именно для этого существуют write barriers (барьеры записи) — механизм, который заставляет диск гарантированно завершить все команды записи, отправленные до барьера, прежде чем начнёт исполнять команды, отправленные после. По сути это дисциплина упорядочивания, а не отдельная физическая операция:

  • В классической реализации на HDD барьер реализовывался как комбинация: досбросить кеш (flush) до записи важного блока, затем ещё раз досбросить кеш после неё, гарантируя порядок «журнал → сброс → данные → сброс».
  • В современных ядрах Linux барьеры как отдельная явная сущность в основном ушли из терминологии — их роль взяли на себя команды FUA (Force Unit Access) на запись плюс FLUSH-команды, которые файловая система расставляет там, где ей нужен гарантированный порядок, без полного сброса кеша на каждую операцию.
  • NVMe изначально спроектирован с учётом упорядочивания через глубокие очереди команд и явные Flush-команды, а не через отдельный барьерный протокол, унаследованный от SCSI/ATA.

Важно: барьер решает проблему порядка. Если диск честно исполняет барьеры, но лжёт на самих flush-командах о факте физической записи (то есть сообщает «сброшено», хотя данные ещё в DRAM), барьер не спасает — порядок команд может быть соблюдён идеально, а физического сохранения данных всё равно не произойдёт. Это две разные гарантии, и вторая зависит целиком от честности прошивки устройства.

Почему это касается конкретно вас, а не только теоретиков файловых систем

На первый взгляд тема выглядит academic — «где-то там в ядре что-то сбрасывается». На практике она напрямую определяет, что произойдёт с базой данных, очередью сообщений или файловым хранилищем при жёстком выключении сервера (сбой питания в дата-центре, паника ядра, kill -9 гипервизора при виртуализации, случайный hard reset).

Практические следствия:

  • СУБД, которые полагаются на честный fsync, при лживом кеше теряют «подтверждённые» транзакции. PostgreSQL, MySQL/InnoDB и большинство других движков используют журнал упреждающей записи (WAL) именно в расчёте на то, что fsync — это настоящая гарантия. Если диск обманывает, вся модель durability из ACID превращается в вероятностную: обычно данные на месте, но не всегда — и это тот случай, когда «обычно» бесполезно, если решение критично.
  • Реплика может «обогнать» источник данных по консистентности не в ту сторону. При синхронной репликации с подтверждением коммита раньше физической записи возможны рассинхронизации, которые невозможно объяснить логами приложения — оно честно делало всё правильно.
  • Виртуализация добавляет ещё один слой, на котором может произойти та же ложь. Гипервизор со своим кешированием (неправильно настроенный кеш-режим виртуального диска) способен воспроизвести ту же проблему на уровне гостевой ОС, даже если физический носитель под хостом честен. Для арендованных серверов и VPS вопрос «что происходит при сбросе кеша» стоит адресовать не только к диску, но и к слою виртуализации.
  • Мелкая случайная запись — самый уязвимый паттерн. Именно там разница между честным и «лживым» кешем максимальна по влиянию на производительность, а значит и соблазн у производителя железа срезать угол — самый сильный. Это те же нагрузки, где важна глубина очереди NVMe и общая архитектура очередей команд.

Как понять, можно ли доверять кешу конкретного диска или контроллера

Прямого стопроцентного способа проверить это программно без контролируемого обрыва питания не существует, но есть практические ориентиры, которые снижают риск:

  • Смотрите на класс накопителя, а не только на бренд. Диски и SSD, позиционируемые как «для дата-центров» / enterprise, в характеристиках почти всегда явно указывают поведение при потере питания — power-loss protection (PLP). Наличие PLP означает, что у SSD есть собственные конденсаторы, которых хватает, чтобы досбросить DRAM-кеш и таблицу трансляции адресов в NAND при внезапном отключении питания.
  • Для RAID-контроллеров исправность BBU/суперконденсатора — не разовая проверка, а предмет мониторинга. Батарея деградирует со временем, и контроллер обычно сам переключается в безопасный write-through режим, когда не уверен в её состоянии — именно поэтому просевшая батарея иногда выглядит как «диск вдруг резко замедлился», это разбирали на примере конкретного случая с батарейкой RAID-контроллера. Если контроллер продолжает работать в write-back при разряженной батарее без потери скорости — это тревожный звоночек. О том, когда вообще имеет смысл ставить аппаратный RAID-контроллер, а когда хватит программного массива — отдельный разговор.
  • Не полагайтесь на маркетинговое название кеша. «Write-back cache» само по себе не значит ни хорошо, ни плохо — значение имеет только энергонезависимая защита этого кеша при отключении питания.
  • Тестирование с контролируемым обрывом питания под нагрузкой — стандартная практика верификации там, где durability критична. Конкретные цифры и версии инструментов приводить не будем — для вашего оборудования и прошивки результат нужно проверять самостоятельно.
  • Настройка fsync/synchronous_commit в СУБД — управление тем, готовы ли вы платить производительностью за честность на уровне приложения. Отключение fsync ради скорости — осознанный отказ от гарантии durability на этом слое, и решение должно быть таким же осознанным, как выбор диска без PLP, а не настройкой «по умолчанию где-то вычитали».

Что можно сделать на своей стороне уже сейчас

Проверка «врёт ли конкретно мой диск» без лабораторного стенда затруднительна, но снизить риск и сделать последствия предсказуемыми — вполне посильная задача:

  • Для продакшен-баз данных и сервисов, где durability важна, выбирайте накопители и RAID-контроллеры с явной защитой кеша при отключении питания (PLP у SSD, исправный BBU/суперконденсатор у контроллера), а не самые дешёвые позиции в прайсе.
  • Если сервер арендованный или виртуальный — уточните у провайдера режим кеширования диска на уровне гипервизора и чем защищён физический носитель под ним; это часть модели надёжности вашего приложения, а не праздный вопрос.
  • Не отключайте fsync/барьеры на файловой системе «для скорости» на боевых базах без понимания, что вы тем самым отказываетесь от гарантии сохранности при сбое — и без ИБП/BBU, компенсирующих этот риск на уровне железа.
  • Держите ИБП с корректным автоматическим завершением работы сервера при долгой пропаже питания — это не альтернатива честному кешу, а дополнительный рубеж: чем меньше внезапных обрывов питания случается физически, тем меньше шансов поймать редкий сценарий с лгущим кешем на практике.
  • Для критичных данных полагайтесь не только на честность одного диска, а на репликацию и регулярные проверенные бэкапы — идея «один честный диск спасёт от всего» сама по себе слишком хрупкая архитектура.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли программно из ОС проверить, что диск честно выполняет flush-команды?

Надёжного универсального способа нет. Косвенно можно смотреть на паспортные характеристики (наличие PLP, класс enterprise/datacenter), но единственная прямая проверка — контролируемый обрыв питания под нагрузкой с последующей сверкой данных на конкретном экземпляре железа.

Если у RAID-контроллера исправна батарея (BBU), значит ли это, что fsync полностью безопасен?

Это снижает риск на уровне контроллера, но не отменяет вопрос честности самих дисков за контроллером и слоя виртуализации, если сервер виртуальный. Слабое звено цепи «приложение → ОС → контроллер → диск → гипервизор» определяет надёжность всей цепочки.

Отключение fsync в PostgreSQL/MySQL действительно так опасно, как об этом пишут?

Да, если сервер может внезапно потерять питание или перезагрузиться аварийно. Отключённый fsync ускоряет запись, потому что СУБД перестаёт дожидаться гарантии сохранности от ОС и диска — при аварийной перезагрузке база может оказаться в неконсистентном состоянии, а не просто «потерять последние секунды».

Чем барьер записи отличается от простого сброса кеша (flush)?

Flush — команда «сбрось всё, что накопилось в кеше, прямо сейчас». Барьер — более широкое понятие: гарантия порядка, при которой записи до барьера физически завершаются раньше записей после него. Flush часто используется как строительный блок для реализации барьера, но сам по себе барьером не является.

Одинаково ли рискуют HDD и SSD в этом плане?

Механизм риска тот же (волатильный DRAM-кеш перед носителем), но у SSD добавляется таблица трансляции адресов (FTL), которую тоже нужно сохранить консистентно при потере питания. Поэтому у enterprise-SSD защита PLP закрывает сразу обе проблемы.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →