Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → WAL: журнал предзаписи и долговечность

WAL: как журнал предзаписи обеспечивает долговечность данных

WAL: принцип write-ahead logging, fsync и групповой коммит, контрольные точки, репликация и настройка durability в PostgreSQL.

Прямой ответ

WAL (write-ahead log, журнал предзаписи) — механизм, при котором любое изменение данных сначала записывается в последовательный журнал, и только потом — в сами файлы данных. Это основа свойства Durability из ACID и одновременно фундамент репликации и восстановления.

Принцип простой: изменение считается совершённым, когда запись о нём оказалась на устойчивом носителе в журнале. Сами страницы данных можно писать позже, лениво, потому что при сбое их состояние восстанавливается из журнала.

Почему это выгодно:

  • Последовательная запись вместо случайной. Журнал дописывается в конец, а страницы данных разбросаны по диску. Последовательный ввод на порядок дешевле.
  • Одна запись на транзакцию. Вместо записи нескольких изменённых страниц достаточно одной записи в журнале.
  • Точка восстановления. После сбоя база проигрывает журнал с последней контрольной точки и приходит в согласованное состояние.

Ключевое место — fsync. Это системный вызов, который требует от операционной системы и устройства действительно записать данные на носитель, а не оставить их в кэше страницы. Без fsync запись в журнал ничего не гарантирует: при потере питания кэш исчезает.

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

  1. Групповой коммит. Несколько транзакций объединяются в один fsync, поэтому пропускная способность при параллельных коммитах значительно выше, чем стоимость одного fsync.
  2. Контрольная точка (checkpoint). Периодически все грязные страницы сбрасываются на диск, а журнал до этой точки можно переиспользовать. Слишком частые контрольные точки увеличивают нагрузку записью, слишком редкие — удлиняют восстановление после сбоя.

Настройки durability в PostgreSQL:

Значение synchronous_commit Гарантии Риск
on Коммит подтверждён после fsync журнала Минимальный
remote_apply / remote_write Подтверждение от реплики Зависит от реплики
local Локальный fsync, без ожидания реплики Потеря при отказе узла
off Подтверждение до fsync Потеря последних миллисекунд записей
SHOW synchronous_commit;
-- точечное ослабление для некритичных данных
BEGIN;
SET LOCAL synchronous_commit = off;
INSERT INTO analytics_events (payload) VALUES ('...');
COMMIT;

Практика: эксплуатация журнала

1. Журнал — это и репликация. Физическая репликация в PostgreSQL — это передача потока записей WAL на реплику. Отсюда следует: настройка журнала напрямую влияет на задержку репликации и на возможность восстановления — см. реплику для чтения.

2. Архивация WAL даёт восстановление на момент времени. Если сохранять сегменты журнала вместе с базовыми копиями, можно восстановить базу на любую точку внутри окна хранения — см. PITR.

3. Размер журнала ограничен. Сегменты переиспользуются после контрольной точки. При всплеске записи количество сегментов растёт, и место на диске журнала заканчивается; база останавливается. Мониторинг свободного места в каталоге WAL обязателен.

4. wal_level определяет объём информации. Значения minimal, replica и logical отличаются тем, сколько данных попадает в журнал. Для логической репликации нужен logical; он увеличивает объём записи.

5. Отключение fsync — не оптимизация. Параметр fsync = off даёт заметный прирост скорости записи, но при сбое питания база может оказаться несогласованной настолько, что потребуется пересоздание. Это допустимо только для одноразовых задач загрузки данных, где исходный источник доступен.

6. Кэш контроллера должен быть защищён. Если дисковый контроллер имеет кэш на запись с батарейным питанием, fsync дёшев. Если контроллер лжёт о выполнении fsync, гарантии нет даже при корректных настройках базы. Документация PostgreSQL прямо указывает на этот риск.

Рекомендации:

  1. Держите журнал на быстром устройстве. Задержка fsync напрямую определяет задержку коммита. Отдельный NVMe-том под WAL — частое и эффективное решение.
  2. Балансируйте контрольные точки. Настройте целевой интервал так, чтобы всплески записи не совпадали с массовым сбросом грязных страниц.
  3. Следите за задержкой репликации. Она измеряется в объёме неприменённого журнала; рост означает, что реплика не успевает.
  4. Тестируйте восстановление. Журнал, который никто не проверял на восстановление, — это гипотеза, а не гарантия.
  5. Разделяйте критичность данных. Ослабление durability допустимо для телеметрии и недопустимо для платежей; делайте это на уровне транзакции, а не всей базы.

Практический сценарий: коммиты тормозили при пиковой нагрузке

Контекст: p99 задержки коммита вырос с 3 мс до 90 мс в часы пик; CPU и память были свободны.

  1. Диагностика. Замер показал, что основное время уходит на ожидание fsync. Число транзакций в секунду выросло в шесть раз, при этом каждая фиксировалась отдельным коммитом.
  2. Причина 1. Приложение писало события по одному, не группируя их в транзакции. Групповой коммит не успевал объединять запросы, потому что между ними были промежутки.
  3. Причина 2. Каталог WAL находился на том же устройстве, что и данные, поэтому последовательная запись журнала конкурировала со случайной записью страниц при контрольных точках.
  4. Действия. (а) Запись событий перевели на батчи по 500 строк в одной транзакции; (б) журнал вынесли на отдельное NVMe-устройство; (в) увеличили целевой интервал контрольных точек, чтобы сгладить пики; (г) добавили метрику задержки fsync и объёма журнала.
  5. Результат. p99 коммита вернулся к 4 мс, число fsync в секунду снизилось в 40 раз, задержка репликации перестала расти в пик.

Вывод: производительность журнала определяется не столько настройками СУБД, сколько шаблоном записи в приложении. Батчинг транзакций почти всегда даёт больший эффект, чем подбор параметров.

FAQ

Может ли база потерять данные при synchronous_commit = on? При корректно работающем железе и файловой системе — нет, коммит подтверждается только после записи журнала на носитель. Риск возникает, если устройство кэширует запись и не выполняет fsync по-настоящему, или если отключено журналирование файловой системы.

Чем WAL отличается от бинарного лога MySQL? Назначением они похожи, но InnoDB использует пару «журнал повторного выполнения + откатный журнал»: первый отвечает за долговечность, второй — за откат и MVCC. В PostgreSQL откат реализуется иначе, через версии строк — см. MVCC.

Нужен ли журнал, если данные и так реплицируются? Да. Репликация защищает от отказа узла, но не от сбоя питания на основном узле и не даёт восстановления на момент времени. Это разные механизмы с разными задачами.

Что происходит при сбое во время контрольной точки? Контрольная точка идемпотентна: после перезапуска база начинает восстановление с последней завершённой контрольной точки и проигрывает журнал вперёд. Незавершённые транзакции откатываются.

Источники: PostgreSQL docs (WAL introduction, WAL configuration, reliability) — ссылки в шапке.