Спор о том, какую файловую систему положить под базу, тянется столько же, сколько существуют сами эти файловые системы, и оброс мифами не хуже выбора языка программирования. Одни уверены, что XFS быстрее по определению, потому что её родословная идёт от больших систем для научных данных. Другие держатся за ext4 как за консервативный и проверенный выбор. Проблема в том, что почти все ходовые аргументы взяты из бенчмарков десятилетней давности, снятых на дисках, которых в боевом проде под нагруженной OLTP-базой уже почти не встретить.
NVMe-накопители изменили картину. То, что раньше упиралось в механику вращающегося диска, теперь упирается совсем в другое. Нагрузка типа базы под транзакциями это поток мелких случайных записей и постоянные принудительные сбросы на диск, а не аккуратное линейное чтение больших файлов. Именно на таком профиле разница между двумя системами либо проявляется, либо растворяется до незначимой. Разберём, что показывают замеры, где зарыта настоящая разница и какие настройки решают в разы больше, чем сам выбор между ext4 и XFS.
Что показывают замеры производительности на самом деле
Если убрать эмоции и посмотреть на воспроизводимые бенчмарки PostgreSQL, главный вывод звучит почти разочаровывающе. На транзакционной нагрузке ext4 и XFS работают практически одинаково и обе уверенно опережают более молодые файловые системы вроде ZFS и Btrfs. Если в бенчмарке PostgreSQL участвуют разные файловые системы, на нагрузке чтение-запись ext4 и XFS выходят явными победителями, тогда как другие системы либо отстают, либо проседают на больших объёмах данных.
Разница между двумя лидерами есть, но она тонкая и контринтуитивная. Несмотря на репутацию XFS как более быстрой системы, ряд замеров стабильно показывает небольшое преимущество ext4 по чистой пропускной способности на транзакционной нагрузке. В одном из развёрнутых тестов лучший результат ext4 оказался примерно на десять процентов выше лучшего результата XFS. Это не пропасть, и обратное соотношение на другом железе и другом профиле тоже встречается, но устойчивая закономерность видна.
Есть и вторая половина картины, которую обрывочные бенчмарки упускают, а для прода она важнее средней цифры. Это стабильность пропускной способности во времени. У ext4 чаще наблюдается чуть более высокая средняя пропускная способность, но с большим разбросом, у XFS пропускная способность ниже, зато ровнее и с меньшим джиттером. Для базы данных предсказуемость отклика нередко ценнее лишних процентов средней скорости, потому что пользователь чувствует именно провалы, а не среднее.
Грубая сводка по транзакционной нагрузке pgbench:
ext4 высокая пропускная способность, БОЛЬШЕ джиттера
XFS чуть ниже пропускная, МЕНЬШЕ джиттера, ровнее
ZFS заметно медленнее на больших объёмах, но очень стабильна
Btrfs стабильно хуже всех, для базы не рекомендуется
Отдельно стоит вынести то, что лучше всего сформулировали сами авторы серьёзных замеров. Единственный честный бенчмарк это тот, что снят на реальном приложении в реальных условиях. Синтетика измеряет узкий аспект, и переносить её выводы на другое железо, ядро и профиль нагрузки рискованно.
Почему на NVMe старые аргументы перестали работать
Чтобы понять, почему древние сравнения вводят в заблуждение, нужно вспомнить, под что эти файловые системы создавались. XFS родилась в мире мощных систем с ECC-памятью, резервным питанием и дорогими дисковыми массивами с батарейной защитой кеша. Это был мир, где железо не врёт: когда файловая система говорит диску зафиксировать данные, он делает это немедленно. На таком фундаменте доверия XFS могла позволить себе быть агрессивнее и нацеленнее на производительность.
Родословная ext4 совсем другая. Она выросла в мире дешёвых потребительских дисков, которые врут ради красивых цифр в бенчмарках, внезапных отключений питания и памяти без коррекции ошибок. Доверять железу было нельзя, поэтому ext4 проектировалась параноидально, с приоритетом выживания данных. Эта разница в философии до сих пор объясняет их поведение под стрессом. Если прошивка диска соврёт об успешной записи или слой хранения виртуальной машины настроен неаккуратно, внезапная потеря питания способна привести к тяжёлому повреждению XFS, иногда неустранимому.
Появление NVMe заставило обе системы эволюционировать, и разрыв на универсальных задачах заметно сократился. Параллельная архитектура XFS оказалась естественно хороша для многоочередных NVMe-накопителей. Ext4 со своей стороны переработала алгоритмы под флеш-хранилище. Обе теперь чисто работают с операциями TRIM и discard, критичными для поддержания скорости SSD. Грубое правило, которое из этого вытекает: XFS обычно извлекает больше пользы из высокого числа операций ввода-вывода и параллелизма, тогда как окружения с массой мелких файлов и ограниченным процессором часто выигрывают от традиционного устройства ext4. База под мелкими записями балансирует ровно на этой границе, поэтому однозначного победителя по железу нет.
Профиль базы под транзакциями, поток мелких записей и постоянный fsync
Чтобы выбор имел смысл, нужно понимать, что именно база делает с диском. Транзакционная нагрузка это не чтение больших файлов. Это много мелких случайных записей в страницы данных, и поверх них постоянный поток записи в журнал предзаписи WAL, который должен быть гарантированно на диске до подтверждения транзакции. Каждое такое подтверждение это вызов синхронизации на диск, и именно частота этих вызовов, а не пропускная способность канала, определяет реальную скорость базы.
Механику синхронизации стоит понять, потому что именно она объясняет, почему файловая система иногда вообще ни при чём. Когда PostgreSQL пишет на диск, он вызывает системный сброс fsync или fdatasync, чтобы вытолкнуть данные на физический носитель. По умолчанию кеш на дисках и контроллерах считается энергозависимым, и при сбросе ядро ставит барьер записи, который проталкивает всё на физический диск, опустошая кеш записи. Это и есть та операция, на которой база теряет больше всего скорости при мелких частых коммитах.
Насколько драматично размер записи влияет на пропускную способность синхронизации, хорошо видно по результатам штатной утилиты PostgreSQL для измерения fsync. На одном и том же железе число операций в секунду меняется кратно в зависимости от размера блока.
pg_test_fsync, open_sync, разный размер записи:
16 КБ один write ~112 операций/сек
8 КБ writes ~55 операций/сек
4 КБ writes ~28 операций/сек
2 КБ writes ~14 операций/сек
1 КБ writes ~7 операций/сек
Вывод из этой таблицы важнее, чем кажется. Чем мельче и чаще записи, тем сильнее всё упирается в стоимость одной синхронизации, и тем меньше на этом фоне значит выбор между ext4 и XFS. Прежде чем спорить о файловой системе, эту утилиту стоит прогнать на своём железе.
# Поставляется в пакете contrib, замеряет реальную стоимость fsync
pg_test_fsync
# Имеет смысл прогнать на разделе, где будет лежать WAL
pg_test_fsync -f /pg_wal_mount/testfile
Барьеры записи, настройка которая решает в разы больше выбора ФС
Здесь начинается самое практически важное. Влияние барьеров записи на производительность кратно превышает разницу между самими файловыми системами. В развёрнутых замерах простое отключение барьеров на разделе улучшало пропускную способность примерно на четверть, тогда как разница между ext4 и XFS составляла единицы процентов, а эффект от TRIM был и вовсе на уровне процента-двух.
Логика тут прямая. Барьер записи существует, чтобы пережить внезапную потерю питания с энергозависимым кешем диска. Если же под базой стоит накопитель с защитой кеша конденсатором или контроллер с батарейной защитой, данные из кеша не пропадут при сбое питания, и барьер становится дорогой страховкой от того, что уже застраховано аппаратно. В этом и только в этом случае его отключение безопасно и даёт ощутимый прирост на записи.
# /etc/fstab, раздел под данные PostgreSQL
# ext4, накопитель с защитой кеша по питанию
UUID=... /pgdata ext4 noatime,nobarrier,errors=remount-ro 0 1
# XFS, аналогичный безопасный профиль
UUID=... /pgdata xfs noatime 0 0
Важная оговорка по версиям, без которой совет устаревает. На современных ядрах Linux, начиная примерно с ветки 4.13, опция nobarrier для XFS объявлена устаревшей. Вместо ручного отключения барьера рекомендуется управлять режимом кеша записи самого блочного устройства, переводя его в режим сквозной записи через правило udev, если хранилище это позволяет и кеш защищён аппаратно. То есть на свежих системах правильнее воздействовать на уровень устройства, а не на опцию монтирования.
# Пример udev-правила: режим сквозной записи для устройства с защитой кеша
ACTION=="add|change", KERNEL=="nvme*", SUBSYSTEM=="block", \
ATTR{queue/write_cache}="write through"
Опцию noatime стоит ставить почти всегда, она убирает бессмысленные для базы записи времени доступа к файлам и не несёт рисков для данных, в отличие от отключения барьеров, которое без аппаратной защиты кеша прямой путь к повреждению.
Режим журналирования ext4 и где XFS его обходит
У ext4 есть рычаг, которого у XFS нет в таком виде, и он напрямую касается мелких записей. Это режим журналирования данных. По умолчанию ext4 работает в режиме ordered, при котором все данные принудительно выталкиваются в основную файловую систему до того, как их метаданные фиксируются в журнале. Это безопасно, но добавляет работы на каждой записи.
Режим writeback ослабляет это правило: порядок записи данных не сохраняется, данные могут попасть в основную файловую систему уже после фиксации метаданных в журнале. В этом режиме ext4 ведёт себя примерно как XFS в её стандартном поведении, журналируя только метаданные, а не данные. Для базы, которая и так сама гарантирует целостность через свой журнал предзаписи и контрольные точки, это нередко осмысленный обмен части паранойи файловой системы на скорость.
# /etc/fstab, ext4 с журналированием только метаданных
UUID=... /pgdata ext4 noatime,data=writeback,nobarrier 0 1
Здесь же стоит знать про параметр commit, который задаёт, как часто ext4 синхронизирует данные и метаданные, по умолчанию раз в пять секунд. Низкое значение бережёт данные ценой производительности, очень большое значение производительность улучшает. Но для PostgreSQL крутить этот параметр обычно не нужно и даже вредно, потому что долговечность транзакций обеспечивает сама база своим механизмом fsync на WAL, а не периодический сброс файловой системы. Это пример настройки, которую трогать не следует, понимая почему.
Геометрия XFS под массив и почему это её сильная сторона
Там, где база лежит не на одиночном NVMe, а на программном или аппаратном массиве, у XFS появляется аргумент, которого у ext4 в таком виде нет. XFS умеет выравниваться под геометрию нижележащего массива, согласуя свои операции с шириной полосы и числом устройств, выжимая из чередующегося массива максимум. На одиночном накопителе это преимущество спит, на массиве оно становится реальным.
# XFS с явным указанием геометрии массива при создании
# su размер страйп-юнита, sw число data-несущих дисков
mkfs.xfs -d su=256k,sw=4 /dev/nvmeXnY
# Проверить уже созданную геометрию
xfs_info /pgdata
Тонкость планирования ёмкости, о которую спотыкаются. XFS это улица с односторонним движением: её можно расширить на работающей системе, в том числе под нагрузкой, но нельзя уменьшить без резервной копии и переформатирования. Ext4 в этом смысле гибче, она допускает и расширение, и сжатие. Если будущая раскладка дискового пространства под вопросом и есть шанс, что место придётся отбирать обратно, эта разница перевешивает проценты в бенчмарках. Выбор файловой системы это не только про скорость, но и про операционную гибкость и надёжность, потому что данные обычно самое ценное, что есть.
Что из этого собирается в практическое решение
Если свести всё к рабочему выводу, он получается неожиданно спокойным. На NVMe под транзакционной нагрузкой PostgreSQL разница в чистой скорости между ext4 и XFS лежит в пределах единиц процентов и для подавляющего большинства систем не должна быть главным критерием. ext4 чаще даёт чуть больше средней пропускной способности с большим джиттером и добавляет рычаг writeback и возможность сжатия раздела. XFS даёт чуть более ровный отклик и заметно выигрывает на больших массивах за счёт выравнивания под геометрию, ценой невозможности сжатия и более высоких требований к честности железа.
Главное же не в самом выборе. Настройки вокруг файловой системы решают кратно больше, чем её марка. Барьеры записи при аппаратно защищённом кеше, режим журналирования, опция noatime, режим кеша блочного устройства на свежих ядрах, и поверх всего этого корректная настройка fsync и WAL в самой базе. Сервер, на котором осознанно настроены эти вещи на любой из двух систем, обгонит сервер с теоретически более быстрой файловой системой и настройками по умолчанию. Поэтому практический совет звучит так: бери ту из двух, что лучше ложится на твои требования по гибкости и массиву, не доверяй чужим бенчмаркам вслепую, прогони замер на своём железе и своём профиле нагрузки, и потрать освободившееся от спора время на настройки, которые реально двигают цифры.