linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Главы
  • How it worksскоро
  • Уроки
  • База знаний
  • Собеседование
Часть IV — Буферный кеш и журнал

$ глава 19 · 55 минут

Контрольные точки и восстановление

У журнала из прошлой главы есть очевидная проблема: если каждое изменение надо уметь доиграть из WAL, то при восстановлении придётся проигрывать журнал с самого начала времён. Через год работы это были бы терабайты журнала и часы восстановления.

Контрольная точка (checkpoint) решает эту проблему. Это момент, когда PostgreSQL гарантирует: все изменения до некоторой точки журнала уже надёжно записаны в файлы данных. Значит, при восстановлении журнал можно проигрывать не с начала, а только с этой точки. Контрольная точка - это закладка, дальше которой откатываться не нужно.

В этой главе соберём всё вместе: что физически делает контрольная точка, как из неё растёт процедура восстановления после сбоя, и как параметры checkpoint_timeout и max_wal_size управляют балансом между скоростью работы и временем восстановления. И проверим главное обещание PostgreSQL

  • что закоммиченное переживёт kill -9.

19.1 Зачем нужна контрольная точка

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

Контрольная точка ставит границу. В этот момент PostgreSQL сбрасывает на диск все грязные буферы, накопленные к данному моменту, и записывает в служебный файл pg_control отметку: «до вот этой позиции журнала (redo point) все изменения уже в файлах данных».

После этого весь журнал до redo point больше не нужен для восстановления - его можно переиспользовать или удалить. А восстановление, случись оно, начнётся не с начала времён, а с последней контрольной точки. Чем чаще контрольные точки, тем короче восстановление - но тем больше работы по записи в обычное время.

19.2 Redo point: откуда начнётся восстановление

Тонкость: контрольная точка не мгновенна. Пока она сбрасывает грязные буферы (это может занять минуты), база продолжает работать и писать новый журнал. Поэтому redo point - это позиция журнала на момент начала контрольной точки, а не её завершения.

Логика такая: всё, что было грязным к началу контрольной точки, она гарантированно запишет. Значит, восстановление, начатое с redo point, увидит в журнале все изменения, которые на тот момент ещё не были в файлах данных, и доиграет их. Изменения до redo point уже на диске - их проигрывать не нужно.

pg_control хранит redo point последней завершённой контрольной точки. Именно его читает PostgreSQL при старте, чтобы понять, с какого места проигрывать журнал.

19.3 Восстановление после сбоя

Теперь сложим процедуру. Сервер упал - kill -9, отключили питание, паника ядра. При следующем старте PostgreSQL видит, что был остановлен некорректно, и запускает crash recovery:

По шагам:

  1. читаем redo point из pg_control;
  2. идём по журналу от redo point вперёд, применяя каждую запись к страницам (это фаза redo);
  3. full-page images по пути чинят возможные рваные страницы, дельты накатывают остальные изменения;
  4. доходим до места, где журнал обрывается (битая или неполная CRC) - это конец доступного журнала;
  5. база открыта.

Восстановление идёт ровно столько журнала, сколько накопилось между последней контрольной точкой и сбоем - поэтому частота контрольных точек напрямую задаёт верхнюю границу времени восстановления.

19.4 Что происходит с незакоммиченными транзакциями

Естественный вопрос: redo проигрывает весь журнал, включая изменения транзакций, которые на момент сбоя не были закоммичены. Разве они не «оживут»?

Нет, и вот почему. Redo действительно накатывает на страницы и изменения незакоммиченных транзакций - физически они появляются в данных. Но видимость в PostgreSQL решает не наличие версии, а статус её транзакции в clog. Транзакция, не успевшая закоммититься до сбоя, в clog так и осталась незавершённой - а значит, по правилам видимости её версии не видны никому. Фактически она откачена.

Получается красиво: redo не разбирает, кто закоммичен, а кто нет - он тупо восстанавливает физическое состояние. А логику «видно/не видно» берёт на себя MVCC поверх clog. Поэтому после восстановления закоммиченные изменения на месте, а незакоммиченные невидимы - ровно как и положено.

19.5 Когда запускается контрольная точка

Контрольную точку запускают два повода, что наступит раньше:

  • по времени - checkpoint_timeout (по умолчанию 5 минут);
  • по объёму журнала - когда с прошлой контрольной точки накопилось журнала больше max_wal_size (по умолчанию 1 ГБ).

Плюс контрольную точку можно вызвать вручную командой CHECKPOINT или косвенно - например, она случается перед остановкой сервера.

max_wal_size - это не жёсткий лимит на размер pg_wal/, а порог, при котором инициируется контрольная точка. Если база пишет быстрее, чем контрольные точки успевают, журнал может временно превысить этот размер. Поэтому на нагруженных базах max_wal_size поднимают, чтобы контрольные точки случались по таймеру, а не каждые несколько секунд по объёму.

19.6 Размазывание записи: checkpoint_completion_target

Если контрольная точка сбросит все грязные буферы разом, получится всплеск нагрузки на диск: только что было тихо - и вдруг шквал записи. Это бьёт по задержкам пользовательских запросов.

Чтобы сгладить всплеск, PostgreSQL растягивает запись грязных буферов во времени. Управляет этим checkpoint_completion_target (по умолчанию 0.9): контрольная точка старается размазать запись на 90% интервала до следующей. Вместо шквала - ровный фоновый поток записи.

Это объясняет, почему ручной CHECKPOINT ощущается как нагрузка, а штатные контрольные точки - почти нет: ручная торопится, штатная не спешит. На пиковых базах баланс между checkpoint_timeout, max_wal_size и checkpoint_completion_target - один из главных рычагов сглаживания нагрузки на диск. Разбор и метрики - в checkpoint.

19.7 Наблюдать за контрольными точками

В PostgreSQL 17 статистику по контрольным точкам вынесли в отдельный вид pg_stat_checkpointer (раньше она жила в pg_stat_bgwriter):

sql
SELECT num_timed, num_requested, buffers_written
FROM pg_stat_checkpointer;
  • num_timed - контрольные точки по таймеру (checkpoint_timeout);
  • num_requested - по объёму журнала или вручную;
  • buffers_written - сколько грязных буферов они записали.

Здоровый признак - когда num_timed заметно больше num_requested: значит, контрольные точки случаются спокойно по расписанию, а не авралом из-за переполнения журнала. Перекос в сторону num_requested

  • сигнал поднять max_wal_size. При log_checkpoints = on каждая контрольная точка ещё и пишет в лог сервера подробности.

19.8 Граница главы: восстановление есть, репликации пока нет

Мы собрали полный цикл надёжности одиночного сервера: изменения идут в журнал (write-ahead), копятся грязными в кеше, периодически сбрасываются контрольной точкой, а после сбоя redo доигрывает журнал от последней контрольной точки. Закоммиченное переживает сбой, незакоммиченное откатывается через clog.

Тот же журнал умеет больше, чем восстановление на одном сервере: его можно передать на реплику, заархивировать для восстановления на точку в прошлом, разобрать на логические изменения. Но сколько именно информации писать в журнал - зависит от режима wal_level. Им и займёмся в последней главе части.

Уроки в sandbox

lab-19.1. Найди redo point и посмотри, что проиграет восстановление

Вскроем машинерию восстановления, не дожидаясь настоящего сбоя. Прочитаем redo point из управляющих данных, увидим, как контрольная точка подтягивает его к текущей позиции, и через pg_walinspect посмотрим ровно те записи журнала, которые crash recovery проиграл бы от redo point до конца. Перед шагами предскажи, как сдвинется redo point после CHECKPOINT.

  1. Прочитай redo point: SELECT redo_lsn FROM pg_control_checkpoint(); - это место, с которого началось бы восстановление.

  2. Нагенерируй изменения (INSERT/UPDATE) и сравни redo_lsn с pg_current_wal_lsn() - между ними и лежит то, что пришлось бы проиграть.

  3. Вызови CHECKPOINT; и снова сними redo_lsn - предскажи: подтянется ли он к текущей позиции?

  4. Через pg_get_wal_records_info(redo_lsn, pg_current_wal_lsn()) посмотри записи, которые проиграло бы восстановление.

  5. Открой во второй сессии незакоммиченную транзакцию и убедись, что её строк не видно: redo накатил бы их физически, но clog держит их невидимыми.

sandbox с автопроверкой - открыть в песочнице

Резюме

  • Контрольная точка сбрасывает все грязные буферы на диск и записывает в `pg_control` redo point - позицию журнала, до которой данные уже на диске.
  • Восстановление начинается не с начала журнала, а с redo point последней контрольной точки; частота контрольных точек задаёт верхнюю границу времени recovery.
  • Redo point - позиция на момент НАЧАЛА контрольной точки, потому что сброс буферов не мгновенен, а база тем временем пишет новый журнал.
  • Crash recovery проигрывает WAL от redo point вперёд до обрыва CRC; FPI чинят рваные страницы, дельты накатывают изменения.
  • Redo физически накатывает и незакоммиченные изменения, но их транзакции не отмечены в clog - MVCC делает их невидимыми, то есть откатывает.
  • Контрольная точка запускается по `checkpoint_timeout` (5 мин), по `max_wal_size` (1 ГБ) или вручную; запись размазывается через `checkpoint_completion_target`.
  • Статистику смотрят в `pg_stat_checkpointer` (PG17): перекос `num_requested` над `num_timed` - сигнал поднять `max_wal_size`.

Контрольные вопросы

  1. Зачем нужна контрольная точка, если все изменения и так есть в журнале?

    Показать ответ

    Чтобы не проигрывать журнал с начала времён. Без контрольных точек восстановление после сбоя означало бы повтор всех изменений за всю историю базы. Контрольная точка сбрасывает грязные буферы на диск и фиксирует redo point - позицию, до которой данные уже надёжно в файлах. После этого журнал до redo point не нужен для восстановления, а recovery начинается с этой точки. Чем чаще контрольные точки, тем короче восстановление, но тем больше работы по записи в обычное время.

  2. Почему redo point - это позиция на начало контрольной точки, а не на её конец?

    Показать ответ

    Потому что контрольная точка не мгновенна: сброс всех грязных буферов может занять минуты, и всё это время база работает и пишет новый журнал. Контрольная точка гарантирует, что запишет всё, что было грязным к её началу. Значит, восстановление, начатое с позиции на начало контрольной точки, увидит в журнале все изменения, которых на тот момент ещё не было на диске, и доиграет их. Брать позицию на конец было бы неверно - часть изменений между началом и концом могла не попасть в файлы данных.

  3. Если redo проигрывает весь журнал, почему незакоммиченные транзакции не оживают?

    Показать ответ

    Redo действительно накатывает на страницы и изменения незакоммиченных транзакций - физически их версии появляются в данных. Но видимость в PostgreSQL определяется статусом транзакции в clog, а не наличием версии. Транзакция, не успевшая закоммититься до сбоя, осталась в clog незавершённой, поэтому по правилам видимости её версии не видны никому

    • она фактически откачена. Redo восстанавливает физическое состояние, а логику «видно/не видно» берёт на себя MVCC поверх clog.
  4. Что запускает контрольную точку и что означает max_wal_size?

    Показать ответ

    Контрольная точка запускается по таймеру (checkpoint_timeout, 5 мин по умолчанию), по объёму накопленного журнала (max_wal_size, 1 ГБ), вручную командой CHECKPOINT или, например, перед остановкой сервера - что наступит раньше. max_wal_size - не жёсткий лимит на размер pg_wal/, а порог, при котором инициируется контрольная точка. Если база пишет быстрее, журнал может временно его превысить. На нагруженных базах его поднимают, чтобы контрольные точки шли по таймеру, а не авралом по объёму.

  5. На что смотреть в pg_stat_checkpointer и о чём говорит перекос num_requested?

    Показать ответ

    Смотрят на num_timed (контрольные точки по таймеру), num_requested (по объёму журнала или вручную) и buffers_written (сколько буферов они сбросили). Здоровая картина - когда num_timed заметно больше num_requested: контрольные точки случаются спокойно по расписанию. Перекос в сторону num_requested означает, что журнал переполняется быстрее, чем проходит таймер, и контрольные точки идут авралом по объёму - это сигнал поднять max_wal_size.

← Предыдущая18-wal-basicsСледующая →20-wal-levels
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки