WAL, контрольные точки и долговечность

Рейтинг: 70.1% · 9 голосов
Подробный курс по PostgreSQL: SQL по демобазе, типы данных и JSONB, индексы, транзакции и MVCC, VACUUM, EXPLAIN и планировщик, роли и привилегии, репликация, бэкап, настройка под нагрузку и под 1С. Актуально на 2026.
Ответить
Аватара пользователя
Egor_DBA
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

WAL, контрольные точки и долговечность

Сообщение Egor_DBA »

Оглавление курса (47)
  1. Что такое PostgreSQL: история, философия, где применяется
  2. Урок 1. Что нового в PostgreSQL 15 и далее (15 -> 16 -> 17)
  3. Установка и первый запуск: Linux, Windows, Docker
  4. psql детально: метакоманды и работа в консоли
  5. Демобаза Авиаперевозки Postgres Pro: структура и развёртывание
  6. SELECT: проекция, фильтрация, сортировка
  7. Транзакции и ACID
  8. Уровни изоляции транзакций: Read Committed, Repeatable Read, Serializable
  9. Соединения таблиц (JOIN)
  10. Агрегация: GROUP BY, HAVING, GROUPING SETS
  11. Подзапросы и CTE (WITH), рекурсия
  12. Оконные функции
  13. Изменение данных: INSERT/UPDATE/DELETE, UPSERT, MERGE
  14. Числовые и булевы типы, NULL и трёхзначная логика
  15. Строки, текст, дата и время, таймзоны
  16. Массивы, диапазоны, enum, составные типы
  17. JSON и JSONB: операторы и индексация
  18. DDL: CREATE TABLE, схемы, ALTER
  19. Ограничения целостности
  20. Представления и материализованные представления
  21. Последовательности, IDENTITY, генерируемые столбцы
  22. Индексы: B-tree и когда индекс не используется
  23. Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN
  24. Продвинутые индексы: частичные, по выражению, покрывающие
  25. Обслуживание индексов и таблиц: bloat, REINDEX, CONCURRENTLY
  26. Полнотекстовый поиск
  27. Роли и пользователи
  28. Привилегии: GRANT/REVOKE
  29. Row-Level Security и безопасность данных
  30. Подключение приложения: pg_hba.conf, SSL, пулы соединений
  31. Серверное программирование: функции и процедуры
  32. Триггеры и события
  33. Архитектура PostgreSQL: процессы и память
  34. MVCC: версии строк и видимость
  35. VACUUM, autovacuum, freeze и wraparound
  36. WAL, контрольные точки и долговечность (вы здесь)
  37. Блокировки и взаимоблокировки
  38. Планировщик запросов
  39. EXPLAIN и EXPLAIN ANALYZE: чтение планов
  40. Конфигурация сервера: ключевые параметры
  41. Настройка под нагрузку и под 1С
  42. Параллельные запросы и партиционирование
  43. Резервное копирование и восстановление
  44. Репликация: потоковая и hot standby
  45. Логическая репликация и кластерные решения
  46. Внешние данные, расширения и сертификация
  47. Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE
Урок 35. WAL, контрольные точки и долговечность

Представьте, что сервер postgresql выключили рывком - выдернули питание прямо посреди транзакции. Откуда база знает, что уже зафиксировано, а что нет, и почему данные на диске не превращаются в кашу? Ответ - журнал предзаписи WAL (Write-Ahead Log). Это сердце надёжности PostgreSQL. В этом уроке разберём, зачем нужен WAL, как он обеспечивает краш-рекавери, репликацию и восстановление на момент времени (PITR), что такое контрольные точки (checkpoint) и процесс checkpointer, и как параметры full_page_writes, fsync, synchronous_commit и wal_level балансируют между долговечностью и скоростью записи.

Изображение

Как это работает

Главная идея одной фразой: прежде чем менять страницу данных, запиши намерение в журнал. Когда транзакция что-то правит, PostgreSQL не бежит сразу переписывать табличные файлы на диске. Сначала он формирует компактную запись об изменении (WAL-запись) и кладёт её в журнал. Слово "предзапись" буквально означает: журнал пишется ВПЕРЁД самих данных.

Это даёт фокус. Сами страницы таблиц и индексов живут в разделяемой памяти (буферном кэше) и считаются грязными, пока их не сбросили на диск. На диск их можно отправить лениво, потом, пачкой. А вот WAL при коммите обязан физически дойти до диска. Получается, что вместо множества случайных записей в разные файлы мы делаем одну последовательную дозапись в конец журнала - а последовательная запись на любом носителе в разы быстрее случайной.

Долговечность (буква D в ACID) обеспечивается именно так: в момент COMMIT процесс backend вызывает сброс журнала и ждёт подтверждения, что WAL до этого места лёг на диск. Если сразу после этого сервер падает, при следующем старте PostgreSQL входит в восстановление: читает журнал и заново проигрывает (накатывает) все изменения, которые не успели попасть в файлы данных. Это и есть краш-рекавери. Зафиксированное не теряется, незавершённое откатывается.

Контрольная точка (checkpoint) - это гарантия, с которой можно начинать восстановление. В этот момент фоновый процесс checkpointer сбрасывает на диск все грязные страницы, накопившиеся до определённой позиции в журнале. После успешного checkpoint всё, что было раньше, уже надёжно в файлах данных, и старые сегменты WAL можно переиспользовать или удалить. Поэтому восстановление после краха начинается не с начала времён, а с последней контрольной точки - чем чаще checkpoint, тем быстрее старт после сбоя, но тем выше нагрузка на диск в обычной работе. Это классический компромисс, которым вы управляете через checkpoint_timeout и max_wal_size.

Зачем full_page_writes. Запись страницы на диск не атомарна: типичная страница PostgreSQL 8 КБ, а сектор диска 512 байт или 4 КБ. При сбое питания страница может оказаться записанной наполовину (torn page, разорванная запись). Чтобы такую полусломанную страницу можно было починить, после каждого checkpoint при первом изменении страницы PostgreSQL пишет в журнал её полный образ. Это раздувает WAL, зато гарантирует, что восстановление получит целую страницу, поверх которой накатит остальные изменения.

SQL и примеры

Посмотрим ключевые параметры долговечности. Демобаза "Авиаперевозки" (схема bookings) подойдёт, чтобы погонять реальную запись.

Код: Выделить всё

-- какой уровень журналирования и режимы безопасности заданы
SHOW wal_level;            -- minimal | replica | logical
SHOW fsync;                -- on - синхронизировать запись с диском
SHOW synchronous_commit;   -- on | off | local | remote_apply ...
SHOW full_page_writes;     -- on - писать полные образы страниц

-- сразу группой, с единицами измерения
SELECT name, setting, unit
FROM pg_settings
WHERE name IN ('wal_level','fsync','synchronous_commit',
               'full_page_writes','checkpoint_timeout','max_wal_size');
Текущая позиция в журнале и сколько WAL сгенерировала рабочая нагрузка:

Код: Выделить всё

-- текущая точка записи в журнал (LSN, Log Sequence Number)
SELECT pg_current_wal_lsn();

-- зафиксируем LSN, выполним массовую запись, посчитаем прирост журнала
SELECT pg_current_wal_lsn() AS before \gset

-- нагружаем таблицу бронирований из демобазы
UPDATE bookings.bookings
SET total_amount = total_amount + 0
WHERE book_date > '2017-07-01';

SELECT pg_size_pretty(
         pg_wal_lsn_diff(pg_current_wal_lsn(), :'before')
       ) AS wal_written;
Здесь pg_wal_lsn_diff показывает, сколько байт журнала породил даже безобидный на вид UPDATE - часть из них уйдёт на полные образы страниц.

Принудительно запустить контрольную точку и посмотреть статистику:

Код: Выделить всё

CHECKPOINT;  -- требует прав суперпользователя

-- статистика контрольных точек (в PG 15-16)
SELECT checkpoints_timed, checkpoints_req,
       buffers_checkpoint, buffers_clean
FROM pg_stat_bgwriter;
Важно про версии: в PostgreSQL 17 статистику checkpoint вынесли в отдельное представление pg_stat_checkpointer (поля num_timed, num_requested, buffers_written), а pg_stat_bgwriter оставили только под фоновую запись. Если пишете мониторинг под 17, смотрите новое представление.

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

Код: Выделить всё

-- сжимать full-page образы в журнале (с PG 15 поддержаны lz4 и zstd)
ALTER SYSTEM SET wal_compression = 'zstd';
SELECT pg_reload_conf();
Частые грабли
  • Выключают fsync ради скорости и забывают вернуть. При любом сбое питания база может стать нечитаемой без шанса на восстановление. fsync=off допустим только на одноразовой загрузочной инстанции, которую не жалко пересоздать.
  • Путают synchronous_commit=off с потерей целостности. На самом деле база останется консистентной после краха, но несколько последних подтверждённых транзакций могут пропасть. Для денег это недопустимо, для логов и аналитики - часто приемлемо.
  • Думают, что wal_level=minimal ускорит всё и везде. Он экономит журнал на массовых загрузках в одной транзакции (COPY в только что созданную таблицу), но полностью ломает физическую реплику и PITR - архивировать с него нечего.
  • Ставят слишком большой max_wal_size, радуются редким checkpoint, а потом в восстановлении после сбоя сервер стартует минутами, потому что накопилось много неприменённых изменений.
  • Отключают full_page_writes на обычном железе. Это приглашение к разорванным страницам при сбое питания. Отключать его можно лишь там, где атомарность 8 КБ гарантирует сама подсистема хранения (некоторые ФС с CoW, отдельные массивы).
  • Кладут pg_wal на тот же раздел, что и данные, и при всплеске WAL диск переполняется - сервер останавливается целиком. WAL любит отдельный быстрый том.
  • Для PostgreSQL под 1С забывают, что тяжёлые регламентные операции дают шквал журнала: без выделенного диска под WAL и адекватного max_wal_size checkpoint начинает штормить и проседает отклик.
Мини-лаба
  • Подключитесь к своему серверу: psql -d demo. Выполните SHOW wal_level; и группу параметров через запрос к pg_settings из урока. Запишите текущие значения.
  • Зафиксируйте LSN: SELECT pg_current_wal_lsn() \gset как before.
  • Сгенерируйте нагрузку: сделайте UPDATE по таблице bookings.ticket_flights, меняющий какое-нибудь поле у тысяч строк.
  • Посчитайте прирост журнала через pg_wal_lsn_diff и pg_size_pretty. Запомните число.
  • Выполните CHECKPOINT; затем сразу повторите тот же UPDATE и снова замерьте прирост WAL. Сравните с шагом 4 и объясните разницу (подсказка: full_page_writes после контрольной точки).
  • Включите wal_compression = 'zstd' через ALTER SYSTEM и pg_reload_conf(), повторите замер. Оцените, насколько уменьшился объём журнала.
  • Посмотрите счётчики в pg_stat_bgwriter (или pg_stat_checkpointer на PG 17): сколько контрольных точек прошло по таймеру, а сколько по требованию.
Контрольные вопросы
  • Почему запись в WAL быстрее, чем прямая запись изменений в табличные файлы, хотя данные в итоге всё равно окажутся на диске?
  • Что именно происходит при краш-рекавери и с какой позиции журнала PostgreSQL начинает накат изменений?
  • Какую проблему решает full_page_writes и почему первое изменение страницы после checkpoint дороже остальных?
  • Чем грозит synchronous_commit=off и в каких сценариях это допустимый компромисс?
  • Какие значения wal_level существуют и какой минимально нужен для физической реплики и для PITR?
  • Как checkpoint_timeout и max_wal_size влияют на скорость восстановления после сбоя и на нагрузку в обычной работе?
👍 ❤️4 🔥1 😄 🤔
Аватара пользователя
cephmaker
Сообщения: 1
Зарегистрирован: 18 май 2026, 09:49

Re: WAL, контрольные точки и долговечность

Сообщение cephmaker »

А если synchronous_commit=off, то после краша я теряю последние транзакции - а как понять, сколько именно? Есть способ оценить окно потерь?
👍1 ❤️1 🔥 😄 🤔2
Аватара пользователя
pimpman
Сообщения: 1
Зарегистрирован: 27 май 2026, 18:06

Re: WAL, контрольные точки и долговечность

Сообщение pimpman »

Замерил у себя: первый UPDATE после CHECKPOINT дал раза в три больше WAL, чем повторный. Реально из-за full_page_writes, как в уроке. С wal_compression=zstd просело прилично.
👍2 ❤️ 🔥 😄 🤔2
Ответить
← Предыдущая глава
VACUUM, autovacuum, freeze и wraparound
Следующая глава →
Блокировки и взаимоблокировки

Все главы курса «PostgreSQL: от первого запроса до продакшена»

Поделиться темой: ✈ Telegram VK

Вернуться в «PostgreSQL: от первого запроса до продакшена»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя