wiki/docs/postgresql/Репликация.md
ilyamak04 40ba9a93c4
All checks were successful
Build MkDocs / build-and-deploy (push) Successful in 19s
commit
2025-02-25 23:10:18 +03:00

13 KiB
Raw Blame History

Настройка репликации Master-Slave

  • apt install postgresql postgresql-contrib -y - устанавливаем СУБД на обе машины
  • systemctl status postgresql

Настройка Master

# /etc/postgresql/16/main/postgresql.conf
listen_addresses = 'localhost,150.241.66.94' # 150.241.66.94 адрес ВМ, на которой slave
wal_level = replica # минимальный уровень необходимый для репликации
max_wal_senders = 5 # максимальное количество подключений для передачи WAL, максимум 5 слейвов 
wal_keep_size = 1024MB
hot_standby = on # разрешаем селекты с реплики, по умолчанию нельзя
archive_mode = on # включаем архивирование WAL
archive_command = 'find /var/lib/postgresql/16/main/archive -type f -mtime +7 -delete; gzip < %p > /var/lib/postgresql/16/main/archive/%f.gz'
# /etc/postgresql/16/main/pg_hba.conf
# файл pg_hba.conf (Host-Based Authentication) управляет доступом к PostgreSQL, определяя, какие пользователи могут подключаться
# c каких IP-адресов и каким способом аутентификации

host    replication     replicator      192.168.1.2/32           scram-sha-256

Создадим роль для репликации, выполнить:

sudo -u postgres psql
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD '<password>'; 
\q

Открыть «MASTER» базу данных, выполнить скрипт на обновление информации о настройках доступа из файла pg_hba.conf: SELECT pg_reload_conf();

??? info "Сжатый WAL" PostgreSQL не умеет автоматически разархивировать сжатые WAL-файлы. Если WAL-логи в сжатом формате (.gz), то перед восстановлением их нужно разархивировать вручную в restore_command.

Если archive_command на master сжимает WAL при архивации, например:

archive_command = 'gzip < %p > /var/lib/postgresql/archive/%f.gz'

То restore_command на slave должен разархивировать WAL перед восстановлением:

restore_command = 'gunzip -c /var/lib/postgresql/archive/%f.gz > %p'

Как это работает?

restore_command ищет запрашиваемый WAL-файл в архиве.
Если он найден в сжатом виде (.gz), команда gunzip -c разархивирует его в нужное место (%p).

slave применяет этот WAL-файл и продолжает репликацию.

Настройка Slave

По умолчанию реплику (slave) нельзя записывать данные, потому что она работает в режиме только для чтения (read-only), на то она и слейв реплика.

sudo systemctl stop postgresql
sudo -u postgres rm -rf /var/lib/postgresql/16/main/*
sudo -u postgres pg_basebackup -h 192.109.139.92 -U replicator -D /var/lib/postgresql/16/main -P -R --wal-method=stream` # 192.109.139.92 - ip мастера

Если файл standby.signal присутствует в директории данных ($PGDATA) при запуске PostgreSQL, сервер не будет принимать записи и будет получать данные с Master. Он создаётся автоматически при запуске pg_basebackup с флагом -R или вручную. Если удалить standby.signal и перезапустить PostgreSQL, сервер станет обычным Master (потеряет связь с репликой).

После выполнения pg_basebackup в /var/lib/postgresql/16/main/ на Slave должен появиться файл standby.signal. Он сообщает PostgreSQL, что сервер работает как Standby (slave).

sudo systemctl start postgresql
sudo systemctl status postgresql
# проверить репликацию на мастере 
SELECT * FROM pg_stat_replication;
# проверить репликацию на слейве
SELECT * FROM pg_stat_wal_receiver; 

На slave после окончания загрузки бд и запуска postgres проверить наличие файла /var/lib/pgsql/16/data/standby.signal, а также наличие строки подключения к серверу master в файле /var/lib/pgsql/16/data/postgresql.auto.conf

Открыть master базу данных, выполнить скрипт на проверку состояния репликации (скрипт должен вернуть строку, в поле «state» должно быть значение «streaming»)

select * from pg_stat_replication;

Открыть slave базу данных, выполнить скрипт на проверку состояния репликации (скрипт должен вернуть строку, в поле «status» должно быть значение «streaming»)

select * from pg_stat_wal_receiver;

Бэкапирование

По умолчанию pg_dump создает логический бэкап в формате plain (обычный SQL-скрипт)

В PostgreSQL есть два типа дампов:

  • Физические (pg_basebackup) — побайтовая копия файлов БД.
  • Логические (pg_dump, pg_dumpall) — SQL-скрипты или архивные файлы.

!!! info "" pg_dumpall создает резервную копию всех баз данных, роли (пользователи и группы), права доступа (GRANT/REVOKE), настройки tablespace, глобальные параметры (например, настройки аутентификации). Умеет отдавать только дамп в формате .sql

`pg_dump` не экспортирует роли, права доступа, tablespaces, параметры кластера (например, `pg_hba.conf`)
  1. plain (Текстовый SQL-скрипт)

Описание:

В этом формате по дефолту снимает pg_dump. Формат plain представляет собой обычный SQL-скрипт, содержащий скрипты CREATE TABLE, INSERT INTO, ALTER TABLE и другие скрипты для создания и наполнения бд .

Дамп в формате plain:

pg_dump -U postgres -d mydatabase -Fp -f backup.sql

Пример содержимого файла дампа:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name TEXT
);
INSERT INTO users (id, name) VALUES (1, 'Alice');
  • Можно редактировать в текстовом редакторе.
  • Можно восстановить частично, скопировав нужные команды.
  • Относительно долгое восстановление, так как все данные вставляются скриптами.

Как восстановить:

psql -U postgres -d mydatabase -f backup.sql
  1. custom (Сжатый бинарный формат)

Описание:

Формат custom является бинарным, поддерживает сжатие и позволяет восстанавливать отдельные объекты базы данных, такие как таблицы и схемы.

Как создать дамп в формате custom:

pg_dump -U postgres -d mydatabase -Fc -Z 9 -f backup.dump

Параметры:

  • Fc - указывает на использование формата custom.

  • Z 9 - применяет максимальное сжатие дампа.

  • -a или --data-only: Дамп только данных, без схемы

  • -s или --schema-only: Дамп только схемы, без данных

  • -O или --no-owner: Исключает команды SET OWNER из дампа

  • -x или --no-privileges: Исключает команды GRANT/REVOKE из дампа.

  • Поддерживает выборочное снятие отдельных объектов.

  • Поддерживает параллельное снятие с -j.

Посмотреть содержимое дампа без восстановления:

pg_restore -l backup.dump

Как восстановить базу данных:

pg_restore -U postgres -d mydatabase -f backup.dump
  1. directory (Каталог с дампом)

Описание:

Формат directory сохраняет резервную копию в виде каталога, содержащего отдельные файлы для каждой таблицы и других объектов базы данных. Этот формат позволяет параллельно снимать бэкап.

Дамп в формате directory:

pg_dump -U postgres -d mydatabase -Fd -j4 -f backup_dir/

Параметры:

  • Fd — указывает на формат directory.

  • j4 — использует 4 параллельных потока для ускорения процесса.

  • Можно восстанавливать отдельные таблицы и объекты.

Восстановление

pg_restore -U postgres -d mydatabase -j 4 backup_dir/
  1. tar (Архив tar) Описание:

Формат tar создает архив tar, содержащий все необходимые файлы для восстановления базы данных. Он удобен для хранения и передачи, но восстановление в данном формате выполняется медленнее по сравнению с directory или custom.

Дамп в формате tar:

pg_dump -U postgres -d mydatabase -F t -f backup.tar

Параметры:

  • Ft — указывает на формат tar.
  • Медленное восстановление, так как данные сначала извлекаются из архива.

Восстановление бд:

pg_restore -U postgres -d mydatabase -Ft backup.tar

!!! info "" Все форматы дампов кроме .sql восстанавливаются через pg_restore

Пример организации бэкапирования

Скрипт backup.sh для снятия бэкапа, удаления бэкапов

#!/bin/bash

if [ "$#" -ne 3 ]; then
    echo "Необходимо передать три аргумента."
    echo "Пример: $0 <имя_стенда> <имя_базы_данных> <время_жизни_бэкапа_в_днях>"
    exit 1
fi

PG_PASSWORD="postgres"
PG_PORT="5432"
STAND_NAME="$1"
DB_NAME="$2"
BACKUP_TTL="$3"
BACKUP_DIR="/opt/backups/${STAND_NAME}"
BACKUP_FILE="${STAND_NAME}_$(date +%Y%m%d).dump"
LOG_DIR="${BACKUP_DIR}/logs"
LOG_FILE="${LOG_DIR}/backup_${STAND_NAME}.log"

mkdir -p "${BACKUP_DIR}"
mkdir -p "${LOG_DIR}"

echo "$(date +%Y%m%d_%H%M%S) Начало резервного копирования базы данных ${DB_NAME}" >> ${LOG_FILE}

PGPASSWORD=${PG_PASSWORD} pg_dump -p ${PG_PORT} -h localhost -U postgres -d ${DB_NAME} -Fc -Z 9 -f "${BACKUP_DIR}/${BACKUP_FILE}" 2>> ${LOG_FILE}

if [ $? -eq 0 ]; then
    echo "$(date +%Y%m%d_%H%M%S) Резервное копирование успешно завершено: ${BACKUP_DIR}/${BACKUP_FILE}" >> ${LOG_FILE}

    find ${BACKUP_DIR} -type f -name "*.dump" -mtime +${BACKUP_TTL} -exec rm {} \;

    if [ $? -eq 0 ]; then
        echo "$(date +%Y%m%d_%H%M%S) Бэкапы старше ${BACKUP_TTL} дней удалены." >> ${LOG_FILE}
    else
        echo "$(date +%Y%m%d_%H%M%S) Ошибка при удалении бэкапов." >> ${LOG_FILE}
    fi

else
    echo "$(date +%Y%m%d_%H%M%S) Ошибка при резервном копировании базы данных ${DB_NAME}" >> ${LOG_FILE}
fi

Добавить в cron

# Для снятия бэкапа: <путь к скрипту> <имя каталога для бэкапа> <имя базы данных стенда> <кол-во хранимых бэкапов>
00 23 * * * /opt/backups/backup.sh stand1 database1 3
59 23 * * * /opt/backups/backup.sh stand2 database2 3