Настройка непрерывной архивации

Архив будем хранить в каталоге /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

Конец демонстрации.