P99 fsync против пиковых IOPS: что брать для планирования
p99 fsync против пиковых IOPS сравнивать бессмысленно, поскольку показатели снимают в разных режимах работы накопителя. Для WAL важна хвостовая задержка fsync при подтверждённой записи, тогда как пиковые IOPS обычно получают с глубокой очередью. Вывод действует только для указанной конфигурации: устройства, файловой системы, кэша, sync-метода и нагрузки.
Почему p99 fsync полезнее пиковых IOPS?
p99 fsync показывает границу, ниже которой уложились 99% синхронизаций в прогоне. Пиковые IOPS показывают пропускную способность при заданной очереди и ничего не говорят об одном flush WAL. Но и p99 оценивают только вместе с числом операций, длительностью теста и конфигурацией конкретного хранилища.
Показать, где WAL ждёт подтверждения записи
При COMMIT процесс добавляет запись о завершении транзакции в WAL и вызывает XLogFlush до нужного LSN. PostgreSQL сбрасывает WAL-буферы в файл, затем просит ОС синхронизировать его. При synchronous_commit=on клиент получает подтверждение после локального flush. Если настроена синхронная репликация, время ответа также зависит от ожидания standby и выбранного режима.
Способ синхронизации WAL задаёт wal_sync_method. На Linux по умолчанию используется fdatasync: PostgreSQL пишет WAL через файловый кэш, затем вызывает fdatasync(). Варианты open_datasync и open_sync открывают файл с другими флагами. Семантика каждого варианта разобрана в документации PostgreSQL, а скорость проверяют на файловой системе будущего pg_wal.
Завершённый write() ещё не говорит о сохранности после потери питания, поскольку данные могли остаться в памяти ядра или энергозависимом кэше контроллера. Поэтому хвостовая задержка fsync включает работу ядра, файловой системы и контроллера, а не только запись блока. Перед тестом фиксируют устройство, LVM или RAID, mount options, политику кэша и наличие power-loss protection.
Почему пик операций не предсказывает транзакционную задержку
IOPS измеряют числом завершённых операций за секунду. Высокий результат часто получают с десятками запросов в очереди, чтобы контроллер использовал параллелизм NAND. При одиночном COMMIT это преимущество исчезает, так как транзакция ждёт конкретный flush.
Средняя latency тоже скрывает паузы. Если 9900 синхронизаций заняли 0,2 мс, а 100 заняли 20 мс, среднее составит около 0,4 мс, хотя у 1% транзакций задержка будет около 20 мс. Заявленная p99 задержка хранилища требует единиц, числа выборок и периода. Без них доля медленных операций неизвестна.
Глубину очереди меняют отдельной серией. QD=1 показывает задержку без очереди, а QD=8 или 32 проверяет параллелизм. При синхронном psync значение iodepth выше единицы не создаёт настоящую очередь, поэтому нужен асинхронный engine с direct=1. Полученную пропускную способность нельзя выдавать за задержку WAL и тем более сравнивать с рекламным read IOPS.
Читать p50, p95 и p99 как форму риска
Перцентили относятся к одному распределению. p50 делит выборку пополам, выше p95 остаются 5% операций, выше p99 остаётся 1%. Для 100 000 операций этот хвост даёт около тысячи наблюдений, а для 1000 – только десять, поэтому маленькая выборка сильнее реагирует на шум.
Как измерять fsync при реалистичной нагрузке?
Воспроизведите условия реального WAL: размер синхронной записи, конкурентность, глубину очереди, sync-метод и файловую систему. Измеряйте после прогрева устройства. Затем сохраните полное распределение, число операций и временной ряд вместе с политикой кэша. После микротеста проверьте вывод транзакционной нагрузкой при тех же гарантиях сохранности данных.
Показатели p50/p95/p99 дополняют гистограммой: по ней видно, образует ли хвост один пик или несколько режимов. На графике, где IOPS отложены против задержки, подпишите block size, queue depth, jobs и длительность. Максимум сохраняют отдельно, так как выброс мог совпасть с checkpoint, discard или работой гипервизора.
По временному ряду видно, распределены ли редкие паузы по всему тесту или хвост создал один провал. Всплески сопоставляют с выводом iostat -x 1, телеметрией NVMe и checkpoint.
Повторить размер записи, очередь и конкурентность WAL
Как правило, сначала фиксируют версию PostgreSQL, wal_sync_method, synchronous_commit, объём WAL за секунду и число одновременно завершающихся транзакций. Скорость генерации WAL считают по разнице показаний pg_stat_wal, а конкурентность берут из журналов и метрик за тот же период. Размер синхронной записи берут из реального профиля, а при его отсутствии помечают как допущение. При конкуренции PostgreSQL может подтвердить несколько транзакций одним flush, поэтому число синхронизаций окажется меньше числа COMMIT.
Сначала моделируют одиночную синхронизацию с выбранным размером блока, например 8 KiB: один job, QD=1, тот же каталог и sync-метод. Затем повышают число jobs. Асинхронный QD-тест идёт отдельно, иначе невозможно понять, что дало прирост: конкурентность приложения или внутренняя очередь устройства.
Набор данных и длительность выбирают так, чтобы тест не закончился на быстром кэше. Для виртуального диска указывают thin provisioning, репликацию и локальный кэш. Бенчмарк надёжной записи, упирающийся в RAM или короткий SLC-burst, ничего не показывает. Поэтому в отчёт вносят свободное место, заполнение и способ предварительной записи.
Согласовать pg_test_fsync, fio и транзакционный прогон
pg_test_fsync сравнивает способы синхронизации и выводит среднее время операции. По документации PostgreSQL файл размещают на той же файловой системе, где будет pg_wal. Утилита помогает выбрать wal_sync_method, но не показывает хвост распределения и не предсказывает пропускную способность базы.
В прогоне, опубликованном 27 мая 2025 года, использовались PostgreSQL 16, Samsung 990 Pro, XFS и write-back-кэш. Фрагмент сырого вывода:
fdatasync: 608.573 ops/s, 1643 us/op
fsync: 177.431 ops/s, 5636 us/op
На том же сервере, но на Micron 7400 с PLP среднее время fdatasync составило 24 мкс. Полный вывод и топология стенда есть в исходном отчёте, но из-за разной конфигурации это сравнение классов накопителей, а не моделей.
Чтобы получить распределение, используют fio job, в котором заданы engine, block size, QD и длительность. Укажите отдельный тестовый файл: fio перезапишет его и потребует не менее 32 GiB свободного места.
[wal-fsync]
filename=/mnt/wal-test/fio.bin
size=32G
ioengine=psync
rw=write
bs=8k
direct=0
overwrite=1
fdatasync=1
iodepth=1
numjobs=1
time_based=1
runtime=600
ramp_time=60
percentile_list=50:95:99:99.9
Команда fio wal-fsync.fio --output=wal-fsync.txt --output-format=normal сохраняет отчёт, а percentile_list задаёт перцентили. Для WAL смотрите раздел fsync/fdatasync/sync_file_range. Отдельная асинхронная серия покажет, как меняется задержка NVMe при разной глубине очереди. Затем подтвердите результат в pgbench с -l или прикладном сценарии при тех же гарантиях сохранности данных.
Поймать cache exhaustion, flush и фоновые паузы
Короткий запуск может закончиться до исчерпания быстрого кэша SSD. Поэтому файл предварительно заполняют, устройство прогревают, а тест ведут до устойчивого режима. По временному ряду видно, как после исчерпания SLC-кэша падает пропускная способность или растёт задержка. Там же видны фоновые паузы. Один итоговый p99 смешивает быстрый старт с установившимся режимом.
Перед серией фиксируют write cache, PLP, температуру, прошивку, заполнение, discard и фоновые операции. Что из этого списка недоступно на виртуальном диске, также отмечают в отчёте. Кэш не отключают, поскольку тест повторяет рабочую конфигурацию, а потерю питания проверяют отдельно.
Какой p99 допустим для транзакционной нагрузки?
1. Универсального порога нет: вычтите из SLO время сети, блокировок, CPU и других синхронных шагов.
2. Определите, какую часть остатка можно отдать ожиданию WAL с учётом group commit.
3. Задайте запас и долю нарушений, затем подтвердите порог транзакционным тестом.
p99 fsync 5 мс уже больше, чем весь SLO в 4 мс. При SLO 100 мс такая задержка может поместиться в бюджет только после учёта остальных ожиданий. Результат проверяют после прогрева и при рабочем заполнении диска, а критерий пересматривают при смене прошивки, миграции VM или росте нагрузки.
Связать хвост fsync с SLO транзакций
p99 fsync нельзя напрямую приравнивать к p99 транзакции. В критический путь транзакции входят блокировки, вычисления, сеть, репликация и иногда несколько flush. Group commit объединяет синхронизации, а synchronous_commit=off убирает ожидание локального диска. Поэтому перед сравнением выясняют, какие этапы идут последовательно, а какие перекрываются.
Затем сопоставляются временные ряды. Если пики commit latency совпадают с ростом времени WAL fsync, await и длины очереди, накопитель становится основной гипотезой. Если метрики хранения остаются ровными, задержку ищут в блокировках, CPU или ожидании реплики. Причину подтверждают изменением одного фактора: устройства, sync-метода или профиля нагрузки.
В SLO записывайте порог, долю нарушений, окно и минимальное число операций. Вместе с результатами транзакционного теста нужно сохранить latency histogram, указав границы бакетов и число наблюдений. Размер запаса задаётся до серии. При росте конкурентности тест лучше повторить: очередь и group commit меняют связь между одним flush и числом завершённых транзакций.
Сделать результат проверяемым на другом стенде
К job-файлу и сырому выводу обычно добавляют дату, версии fio, PostgreSQL и ядра, файловую систему и mount options, модель и прошивку диска, RAID/LVM, кэш и PLP, размер файла, block size, QD, jobs, длительность и прогрев. Для VM также указывают контроллер и неизвестные свойства хранилища.
В папку результата положите fio.job, fio.txt, вывод pg_test_fsync, журналы pgbench, iostat, NVMe log и CSV временного ряда. Рядом с графиком приведите перцентили и число операций. Без исходных файлов нельзя проверить единицы, фильтрацию и границы интервала.
У steady-state workload должен быть критерий: например, 10 минут после 5-минутного прогрева или остановка fio по условию steady state. Выбор зафиксируйте в job-файле. В краткой версии публикации оставьте график p99 и ссылку на полную методику.
Хранилище для WAL выбирают по повторяемой задержке подтверждённой записи и по тому, укладывается ли транзакция в свой SLO. Сначала проверьте sync-метод на файловой системе pg_wal, затем снимите распределение задержек в fio и подтвердите вывод транзакционным тестом. Сырой вывод нужен, чтобы не спутать смену накопителя с изменением нагрузки. После такой проверки p99 fsync против пиковых IOPS перестаёт быть выбором между двумя цифрами: для планирования берут p99, измеренный при нужных гарантиях записи и сопоставленный с SLO.














