Пошук уроків, статей та іншого контенту
Налаштуєте архівацію WAL і виконаєте Point-in-Time Recovery до заданого моменту або стану бази.
Point-in-Time Recovery (PITR) — це відновлення кластера PostgreSQL зі створеної базової копії та послідовне застосування WAL-журналів до вибраного моменту.
WAL містить записи про зміни даних. PostgreSQL спочатку записує зміну до WAL, а вже потім може записати змінені сторінки таблиць на диск. Якщо зберігати завершені WAL-сегменти в архіві, їх можна застосувати до базової копії та відновити стан кластера:
на певний момент часу;
до іменованої точки відновлення;
до конкретного LSN;
до кінця доступного WAL.
Схема відновлення виглядає так:
Створюється базова резервна копія кластера.
Усі наступні WAL-сегменти копіюються до архіву.
Для відновлення береться базова копія.
PostgreSQL отримує WAL із архіву через restore_command.
Відновлення зупиняється на заданій цілі.
PITR не замінює регулярні базові копії. WAL-архів сам по собі не є повною резервною копією.
Для PITR потрібні:
wal_level = replica або вище;
archive_mode = on;
команда archive_command або archive_library.
У типовій конфігурації достатньо wal_level = replica. Параметр archive_mode вмикається під час запуску сервера, тому після його зміни потрібен повний restart, а не лише reload.
Приклад налаштувань у postgresql.conf:
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /var/lib/postgresql/wal-archive/%f && cp %p /var/lib/postgresql/wal-archive/%f'У цьому прикладі:
%p — шлях до WAL-файлу відносно каталогу даних;
%f — ім’я WAL-файлу;
test ! -f ... не дозволяє перезаписати вже заархівований файл;
команда повертає код 0, якщо архівування завершилося успішно.
Каталог архіву має існувати, а користувач, від імені якого працює PostgreSQL, повинен мати до нього доступ:
sudo install -d -o postgres -g postgres -m 700 /var/lib/postgresql/wal-archiveПісля зміни archive_mode перезапустіть сервер:
sudo systemctl restart postgresqlІ перевірте значення параметрів:
SHOW wal_level;
SHOW archive_mode;
SHOW archive_command;PostgreSQL архівує WAL-сегмент після його заповнення. На малонавантаженій системі це може відбуватися нечасто. Для перевірки або зменшення затримки можна примусово завершити поточний сегмент:
SELECT pg_switch_wal();Перевірити стан архіватора можна так:
SELECT
archived_count,
last_archived_wal,
last_archived_time,
failed_count,
last_failed_wal,
last_failed_time
FROM pg_stat_archiver;Якщо failed_count зростає, WAL не потрапляє до архіву. Найчастіші причини:
неправильний шлях;
відсутні права доступу;
переповнений диск;
помилка команди копіювання;
недоступне віддалене сховище.
Для PITR потрібна базова копія, з якої PostgreSQL почне відтворення WAL. Найзручніше створити її за допомогою pg_basebackup.
Приклад:
sudo -u postgres pg_basebackup \
-D /srv/backups/base-2026-09-01 \
-Fp \
-X stream \
-P \
-c fastПараметри:
-D — каталог базової копії;
-Fp — копія у вигляді звичайних файлів;
-X stream — передавати необхідний WAL окремим потоком під час копіювання;
-P — показувати прогрес;
-c fast — виконати швидку контрольну точку перед створенням копії.
Каталог базової копії не повинен бути каталогом, у якому працює поточний кластер. Після завершення переконайтеся, що команда завершилася без помилок.
У production-середовищі базову копію потрібно зберігати на іншому диску або в іншому сховищі. Якщо зберігати її разом із сервером бази даних, втрата диска може знищити і робочу базу, і резервну копію.
Для відновлення до часу використовується параметр recovery_target_time.
Наприклад:
recovery_target_time = '2026-09-01 14:35:00+03'
recovery_target_action = 'promote'Час потрібно задавати однозначно, бажано із часовим поясом. Якщо часовий пояс не вказати, інтерпретація залежатиме від часового поясу сервера.
Відновлення до часу не завжди означає відновлення безпосередньо після конкретної SQL-команди. PostgreSQL застосовує WAL послідовно та зупиняється, коли досягає вказаної часової цілі.
Тому для критичних операцій краще створювати іменовану точку відновлення.
Перед небезпечною операцією можна створити логічну мітку в WAL:
SELECT pg_create_restore_point('before_customer_cleanup');Функція повертає LSN, але для відновлення можна використовувати ім’я:
recovery_target_name = 'before_customer_cleanup'
recovery_target_action = 'promote'Така точка має бути заархівована разом із відповідними WAL-сегментами. Одразу після створення точки доцільно примусово перемкнути WAL-сегмент:
SELECT pg_create_restore_point('before_customer_cleanup');
SELECT pg_switch_wal();Іменована точка точніша за приблизний час, оскільки прив’язана до конкретного запису в WAL.
Якщо відомий LSN, можна використати:
recovery_target_lsn = '0/7000060'
recovery_target_action = 'promote'LSN можна отримати, наприклад, із результату pg_create_restore_point() або з журналів і засобів моніторингу.
Одночасно задавати recovery_target_time, recovery_target_name і recovery_target_lsn не потрібно. Виберіть одну ціль.
Нижче наведено типовий сценарій відновлення до іменованої точки.
Відновлення виконується в окремий каталог або на окремому сервері. Не можна видаляти файли активного production-кластера без перевіреної копії.
sudo systemctl stop postgresqlЯкщо відновлення виконується на окремому сервері, зупиніть його тестовий кластер.
Приклад для окремого тестового кластера:
sudo mv /var/lib/postgresql/16/main /var/lib/postgresql/16/main.before-pitr
sudo cp -a /srv/backups/base-2026-09-01 /var/lib/postgresql/16/main
sudo chown -R postgres:postgres /var/lib/postgresql/16/main
sudo chmod 700 /var/lib/postgresql/16/mainШляхи залежать від операційної системи, версії PostgreSQL і способу інсталяції.
У каталозі даних відновлюваного кластера додайте до postgresql.conf:
restore_command = 'cp /var/lib/postgresql/wal-archive/%f %p'
recovery_target_name = 'before_customer_cleanup'
recovery_target_action = 'promote'restore_command отримує:
%f — ім’я WAL-файлу, який потрібно знайти;
%p — шлях, куди потрібно його скопіювати.
Команда повинна завершуватися з ненульовим кодом, якщо файл ще не знайдений. Це повідомляє PostgreSQL, що потрібного WAL поки немає або він відсутній.
Для відновлення до часу конфігурація матиме такий вигляд:
restore_command = 'cp /var/lib/postgresql/wal-archive/%f %p'
recovery_target_time = '2026-09-01 14:35:00+03'
recovery_target_action = 'promote'Для PostgreSQL 12 і новіших версій створіть порожній файл recovery.signal:
sudo -u postgres touch /var/lib/postgresql/16/main/recovery.signalФайл має належати користувачу PostgreSQL:
sudo chown postgres:postgres /var/lib/postgresql/16/main/recovery.signalНаявність recovery.signal вказує PostgreSQL запустити режим відновлення. Після досягнення цілі та promotion цей файл перейменовується або видаляється самим PostgreSQL.
sudo systemctl start postgresqlПеревірте стан сервера:
sudo -u postgres psql -c "SELECT pg_is_in_recovery();"Якщо recovery_target_action = 'promote', після досягнення цілі значення стане false, і кластер працюватиме як звичайний primary.
Повний приклад сценарію:
#!/usr/bin/env bash
set -euo pipefail
DATA_DIR="/var/lib/postgresql/16/main"
BASE_BACKUP="/srv/backups/base-2026-09-01"
WAL_ARCHIVE="/var/lib/postgresql/wal-archive"
# Зупиняємо тестовий кластер перед заміною його файлів.
sudo systemctl stop postgresql
# Зберігаємо поточний каталог на випадок перевірки або повторної спроби.
sudo mv "$DATA_DIR" "${DATA_DIR}.before-pitr"
# Розгортаємо базову копію.
sudo cp -a "$BASE_BACKUP" "$DATA_DIR"
sudo chown -R postgres:postgres "$DATA_DIR"
sudo chmod 700 "$DATA_DIR"
# Додаємо параметри відновлення.
sudo -u postgres bash -c "cat >> '$DATA_DIR/postgresql.conf' <<EOF
restore_command = 'cp $WAL_ARCHIVE/%f %p'
recovery_target_name = 'before_customer_cleanup'
recovery_target_action = 'promote'
EOF"
# У PostgreSQL 12+ цей файл вмикає режим відновлення.
sudo -u postgres touch "$DATA_DIR/recovery.signal"
# Запускаємо відновлення.
sudo systemctl start postgresqlЦей сценарій передбачає, що каталог архіву доступний на сервері відновлення, а базова копія сумісна з його конфігурацією та версією PostgreSQL.
Іноді потрібно спочатку перевірити відновлений стан, а вже потім зробити кластер доступним для запису. Для цього використовується:
recovery_target_action = 'pause'Після досягнення цілі PostgreSQL призупинить відновлення. Кластер залишатиметься в режимі recovery, тому він буде доступний лише для читання.
Стан можна перевірити:
SELECT pg_is_in_recovery();Після перевірки можна:
продовжити відтворення WAL;
або виконати promotion і зробити кластер записуваним.
Для promotion використовується:
sudo -u postgres pg_ctl \
-D /var/lib/postgresql/16/main \
promoteЯкщо після відновлення потрібен звичайний primary, часто зручніше одразу використовувати recovery_target_action = 'promote'.
Після завершення відновлення перевірте:
SELECT pg_is_in_recovery();Очікуване значення після promotion:
fДалі потрібно перевірити саме ті дані, заради яких виконувалося відновлення:
SELECT count(*) FROM customers;
SELECT max(created_at) FROM orders;Також перегляньте журнали PostgreSQL. У них мають бути повідомлення про:
запуск recovery;
використання WAL із архіву;
досягнення recovery target;
завершення recovery або promotion.
Не вважайте успішний запуск сервера достатньою перевіркою. Сервер може запуститися, але відновлення може бути зупинене раніше, ніж очікувалося, або досягнення потрібного WAL може бути неможливим через неповний архів.
Для відновлення потрібен безперервний ланцюжок WAL від базової копії до цільового моменту. Якщо одного сегмента немає, PostgreSQL не зможе продовжити recovery.
Тому не можна просто видаляти старі WAL-файли за віком, не враховуючи базові копії, які ще можуть використовуватися.
Політика очищення повинна гарантувати, що для кожної доступної базової копії зберігається весь потрібний діапазон WAL.
Команда archive_command запускається для кожного завершеного WAL-сегмента. Якщо команда повертає помилку, PostgreSQL не вважає сегмент заархівованим і спробує повторити операцію.
Не слід використовувати команду, яка завжди повертає успіх, наприклад:
archive_command = 'cp %p /some/path/%f || true'У такому разі PostgreSQL може вважати архівування успішним, навіть якщо копіювання завершилося помилкою.
Локальний каталог на тому самому диску захищає від помилкового видалення даних, але не захищає від:
відмови диска;
пошкодження файлової системи;
втрати сервера;
шифрувальника;
помилки адміністратора.
Практична конфігурація повинна копіювати WAL до іншого диска, сервера або сховища та регулярно перевіряти можливість відновлення.
archive_mode змінили без restartarchive_mode не застосовується звичайним reload. Після зміни потрібен повний restart PostgreSQL.
recovery.signalУ PostgreSQL 12 і новіших версіях одного лише restore_command недостатньо. Для запуску recovery потрібен recovery.signal.
restore_commandЯкщо команда повертає успіх, коли WAL-файл відсутній, PostgreSQL може поводитися некоректно під час пошуку наступного сегмента. Команда повинна повертати помилку, якщо файл не знайдено.
WAL потрібно застосовувати до базової копії того самого кластера. Не можна взяти базову копію іншого кластера та очікувати, що його WAL буде застосовано коректно.
Навіть один відсутній WAL-сегмент може зробити відновлення до потрібної точки неможливим. Потрібно контролювати pg_stat_archiver, резервне сховище та результати тестових відновлень.
Часова ціль залежить від часового поясу та точності часу на сервері. Для важливої операції краще заздалегідь створити pg_create_restore_point() і відновлюватися за recovery_target_name.
Під час PITR легко переплутати production-кластер і відновлений екземпляр. Для відновлення використовуйте окремий сервер, порт або каталог і перевіряйте data_directory:
SHOW data_directory;
SHOW port;PITR відновлює базу з базової копії та послідовного WAL-архіву.
Для архівації потрібно налаштувати wal_level, archive_mode і archive_command.
Базову копію зручно створювати через pg_basebackup.
Для відновлення задаються restore_command і recovery.signal.
Ціллю recovery може бути час, ім’я точки відновлення або LSN.
Для точної операції варто використовувати pg_create_restore_point().
recovery_target_action = 'promote' автоматично робить відновлений кластер доступним для запису.
Надійність PITR потрібно перевіряти не лише за статусом сервера, а й практичними тестовими відновленнями.