Разберем процесс репликации. Репликация в PostgreSQL — это процесс синхронизации нескольких копий кластера баз данных на разных серверах. Она бывает логической и физической.

Задачи и виды репликации

Репликация PostgreSQL решает две задачи:

  • отказоустойчивость — если сломается один из серверов, клиенты могут продолжить работать на резервном;
  • масштабируемость — резервный сервер принимает запросы на чтение, так что некоторую нагрузку можно возложить на него.

Репликация на серверах PostgreSQL бывает двух видов:

  • Физическая — основной сервер передает поток wal записей на сервер репликации. Требования следующие:
    • одинаковые версии PostgreSQL;
    • одинаковые ОС;
    • возможна репликация только всего кластера.
  • Логическая — поставщик публикует свои изменения, а подписчик получает и применяет эти изменения у себя. Особенности следующие:
    • оба сервера могут быть и поставщиком и подписчиком, но на разные объекты. Например на в одном кластере опубликована одна табличка, а на другом — другая, и эти кластера подписаны друг на друга (на эти таблички). Это позволяет использовать двухсторонний обмен;
    • репликация возможна между разными ОС;
    • возможна выборочная репликация отдельных объектов кластера.

Физическая репликация PostgreSQL

Алгоритм создания такой репликации следующий:

  • Делаем резервную копию с помощью pg_basebackup. С помощью отдельных опций указываем что копию нужно подготовить к дальнейшей репликации.
  • Разворачиваем полученную резервную копию на сервере репликации.
  • Там же, создаем специальный файл с настройками репликации (если были указаны нужные опции для команды pg_basebackup, то все необходимые файлы, нужные для репликации, создадутся автоматом):
    • в 10 версии PostgreSQL создаём файл recovery.conf, прописываем там standby_mode = on;
    • начиная с 12 версии создаём пустой файл standby.signal;
  • Стартуем PostgreSQL на реплике, после чего начнётся процесс репликации. Сервер начнёт процесс восстановления из потока wal записей.

Сервер репликации может принимать запросы, но только на чтение. Этот сервер не генерирует wal записи, а продолжает их получать из потока по протоколу репликации с основного сервера.

Журнальные записи можно передавать по протоколу репликации или можно использовать архив WAL. Но обычно применяют первое.

На главном сервере появляется процесс wal sender, который передаёт wal записи. На сервере репликации появляется wal receiver, который принимает эти записи.

Репликация может быть синхронной и асинхронной. При синхронной репликации основной сервер не только отправляет запись на реплику, но и дожидается подтверждения что запись туда дошла и была там применена. Но синхронная реплика сильно тормозит производительность кластера.

Переключение на реплику

Переключение на реплику осуществляется либо планово, либо аварийно. Но в любом случае оно происходит в ручном режиме. Хотя процесс можно автоматизировать, например с помощью скриптов.

Для восстановления основного сервера его обычно превращают в реплику и синхронизируют с бывшей репликой. После синхронизации сервера опять меняют местами.

Восстановление основного сервера проделывают следующими способами:

  • Если основной сервер был остановлен корректно и все журнальные записи успели перейти на реплику, то можно просто поменять сервера местами:
    • останавливаем основной сервер;
    • реплику превращаем в основной сервер;
    • основной сервер превращаем в реплику.
  • Если основной сервер был остановлен некорректно, то может произойти рассогласование журнальных записей. В таком случае алгоритм следующий:
    • реплику превращаем в основной сервер (пусть клиенты пока поработают на этом сервере);
    • на основном сервере (который был выключен некорректно) очищаем каталог PGDATA;
    • затем основной сервер превращаем в реплику.
  • Восстановление с помощью утилиты pg_rewind позволяет не очищать PGDATA даже при некорректном завершении. Но не всегда это срабатывает. Обычно для восстановления можно вначале пробовать утилиту pg_rewind, а если не сработает то очищать PGDATA.

Сценарии использования физической репликации

  • обычная репликация — для создания резервного сервера;
  • каскадная репликация — к основному серверу подключаем реплику, а к этой реплики еще одну реплику;
  • отложенная репликация — в recovery.conf специальным параметром можно указать задержку воспроизведения. Чтобы реплика всегда отставала от основного сервера, например, на час.

Логическая репликация PostgreSQL

При репликации передаются wal записи, но для работы логической репликации нужно изменить формат этих записей. Для этого нужно поменять параметр кластера wal_level = logical.

Поставщик — передаёт логические wal записи. Но передаются не все команды, а только INSERT, UPDATE, DELETE и TRANSCATE. То есть CREATE не передаётся, поэтому нужна начальная физическая синхронизация. Также мы можем ограничить публикацию некоторыми командами, например передавать только INSERT, а UPDATE не передавать.

При такой репликации всегда используется слот логической репликации. Благодаря которому понятно, какие записи уже были переданы, а какие нет.

Подписчик — получает wal записи и применяет изменения без разбора, трансформаций и планирования.

Подписчик и поставщик это равноценные сервера, они оба доступны на чтение и запись для клиентов. Поэтому могут возникать конфликты. Например на поставщике выполняется INSERT, а на подписчике уже такая строка есть. Подобные конфликты решаются пока только в ручном режиме.

На поставщике работает уже знакомый процесс wal sender, а на подписчике logical replication worker который получает логические wal записи и применяет их от имени суперпользователя.

Сценарии использования логической репликации

  • Собираем данные на центральном кластере. Например в каждом филиале есть сервер баз данных и данные со всех этих серверов собираются на каком-нибудь центральном сервере.
  • Распространяем данные с центрального кластера. Например на центральном сервере мы меняем какую-то табличку, а подписчики в филиалах обновляют её у себя.
  • Можно использовать логическую репликация для обновления кластера. Так как версия Postgres не имеет значение, можем выключить и обновить подписчик до новой версии. Затем поменять местами поставщика и подписчика. И наконец выключить и обновить основной сервер.
  • Мультимастер — кластер в котором данные могут менять несколько серверов. Логическая репликация для этого тоже предназначена, но в PostgreSQL пока реализовать такое нельзя. Существует проблема зацикливания. Если оба сервера будут публиковать одну и туже таблицу и будут подписчиками друг на друга, то возникнет зацикливание и произойдёт ошибка репликации.

Практика

Физическая репликация

После предыдущего урока у нас два каталога PGDATA. При этом второй кластер выключен.

Очистим каталог /usr/local/pgsql/data2/ и заново сделаем туда резервную копию с помощью pg_basebackup. Ключ -R подготовит архивную копию к дальнейшей репликации (создаст файл standby.signal):

postgres@s-pg13:~$ rm -rf /usr/local/pgsql/data2/*

postgres@s-pg13:~$ pg_basebackup --pgdata=/usr/local/pgsql/data2 -R

postgres@s-pg13:~$ ls /usr/local/pgsql/data2
backup_label base pg_commit_ts pg_hba.conf pg_logical pg_notify pg_serial pg_stat pg_subtrans pg_twophase pg_wal postgres.conf postgresql.conf backup_manifest global pg_dynshmem pg_ident.conf pg_multixact pg_replslot pg_snapshots pg_stat_tmp pg_tblspc PG_VERSION pg_xact postgresql.auto.conf standby.signal

Помимо standby.signal в postgresql.auto.conf были внесены параметры для последующей репликации:

postgres@s-pg13:~$ cat /usr/local/pgsql/data2/postgresql.auto.conf
# Do not edit this file manually!
# It will be overwritten by the ALTER SYSTEM command.
work_mem = '10MB'
track_io_timing = 'on'
track_functions = 'all'
log_min_duration_statement = '0'
log_line_prefix = '(pid=%p) '
primary_conninfo = 'user=postgres passfile=''/home/postgres/.pgpass'' channel_binding=disable port=5432 sslmode=disable sslcompression=0 ssl_min_protocol_version=TLSv1.2 gssencmode=disable krbsrvname=postgres target_session_attrs=any'

Был добавлен параметр primary_conninfo, в котором указаны опции подключения к основному серверу:

  • port=5432
  • sslmode=disable
  • и другие.

Вот ещё пример, который не нужно выполнять!

Задаются параметры подключения к основному серверу с помощью опций pg_basebackup. Например мы подключались бы к другому серверу, а для репликации был бы отдельный пользователь, тогда команда выглядела бы так:

$ pg_basebackup -h 192.168.0.100 -D /var/lib/postgresql/12/main -R -P -U replication -X stream

В команде выше мы используем следующие опции:

  • -h — хост основного сервера;
  • -D — каталог PGDATA на основном сервере;
  • -R — создает файл standby.signal;
  • -P — включает отчет о прогрессе;
  • -U — указываем пользователя для подключения;
  • -X stream —  используется для включения необходимых wal файлов в резервную копию, stream означает потоковую передачу WAL.

После такой команды, конфигурационный файл postgresql.auto.conf будет содержать такой параметр подключения к основному серверу:

primary_conninfo = 'user=replication passfile=''/var/lib/postgresql/.pgpass'' host=192.168.0.100 port=5432 sslmode=prefer sslcompression=0 gssencmode=prefer krbsrvname=postgres target_session_attrs=any'

Поменяем порт второго кластера, чтобы он мог работать одновременно с первым:

postgres@s-pg13:~$ echo 'port = 5433' >> /usr/local/pgsql/data2/postgresql.auto.conf

Файл standby.signal который появился во втором кластере означает настройку standby_mode=on в recovery.conf до PostgreSQL 12. То есть ничего делать не нужно для того чтобы перевести сервер в режим работы реплики.

Можем запустить второй кластер:

postgres@s-pg13:~$ pg_ctl -w -l /home/postgres/logfile2 -D /usr/local/pgsql/data2 start
waiting for server to start.... done
server started

Посмотрим на процессы реплики. Процесс walreceiver streaming принимает поток wal записей, а процесс startup recovering применяет изменения:

postgres@s-pg13:~$ ps -o pid,command --ppid `head -n 1 /usr/local/pgsql/data2/postmaster.pid`
  PID COMMAND
 8320 postgres: startup recovering 000000010000000000000009
 8321 postgres: checkpointer
 8322 postgres: background writer
 8323 postgres: stats collector
 8324 postgres: walreceiver streaming 0/9000148

В основном кластере появился процесс walsender postgres, который передаёт wal записи:

postgres@s-pg13:~$ ps -o pid,command --ppid `head -n 1 /usr/local/pgsql/data/postmaster.pid`
  PID COMMAND
 8325 postgres: walsender postgres [local] streaming 0/9000148
29392 postgres: checkpointer
29393 postgres: background writer
29394 postgres: walwriter
29395 postgres: autovacuum launcher
29396 postgres: stats collector
29397 postgres: logical replication launcher

Проверить состояние репликации можно в представлении pg_stat_replication на главном сервере:

postgres@s-pg13:~$ psql -p 5432
Timing is on.
psql (13.3)
Type "help" for help.

postgres@postgres=# SELECT * FROM pg_stat_replication \gx
-[ RECORD 1 ]----+------------------------------
pid              | 8325
usesysid         | 10
usename          | postgres
application_name | walreceiver
client_addr      |
client_hostname  |
client_port      | -1
backend_start    | 2021-06-30 11:18:38.820549+03
backend_xmin     |
state            | streaming
sent_lsn         | 0/9000148
write_lsn        | 0/9000148
flush_lsn        | 0/9000148
replay_lsn       | 0/9000148
write_lag        |
flush_lag        |
replay_lag       |
sync_priority    | 0
sync_state       | async
reply_time       | 2021-06-30 11:21:19.151904+03

Time: 1,885 ms

Внесём некоторые изменения на мастере:

postgres@postgres=# CREATE DATABASE replica_overview;
CREATE DATABASE
Time: 84,617 ms

postgres@postgres=# \c replica_overview;
You are now connected to database "replica_overview" as user "postgres".

postgres@replica_overview=# CREATE TABLE test(id integer PRIMARY KEY, descr text);
CREATE TABLE
Time: 4,285 ms

Проверим, создались ли эти объекты на сервере репликации:

postgres@replica_overview=# \q

postgres@s-pg13:~$ psql -p 5433 -d replica_overview
Timing is on.
psql (13.3)
Type "help" for help.

postgres@replica_overview=# SELECT * FROM test;
 id | descr
----+-------
(0 rows)

Time: 0,961 ms

Сама реплика ничего менять не может:

postgres@replica_overview=# INSERT INTO test VALUES (2, 'Два');
ERROR:  cannot execute INSERT in a read-only transaction
Time: 0,295 ms

Переведём реплику в обычный режим, то есть отключим репликацию. Это делается с помощью команды promote. При этом у нас пропадет файлик standby.signal.

postgres@replica_overview=# \q

postgres@s-pg13:~$ pg_ctl -w -D /usr/local/pgsql/data2 promote
waiting for server to promote.... done
server promoted

postgres@s-pg13:~$ ls /usr/local/pgsql/data2/
backup_label.old global pg_hba.conf pg_multixact pg_serial pg_stat_tmp pg_twophase pg_xact postgresql.conf
backup_manifest pg_commit_ts pg_ident.conf pg_notify pg_snapshots pg_subtrans PG_VERSION postgres.conf postmaster.opts
base pg_dynshmem pg_logical pg_replslot pg_stat pg_tblspc pg_wal postgresql.auto.conf postmaster.pid

После этого второй кластер может писать данные:

postgres@s-pg13:~$ psql -p 5433 -d replica_overview
Timing is on.
psql (13.3)
Type "help" for help.

postgres@replica_overview=# INSERT INTO test VALUES (2, 'Два');
INSERT 0 1
Time: 1,328 ms

Логическая репликация

Поменяем параметр wal_level = logical для первого кластера и перезапустим этот его:

postgres@replica_overview=# \q

postgres@s-pg13:~$ psql -p 5432 -d replica_overview
Timing is on.
psql (13.3)
Type "help" for help.

postgres@replica_overview=# ALTER SYSTEM SET wal_level = logical;
ALTER SYSTEM
Time: 0,898 ms

postgres@replica_overview=# \q

postgres@s-pg13:~$ pg_ctl -w -l /home/postgres/logfile -D /usr/local/pgsql/data restart
waiting for server to shut down.... done
server stopped
waiting for server to start.... done
server started

В первом кластере создадим публикацию (CREATE PUBLICATION) и посмотрим на неё с помощью команды \dRp+:

postgres@s-pg13:~$ psql -d replica_overview
Timing is on.
psql (13.3)
Type "help" for help.

postgres@replica_overview=# CREATE PUBLICATION test_pub FOR TABLE test;
CREATE PUBLICATION
Time: 2,804 ms

postgres@replica_overview=# \dRp+
                            Publication test_pub
  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
 postgres | f          | t       | t       | t       | t         | f
Tables:
    "public.test"

Во втором кластере подписываемся на эту публикацию (CREATE SUBSCRIPTION) и отключаем первоначальное копирование данных (copy_data = false):

postgres@replica_overview=# \q

postgres@s-pg13:~$ psql -p 5433 -d replica_overview
Timing is on.
psql (13.3)
Type "help" for help.

postgres@replica_overview=# CREATE SUBSCRIPTION test_sub CONNECTION 'host=localhost port=5432 user=postgres dbname=replica_overview' PUBLICATION test_pub WITH (copy_data = false);
NOTICE:  created replication slot "test_sub" on publisher
CREATE SUBSCRIPTION
Time: 11,027 ms

Выше вы можете заметить предупреждение, что на публикующем сервере создался слот репликации (NOTICE: created replication slot «test_sub» on publisher)!

Теперь посмотрим на подписку с помощью команды \dRs:

postgres@replica_overview=# \dRs
            List of subscriptions
   Name   |  Owner   | Enabled | Publication
----------+----------+---------+-------------
 test_sub | postgres | t       | {test_pub}
(1 row)

Проверим настроенную логическую репликацию. Для этого в первом кластере, вставим новую строчку в опубликованной табличке:

postgres@replica_overview=# \q

postgres@s-pg13:~$ psql -d replica_overview
Timing is on.
psql (13.3)
Type "help" for help.

postgres@replica_overview=# INSERT INTO test VALUES (3, 'Три');
INSERT 0 1
Time: 1,416 ms

Во втором кластере увидим эту строку:

postgres@replica_overview=# \q

postgres@s-pg13:~$ psql -p 5433 -d replica_overview
Timing is on.
psql (13.3)
Type "help" for help.

postgres@replica_overview=# SELECT * FROM test;
 id | descr
----+-------
  2 | Два
  3 | Три
(2 rows)
Time: 0,540 ms

Состояние подписки можно посмотреть в представлении pg_stat_subscription:

postgres@replica_overview=# SELECT * FROM pg_stat_subscription \gx
-[ RECORD 1 ]---------+------------------------------
subid                 | 24587
subname               | test_sub
pid                   | 8578
relid                 |
received_lsn          | 0/9036BE8
last_msg_send_time    | 2021-06-30 11:37:07.893511+03
last_msg_receipt_time | 2021-06-30 11:37:07.893552+03
latest_end_lsn        | 0/9036BE8
latest_end_time       | 2021-06-30 11:37:07.893511+03

Time: 1,585 ms

К процессам сервера подписчика добавился logical replication worker:

postgres@replica_overview=# \q

postgres@s-pg13:~$ ps -o pid,command --ppid `head -n 1 /usr/local/pgsql/data2/postmaster.pid`
  PID COMMAND
 8321 postgres: checkpointer
 8322 postgres: background writer
 8323 postgres: stats collector
 8424 postgres: walwriter
 8425 postgres: autovacuum launcher
 8426 postgres: logical replication launcher
 8578 postgres: logical replication worker for subscription 24587

Если репликация больше не нужна, надо удалить подписку, иначе на публикующем сервере останется открытым слот репликации:

postgres@s-pg13:~$ psql -p 5433 -d replica_overview
Timing is on.
psql (13.3)
Type "help" for help.

postgres@replica_overview=# DROP SUBSCRIPTION test_sub;
NOTICE:  dropped replication slot "test_sub" on publisher
DROP SUBSCRIPTION
Time: 13,534 ms

На этом этот курс подошёл к концу! За основу взяты видео с этого плейлиста на YouTube. Я просто проделал все на PostgreSQL 13 и лишь некоторые моменты связанные с репликацией отличаются от PostgreSQL 10. В дальнейшем, если вернусь к теме PostgreSQL постараюсь усовершенствовать этот курс, спасибо за внимание!

Сводка
Репликация в PostgreSQL
Имя статьи
Репликация в PostgreSQL
Описание
Репликация в PostgreSQL - это процесс синхронизации нескольких копий кластера баз данных на разных серверах. Она бывает логической и физической

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *