Архив будем хранить в каталоге /var/lib/postgresql/archive. Он должен быть доступен пользователю-владельцу PostgreSQL.
postgres$ mkdir /var/lib/postgresql/archive
В реальной практике архив может размещаться на отдельном сервере или дисковой системе с доступом по сети.
Включим режим архивирования и установим команду копирования заполненных сегментов журнала.
Выполняем команды от имени суперпользовательской роли:
student$ psql -U postgres -c "ALTER SYSTEM SET archive_mode = on"
ALTER SYSTEM
student$ psql -U postgres -c "ALTER SYSTEM SET archive_command = 'test ! -f /var/lib/postgresql/archive/%f && cp %p /var/lib/postgresql/archive/%f'"
ALTER SYSTEM
В archive_command мы сначала проверяем наличие файла с указанным именем в архиве, и копируем его только в случае отсутствия.
В archive_command можно указать произвольную команду, лишь бы она завершалась со статусом 0 только в случае успеха. Например, можно организовать сжатие архивируемых сегментов:
α=> -- SET archive_command = 'test ! -f /var/lib/postgresql/archive/%f && gzip <%p >/var/lib/postgresql/archive/%f'
В идеале команда архивирования должна выполнять sync, чтобы файл гарантированно попал в энергонезависимую память. Иначе при сбое он может пропасть из архива, а сервер может успеть стереть его из каталога pg_wal.
В общем случае удобно поместить всю необходимую логику архивирования в отдельный скрипт и вызываеть его:
α=> -- SET archive_command = 'archive.sh "%f" "%p"'
Для того, чтобы настройки вступили в силу, потребуется рестарт сервера.
α=> \q
student$ sudo pg_ctlcluster 10 alpha restart
Проверим работу настройки. Создадим базу и таблицу.
student$ psql
α=> CREATE DATABASE backup_archive;
CREATE DATABASE
α=> \c backup_archive
You are now connected to database "backup_archive" as user "student".
α=> CREATE TABLE t(s text);
CREATE TABLE
α=> INSERT INTO t VALUES ('Привет, мир!');
INSERT 0 1
Вот какой сегмент WAL используется сейчас:
α=> SELECT pg_walfile_name(pg_current_wal_lsn());
pg_walfile_name -------------------------- 000000010000000000000003 (1 row)
Обратите внимание: первые восемь цифр в имени файла - номер текущей ветви времени.
Чтобы заполнить файл, пришлось бы выполнить большое количество операций, но в тестовых целях проще принудительно переключить сегмент:
student$ psql -U postgres -c "SELECT pg_switch_wal()"
pg_switch_wal --------------- 0/3026220 (1 row)
α=> INSERT INTO t VALUES ('Доброе утро, страна!');
INSERT 0 1
Сегмент сменился:
α=> SELECT pg_walfile_name(pg_current_wal_lsn());
pg_walfile_name -------------------------- 000000010000000000000004 (1 row)
А предыдущий должен был попасть в архив. Проверим:
postgres$ ls -l /var/lib/postgresql/archive
total 16384 -rw------- 1 postgres postgres 16777216 июн 13 19:58 000000010000000000000003
Таким образом, архивация насторена и работает.
postgres$ rm -rf /var/lib/postgresql/10/beta/*
Поскольку мы используем архив, попросим pg_basebackup не добавлять файлы журнала к резервной копии:
postgres$ pg_basebackup -U student --wal-method=none --pgdata=/var/lib/postgresql/10/beta
NOTICE: pg_stop_backup complete, all required WAL segments have been archived
Каталог pg_wal резервной копии пуст:
postgres$ ls -l /var/lib/postgresql/10/beta/pg_wal/
total 4 drwx------ 2 postgres postgres 4096 июн 13 19:59 archive_status
А в архиве прибавилось файлов:
postgres$ ls -l /var/lib/postgresql/archive
total 49156 -rw------- 1 postgres postgres 16777216 июн 13 19:58 000000010000000000000003 -rw------- 1 postgres postgres 16777216 июн 13 19:58 000000010000000000000004 -rw------- 1 postgres postgres 16777216 июн 13 19:59 000000010000000000000005 -rw------- 1 postgres postgres 302 июн 13 19:59 000000010000000000000005.00000024.backup
Заглянем в сгенерированный файл метки:
postgres$ cat /var/lib/postgresql/10/beta/backup_label
START WAL LOCATION: 0/5000024 (file 000000010000000000000005) CHECKPOINT LOCATION: 0/5000058 BACKUP METHOD: streamed BACKUP FROM: master START TIME: 2018-06-13 19:58:59 MSK LABEL: pg_basebackup base backup
Главная информация в этом файле - указание начальной точки для восстановления (строка START WAL LOCATION). Теоретически восстановление можно начать и с более ранней (но не более поздней) позиции, но это потребует больше времени.
Эту же строку надо принимать во внимание, определяя, какие файлы из архива еще нужны, а какие можно удалить.
Изменим порт:
postgres$ echo 'port = 5433' >> /var/lib/postgresql/10/beta/postgresql.auto.conf
Перенаправим непрерывное архивирование в другой каталог, чтобы не возникало коллизий с работающим первым сервером:
postgres$ mkdir /var/lib/postgresql/archive_beta
postgres$ sed -i 's/\/archive/\/archive_beta/g' /var/lib/postgresql/10/beta/postgresql.auto.conf
Вот что получилось:
postgres$ cat /var/lib/postgresql/10/beta/postgresql.auto.conf
# Do not edit this file manually! # It will be overwritten by the ALTER SYSTEM command. archive_mode = 'on' archive_command = 'test ! -f /var/lib/postgresql/archive_beta/%f && cp %p /var/lib/postgresql/archive_beta/%f' port = 5433
Теперь надо создать файл recovery.conf. В простейшем случае в него достаточно поместить команду восстановления, которая будет копировать указанный сегмент WAL обратно из архива по указанному пути:
postgres$ echo "restore_command = 'cp /var/lib/postgresql/archive/%f %p'" >/var/lib/postgresql/10/beta/recovery.conf
Если в recovery.conf не указана целевая точка восстановления (один из параметров recovery_target*), то к базовой резервной копии будут применены записи WAL из всех файлов в архиве.
Следует иметь в виду, что самые последние изменения не попадут в архив, пока очередной сегмент WAL не будет заполнен (или пока не пройдет время, указанное в параметре archive_timeout).
α=> INSERT INTO t VALUES ('Еще не в архиве');
INSERT 0 1
Запускаем сервер.
student$ sudo pg_ctlcluster 10 beta start
Файл recovery.conf переименовался в recovery.done:
postgres$ ls -l /var/lib/postgresql/10/beta/recovery.*
-rw-r--r-- 1 postgres postgres 57 июн 13 19:59 /var/lib/postgresql/10/beta/recovery.done
Проверим, что восстановлено в таблице:
student$ psql -p 5433 -d backup_archive
β=> SELECT * FROM t;
s ---------------------- Привет, мир! Доброе утро, страна! (2 rows)
Как и ожидалось, последняя строка не попала в архив и не восстановилась.
Что стало с ветвью времени после восстановления?
β=> SELECT pg_walfile_name(pg_current_wal_lsn());
pg_walfile_name -------------------------- 000000020000000000000006 (1 row)
Номер увеличился на единицу.
В каталоге pg_wal появился файл истории, соответствующий этой ветви:
postgres$ ls -l /var/lib/postgresql/10/beta/pg_wal/00000002.history
-rw------- 1 postgres postgres 41 июн 13 19:59 /var/lib/postgresql/10/beta/pg_wal/00000002.history
В нем есть информация о "точках ветвления", через которые мы пришли в данную ветвь времени:
postgres$ cat /var/lib/postgresql/10/beta/pg_wal/00000002.history
1 0/6000000 no recovery target specified
Эти файлы PostgreSQL использует, когда мы указываем ветвь времени в recovery.conf. Поэтому файл истории подлежит архивации вместе с сегментами WAL, и удалять из архива их не надо:
postgres$ ls -l /var/lib/postgresql/archive_beta
total 4 -rw------- 1 postgres postgres 41 июн 13 19:59 00000002.history
Конец демонстрации.