Что такое журнал файловой системы и какие именно данные он не спасает
Сервер внезапно теряет питание, вы поднимаете его обратно и с облегчением видите, что fsck не запускается, диск монтируется за секунды, файлы на месте. Кажется, что журналирование файловой системы спасло данные от повреждения. На самом деле оно спасло не данные, а структуру файловой системы — и это разные вещи. Разберёмся, что именно журнал ext4 или XFS гарантирует, а что остаётся на совести приложения и его вызовов fsync.
Содержание
Что вообще делает журнал файловой системы
До появления журналируемых файловых систем восстановление после сбоя выглядело так: при загрузке ядро запускало fsck, который обходил всю файловую систему блок за блоком, искал несостыковки между таблицей инодов, битовыми картами свободного места и структурой каталогов, и пытался их починить. На диске в сотни гигабайт это могло занимать десятки минут — сервер просто не поднимался, пока утилита не заканчивала обход.
Журнал решает эту проблему иначе. Прежде чем менять реальные метаданные на диске — добавлять запись в каталог, обновлять таблицу инодов, помечать блоки как занятые — файловая система сначала записывает описание этого изменения в отдельную область диска, сам журнал. Только после того, как запись в журнал подтверждена, начинается запись в основные структуры. Если между этими двумя шагами происходит сбой, при следующем монтировании файловая система не обходит весь диск: она читает журнал, находит незавершённые транзакции и либо доигрывает их до конца, либо откатывает — в зависимости от того, что было надёжно зафиксировано.
Ключевое слово здесь — метаданные. Журнал в его основном режиме работы фиксирует изменения именно структуры файловой системы: кто где лежит, какие блоки кому принадлежат, какого размера файл. Содержимое файла — те самые байты, которые вы туда записали, — по умолчанию в журнал не попадает вовсе.
Проверить, что журнал вообще включён и как он настроен, можно так:
# для ext4
tune2fs -l /dev/sda1 | grep -i journal
# для XFS
xfs_info /mnt/data | grep -i log
Три режима журналирования в ext4: writeback, ordered, journal
У ext4 есть параметр монтирования data, который определяет, что именно защищает журнал. Это ключевая развилка, которую стоит понимать перед тем, как читать дальше.
data=writeback. Журналируются только метаданные, и порядок их записи относительно данных файла никак не координируется. Ядро может записать новые метаданные раньше, чем реальное содержимое файла окажется на диске. После сбоя файловая система будет структурно целой, но в новых или дозаписанных файлах можно обнаружить старый мусор — например, старое содержимое блока, который раньше принадлежал другому файлу. Это самый быстрый режим и самый ненадёжный по отношению к данным.
data=ordered. Режим по умолчанию в большинстве современных дистрибутивов. Журналируются по-прежнему только метаданные, но ядро гарантирует порядок: данные файла записываются на диск раньше, чем связанные с ними метаданные фиксируются как завершённые. Это убирает проблему «чужого мусора» в файле — вы не увидите чужие данные там, где их не писали. Но это не значит, что ваши собственные последние записи гарантированно окажутся на диске. Если сбой произошёл до того, как данные физически долетели до диска, а метаданные ещё не были зафиксированы, файл в худшем случае будет обрезан или не будет содержать последних изменений — но не будет содержать чужого мусора.
data=journal. В журнал пишутся не только метаданные, но и сами данные файла — полное журналирование. Это единственный режим, где журнал действительно защищает содержимое файлов от потери при сбое, а не только структуру каталогов. Плата за это — каждый блок данных физически записывается на диск дважды: сначала в журнал, потом на постоянное место. Из-за двойной записи и дополнительной синхронизации этот режим ощутимо медленнее на нагрузках с интенсивной случайной записью, поэтому на практике его почти никогда не включают на серверах общего назначения — обычно он оправдан только там, где целостность содержимого файла важнее пропускной способности диска, и то после собственных измерений на конкретной нагрузке.
Посмотреть и поменять режим можно через /etc/fstab или на лету при перемонтировании:
mount -o remount,data=ordered /dev/sda1 /
# в /etc/fstab
/dev/sda1 / ext4 defaults,data=ordered 0 1
XFS устроена иначе и параметра data=journal в привычном для ext4 виде не имеет — об этом отдельно ниже.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверЧто журнал гарантирует, а что нет
Возьмём конкретный пример. Приложение открывает файл, дописывает в конец лог-строку и не вызывает никакой явной синхронизации. В момент между записью строки в буфер ядра и её физическим попаданием на диск сервер теряет питание.
После перезагрузки журнал ext4 или XFS отработает штатно за доли секунды. Файловая система будет консистентна: каталог знает про этот файл, иноды на месте, битовые карты свободного места не повреждены, fsck (если его вообще запустить вручную) ничего не найдёт. Но сама дописанная строка в файле может отсутствовать — она осталась в кеше страниц в оперативной памяти и не пережила выключение питания. Журнал не соврал: он гарантировал именно то, что обещал, — целостность структуры. Он никогда не обещал, что байты, которые вы отправили в файл, окажутся на диске раньше, чем вы явно попросите их туда сбросить.
Это различие особенно важно для баз данных и любых сервисов, которые пишут критичные для целостности данные: очереди сообщений, хранилища сессий, собственные форматы логов транзакций. Журнал файловой системы защищает файловую систему от вас — не даёт ей развалиться структурно. Он не защищает ваши данные от вас же, если вы не попросили ядро гарантированно их сохранить.
Отдельно стоит развеять смежное заблуждение: журналирование файловой системы и журнал транзакций базы данных (WAL в PostgreSQL, redo log в MySQL/InnoDB) — это два независимых механизма на разных уровнях. Подробнее о том, почему у PostgreSQL растёт свой WAL и как он связан с надёжностью записи, можно почитать в статье про рост pg_wal в PostgreSQL — там речь именно про журнал базы, а не файловой системы, и путать их не стоит.
Кеш страниц и почему нужен fsync
Когда приложение вызывает write(), данные почти никогда не попадают на диск немедленно. Они оседают в кеше страниц (page cache) — области оперативной памяти, которой управляет ядро. Оттуда данные будут вытеснены на диск позже: либо когда накопится достаточно «грязных» страниц, либо по таймеру (см. /proc/sys/vm/dirty_expire_centisecs и связанные параметры), либо когда кто-то явно попросит сброс.
Именно для этого явного запроса существуют системные вызовы fsync() и fdatasync(). fsync заставляет ядро сбросить на физический носитель все данные и метаданные конкретного файла, и вернуть управление приложению только после подтверждения записи. fdatasync делает то же самое, но без части метаданных, которые не критичны для чтения содержимого (например, точное время последнего изменения), что немного дешевле.
Никакой режим журналирования файловой системы не заменяет этот вызов. Журнал в режиме data=journal защищает от определённого класса повреждений при сбое, но не отменяет того факта, что данные, ещё не сброшенные на диск в принципе — хоть через журнал, хоть напрямую, — физически там отсутствуют. Если приложение никогда не зовёт fsync, оно полагается исключительно на то, что ядро само решит скинуть страницы вовремя, а это вопрос таймингов, а не гарантий.
Проверить, насколько активно приложение синхронизирует запись, можно через strace:
strace -f -e trace=write,fsync,fdatasync -p <PID>
Если в выводе вы годами не видите fsync или fdatasync рядом с записью критичных данных, приложение рискует потерять последние секунды или минуты работы при внезапном отключении питания — независимо от того, насколько надёжно настроена файловая система под ним. Хорошо спроектированные СУБД вызывают fsync на своём журнале транзакций перед тем, как подтвердить клиенту успешный commit — именно это, а не журнал ext4, даёт гарантию «после commit данные не потеряются». Параметр fsync = on в PostgreSQL и innodb_flush_log_at_trx_commit = 1 в MySQL управляют ровно этим поведением на уровне приложения.
XFS: другой журнал, тот же принцип
XFS изначально проектировалась как журналируемая система для больших объёмов данных и параллельных операций, и в этом смысле она архитектурно ближе к ext4 в режиме data=ordered, чем к data=journal. Журнал XFS фиксирует изменения метаданных — структуру каталогов, распределение экстентов, атрибуты — но не содержимое файлов целиком, и отдельного пользовательского режима «журналировать вообще всё» у XFS в привычном для ext4 виде нет.
Отличие XFS в первую очередь в внутреннем устройстве журнала: она использует delayed logging — группирует и переупорядочивает записи в журнал перед физическим сбросом, что снижает накладные расходы на интенсивной многопоточной нагрузке по сравнению с классическим журналом ext4. Но с точки зрения гарантий для содержимого файлов вывод тот же: XFS не спасает несинхронизированные данные приложения при сбое питания так же, как их не спасает ext4 в режиме по умолчанию. Разница в производительности и внутренней организации журнала не отменяет того, что fsync в приложении остаётся обязательным для данных, которые нельзя терять.
Если стоит вопрос выбора между файловыми системами для конкретной роли сервера — под базу данных, под файловое хранилище, под общего назначения VPS — сравнение по критериям производительности и восстановления после сбоя разобрано в статье ext4 против XFS: как выбрать.
Практические выводы для эксплуатации сервера
Из всего разобранного выше следует несколько конкретных решений, которые стоит принять осознанно, а не по умолчанию.
Для приложений, которые сами управляют целостностью данных — СУБД, брокеры сообщений, распределённые хранилища, — журнал файловой системы вообще не главный рубеж защиты. Главный рубеж — это собственный WAL или redo log приложения плюс корректные вызовы fsync в нужных точках. Проверяйте настройки синхронности именно на уровне приложения, а не полагайтесь на то, что «файловая система же журналируемая».
Для файлов, которые вы записываете вручную или через простые скрипты и для которых важна консистентность конкретной версии — конфиги, бэкапы, архивы, — используйте паттерн «запись во временный файл + fsync + атомарное переименование через rename()» вместо прямой перезаписи существующего файла. Переименование внутри одной файловой системы — атомарная операция на уровне ядра: в отличие от прямой перезаписи, читатель либо увидит старую версию файла целиком, либо новую, но никогда не увидит файл наполовину переписанным.
Отдельно стоит не путать RAID и файловую систему. RAID защищает от отказа физического диска, но не от логического повреждения данных и не заменяет резервные копии — это разобрано в статье миф о том, что RAID — это резервная копия. Журналирование файловой системы решает третью, отдельную задачу — быстрое и корректное восстановление структуры после сбоя, — и три этих механизма (журнал ФС, WAL приложения, резервные копии) закрывают разные риски и не подменяют друг друга.
Наконец, если сервер регулярно теряет питание некорректно — например, стоит без ИБП или виртуализация не гарантирует чистое выключение при сбое хоста, — режим data=journal для конкретных разделов с критичными файлами стоит рассмотреть осознанно, заранее прогнав собственный тест производительности под реальной нагрузкой, а не полагаясь на общие рассуждения о том, что он «надёжнее». На арендованном сервере с нормальным питанием и корректным graceful shutdown при обслуживании этот риск в разы ниже, и data=ordered остаётся разумным выбором по умолчанию.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Если после сбоя fsck ничего не находит, значит ли это, что данные не потеряны?
Нет. Это значит только то, что структура файловой системы цела — каталоги, иноды, распределение блоков. Содержимое конкретных файлов, которое не было синхронизировано на диск до сбоя, могло быть потеряно или обрезано, и fsck этого не покажет, потому что с точки зрения файловой системы всё консистентно.
Нужно ли включать data=journal на сервере с базой данных?
Обычно нет. СУБД вроде PostgreSQL и MySQL сами управляют журналом транзакций и сами вызывают fsync в нужных точках, поэтому дополнительное полное журналирование на уровне файловой системы для них избыточно и добавляет накладные расходы на двойную запись. Разумнее довериться собственному механизму СУБД и режиму data=ordered.
Чем отличается fsync от простого write?
write() кладёт данные в буфер ядра (кеш страниц) и возвращает управление немедленно — физической записи на диск в этот момент могло ещё не произойти. fsync() блокирует выполнение, пока данные и метаданные файла гарантированно не окажутся на физическом носителе. Только после успешного возврата из fsync можно считать данные надёжно сохранёнными.
XFS и ext4 — какая из них надёжнее при сбое питания?
Обе используют журналирование метаданных и обе восстанавливаются после некорректного выключения без длительного fsck. По умолчанию ни одна из них не гарантирует сохранность несинхронизированного содержимого файлов — разница между ними в первую очередь в производительности и внутреннем устройстве журнала, а не в уровне защиты данных приложения.
Помогает ли отключение кеша записи диска (write cache) от этой проблемы?
Это решает другую, смежную проблему. Диски и RAID-контроллеры с собственным кешем записи иногда подтверждают запись ядру раньше, чем данные реально попали на энергонезависимый носитель — это отдельный слой ненадёжности поверх описанного здесь, и на него влияют барьеры записи (write barriers) и настройки кеша самого контроллера, а не режим журналирования файловой системы.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →