Привет! Меня зовут Ваня, я системный администратор в Selectel. Представьте, что вы используете mdadm для отказоустойчивости, bcache — для ускорения медленных HDD, cryptsetup — для шифрования данных, LVM — потому что нужно создавать в рейде несколько блочных устройств, и btrfs — за любовь к сжатию и снапшотам. А теперь прихожу я и говорю, что все это можно заменить одной файловой системой — ZFS. Как именно? Под катом разберемся в ее устройстве, но без излишнего погружения — не будем превращать статью в «простыню».

Используйте навигацию, если не хотите читать текст целиком:
Архитектура ZFS
⠀⠀→ Dataset и Volume
Кэширование в ZFS: чтение и запись
⠀⠀→ Запись: ZIL и SLOG
⠀⠀→ Чтение: ARC и L2ARC
В завершение

Архитектура ZFS


Отправная точка — пул (zpool), именно он создается в первую очередь. Пул расположен на виртуальном устройстве (vdev), которое, в свою очередь, может находиться на одном или нескольких физических дисках. Привычных уровней рейда здесь нет: в ZFS используется уникальный тип отказоустойчивого массива — raidz. Однако почти все его варианты вам покажутся уже знакомыми.

  • stripe — данные распределяются по дискам, без избыточности. По сути это RAID 0.
  • mirror — данные пишутся зеркально на два (или больше) диска. Похоже на RAID 1.
  • raidz1 — данные распределены по всем дискам, но есть избыточность. fault tolerance — один диск (именно это и означает цифра в типе raidz). Аналог RAID 5.
  • raidz2 — тот же raidz1, но с избыточностью в два диска: zpool продолжит работу, даже если они оба выйдут из строя. Прямо как в RAID 6.
  • raidz3 — допускается выход из строя сразу трех дисков. Прямых аналогов среди классических уровней RAID нет.

Dataset и Volume


Внутри zpool можно создать volume и dataset. С dataset все понятно: это та самая файловая система, которую мы можем примонтировать и сложить туда файлы. А вот volume — это уже интереснее. Такая сущность встречается в LVM, но не в обычных ФС. Формально это такой же dataset, но без файловой системы внутри, который нельзя примонтировать, но можно использовать как блочное устройство (в DevFS — /dev/{zvol0,zvol1,zvol2,...}). При этом для него будут применимы все фичи ZFS — например, сжатие, шифрование, кэширование и снапшоты. И все это — внутри отказоустойчивого zpool. Удобно!

Использовать volume можно как диск ВМ или для iSCSI, когда вам нужен сетевой диск на одном из серверов. На этом же уровне реализованы сжатие и шифрование, причем для разных dataset и volume можно использовать разные настройки (алгоритмы шифрования и/или сжатия, ключи шифрования).



Кэширование в ZFS: чтение и запись



Чтобы понять, как устроено кэширование, разберемся, как данные попадают в zpool и как они оттуда читаются. Кто-то может возразить, что у ZFS нет функции кэширования записи, но будут правы лишь формально. Начнем с того, что существует два типа записи: асинхронная — например, через системный вызов aio_write() или write() без флага O_SYNC, а также синхронная — например, тот же write(), но с O_SYNC.

В случае асинхронной записи ZFS работает как большинство кэшируемых систем: сначала сохраняет данные в оперативной памяти, а затем в фоновом режиме записывает их в zpool. Минус очевиден — есть риск потери данных при внезапном отключении питания, сбое ядра и любом другом негативном сценарии. Однако для асинхронной записи это допустимо.

Синхронная запись требует полной надежности, поэтому перед записью в zpool данные временно попадают в «лимб» — ZIL (ZFS Intent Log). Его задача — обеспечение целостности записываемых в zpool данных, чтобы непредвиденная остановка работы системы не привела к потере записываемых данных.

Все изменения в ZIL собираются в группы транзакций (TXG) и вносятся в zpool одним большим блоком. Это работает благодаря методу записи ZFS: при изменении блока обновленные данные записываются в новое место, а старый блок помечается как свободный. Такой подход избавляет от необходимости изменять данные мелкими блоками на месте, снижает фрагментацию и ускоряет операции ввода-вывода.


Как работает файловая система с копированием при записи. Запись новой версии блока и разблокировка старой. Источник.

По указанной причине zpool нельзя заполнять «под завязку». Если не останется свободного места, даже удаление файлов станет невозможным, и пул уйдет в режим read-only. Чтобы избежать деградации производительности, рекомендуем оставлять около 10% свободного пространства.

Такой подход к записи помогает сократить количество операций ввода-вывода с мелкими блоками, где даже топовые «blazing-fast Gen5 Ultra Mega SSD Max» показывают достаточно слабые результаты. Что уж говорить о HDD.

Запись: ZIL и SLOG


По умолчанию ZIL находится в zpool, что может показаться странным: это не дает прироста в скорости, а данные пишутся дважды, сокращая ресурс SSD. Однако ZIL существует не просто так: его можно вынести на отдельный vdev — SLOG.

Несколько рекомендаций по размещению SLOG.

  • Используйте отдельный vdev с резервированием (обычно используют mirror) — на случай сбоя.
  • Выбирайте устройства с минимальной задержкой записи и хорошей работой с «мелкоблочкой» — подойдут SLC SSD или Intel Optane.

Когда я только начинал изучать ZFS, часто натыкался на рекомендации: «Вам не нужен больший объем SLOG — ведь у TXG есть таймаут, по истечении которого ZFS будет синхронно ждать ее внесения в zpool». Однако это вредный совет: если придерживаться его, то каждые пять секунд ZFS будет вносить все изменения из ZIL в zpool. Также новые операции записи будут выполняться намного дольше.

Таймаут TXG — это настраиваемый параметр, так что вам ничего не мешает указать его на ваше усмотрение — хоть до нескольких минут. Так вы увеличите эффективно используемое пространство SLOG.

Итог: SLOG — это фактически кэш для синхронной записи в ZFS. Он может быть практически любых размеров и при правильной конфигурации дает заметный прирост производительности.

Чтение: ARC и L2ARC


С чтением все проще, чем с записью. ZFS кэширует наиболее часто используемые блоки (MFU) и недавно прочитанные (MRU) прямо в оперативной памяти. Как можно догадаться, обращение к ним происходит очень быстро! Этот кэш называется ARC (Adaptive Replacement Cache).

Размер ARC — это тоже настраиваемая сущность (в OpenZFS — по умолчанию половина от установленной ОЗУ). При этом в ARC реализован предиктивный механизм — например если вы прочитали последовательно несколько блоков файла, то ZFS заранее подгрузит в кэш несколько последующих блоков еще до того, как вы их запросите.

Если блоки вытесняются из ARC, они могут быть отправлены во второй уровень кэша — L2ARC, если он настроен. L2ARC располагается на отдельном cache vdev, избыточность для него не требуется: данные уже сохранены в zpool, и в случае сбоя данные не будут утеряны.

В завершение


ZFS решает сразу несколько задач — отказоустойчивость, шифрование, сжатие, снапшоты, кэширование и блочное хранение. Все это — в одной системе, без необходимости собирать франкенштейна из mdadm, bcache, cryptsetup, LVM и прочих.

В будущих текстах расскажу, как установить ОС на ZFS и как с ее помощью настроить инкрементное резервное копирование без привязки к базовой РК. Делитесь в комментариях, как используете ее на практике — и на какие грабли наступали (или наоборот, какие находки спасли вам прод).

Комментарии (4)


  1. c46fd3da
    27.06.2025 08:41

    И ни слова о том, что ZFS это COW система. Самое главное забыли. =)


    1. M_AJ
      27.06.2025 08:41

      Написано, просто не названо CoW (если не считать подписи под картинкой):

      Это работает благодаря методу записи ZFS: при изменении блока обновленные данные записываются в новое место, а старый блок помечается как свободный. 


  1. Johan_Palych
    27.06.2025 08:41

    Мдя.
    FreeBSD handbook - The Z File System (ZFS)

    ZFS is an advanced file system designed to solve major problems found in previous storage subsystem software.
    Originally developed at Sun™, ongoing open source ZFS development has moved to the OpenZFS Project.

    OpenZFS on Linux and FreeBSD - Releases zfs-2.3.3 last week
    OpenZFS documentation
    Performance and Tuning

    Давно использую:
    ZFS on Linux - Proxmox VE


  1. SL1308
    27.06.2025 08:41

    Разве?

    Это работает благодаря методу записи ZFS: при изменении блока обновленные данные записываются в новое место, а старый блок помечается как свободный.