lesson ── postgres-labs ── ~24 мин ── 5 шагов
Настоящий сбой в учебной песочнице не устроить, но всю машинерию
восстановления можно потрогать на работающем сервере. Восстановление
начинается с redo point - позиции журнала, записанной последней
контрольной точкой. Сейчас ты найдёшь redo point, увидишь, как
CHECKPOINT подтягивает его к текущей позиции, и через pg_walinspect
посмотришь ровно те записи, которые crash recovery проиграл бы. Работай в
основном во вкладке psql-a.
интерактивный sandbox
Поднимется контейнер postgreslab/postgres-base с PostgreSQL 17 и psql. В браузере откроется терминал, база lab уже настроена. Каждый шаг проверяется автоматически. Сеть air-gapped, наружу контейнер не ходит.
stack ── PostgreSQL 17 · psql · 1 GB RAM · air-gapped · самоуничтожается через 45 мин простоя
SELECT redo_lsn, checkpoint_lsn FROM pg_control_checkpoint();
redo_lsn - место в журнале, с которого crash recovery начал бы
проигрывание. Всё до него уже надёжно в файлах данных.
✓ redo point найден — отсюда стартовало бы восстановление.
CREATE TABLE recov_demo (tag text);
INSERT INTO recov_demo
SELECT 'committed' FROM generate_series(1, 500);
SELECT pg_current_wal_lsn() > (SELECT redo_lsn FROM pg_control_checkpoint())
AS lsn_ahead_of_redo;
Текущая позиция ушла вперёд от redo point. Промежуток между ними - и есть то, что восстановление пришлось бы доиграть.
✓ Между redo point и текущим LSN накопилась работа для redo.
CHECKPOINT;
SELECT pg_current_wal_lsn() - redo_lsn AS gap_bytes
FROM pg_control_checkpoint();
Контрольная точка записала грязные буферы и сдвинула redo point к
текущей позиции. Предскажи: промежуток gap_bytes станет большим или
маленьким?
После контрольной точки восстанавливать почти нечего — redo point близко к текущему LSN.
✓ redo point подтянулся к текущей позиции — восстановление стало бы коротким.
UPDATE recov_demo SET tag = 'changed' WHERE ctid IN
(SELECT ctid FROM recov_demo LIMIT 100);
SELECT count(*) AS records_to_replay
FROM pg_get_wal_records_info(
(SELECT redo_lsn FROM pg_control_checkpoint()),
pg_current_wal_lsn());
Это ровно те журнальные записи, которые redo накатил бы от redo point до конца журнала, случись сбой прямо сейчас.
✓ Ты увидел содержимое будущего redo своими глазами.
Теперь покажем, почему redo не «оживляет» незакоммиченное. Во вкладке psql-a вставь строку, но не коммить:
BEGIN;
INSERT INTO recov_demo VALUES ('ghost');-- COMMIT не делаем!
Redo при восстановлении накатил бы эту строку физически - но её транзакция не отмечена в clog как зафиксированная, поэтому по правилам видимости её не видно. Проверим из соседней сессии.
✓ Из psql-b «призрака» не видно — незакоммиченное фактически откачено.
Восстановление стартует с redo point из pg_control_checkpoint(). Контрольная точка подтягивает redo point к текущей позиции, сокращая будущее восстановление. pg_walinspect показывает записи, которые проиграл бы redo. Незакоммиченные транзакции невидимы через clog.
команды
SELECT redo_lsn FROM pg_control_checkpoint();позиция старта восстановленияSELECT count(*) FROM pg_get_wal_records_info(redo_lsn, pg_current_wal_lsn());что проиграл бы redoCHECKPOINT;подтянуть redo point вперёдконцепции