[Из песочницы] Replication slots в PostgreSQL

До девятой версии в PostgreSQL для создания «теплого» резервного сервера использовался WAL archiving. В версии 9.0 появилась потоковая репликация с возможностью создания «горячего» read-only сервера. В следующей версии PostgreSQL 9.4 появится новый функционал для создания потоковой репликации под названием replication slots. Рассмотрим что он собой представляет и чем отличается от предыдущих способов.На сегодняшний момент доступен первый кандидат в релизы. В качестве тестового стенда выбраны 2 виртуалки под Ubuntu 14.04. Процесс сборки и установки одинаков для основного и резервного серверов. Ставим из исходников, предварительно поставив необходимые пакеты: sudo apt-get update && sudo apt-get -y install make g++ checkinstall libreadline6-dev zlib1g-dev Скачиваем и распаковываем архив из репозитория: wget https://ftp.postgresql.org/pub/source/v9.4rc1/postgresql-9.4rc1.tar.gz tar xzf postgresql-9.4rc1.tar.gz Собираем и устанавливаем пакет: cd postgresql-9.4rc1/ ./configure make sudo checkinstall По умолчанию бинарники для работы с СУБД ставятся в /usr/local/pgsql/.Добавим пользователя postgres в систему: sudo useradd -M postgres Создадим директорию для кластера: sudo mkdir -p /data/db sudo chown postgres: postgres /data/db sudo chmod 0700 /data/db Далее выполняем действия на основном сервере. Инициализируем кластер: sudo -u postgres /usr/local/pgsql/bin/initdb -D /data/db Кроме структуры кластера, initdb создаст дефолтные конфиги. Создадим в кластере директорию pg_log, в которой будут хранится логи: sudo -u postgres mkdir /data/db/pg_log sudo -u postgres chmod 0700 /data/db/pg_log Добавим записи в pg_hba.conf для подключения пользователей и чтобы резервный сервер мог забирать WAL-логи с основного: host all all 192.168.1.0/24 md5 host replication replica 192.168.1.108/32 md5 В конфиге postgresql.conf правим параметры: listen_addresses = '*' — слушать входящие соединения на всех интерфейсахwal_level = hot_standby — необходимый формат WAL-логов для репликацииmax_wal_senders = 2 — количество одновременных соединений для репликацииlogging_collector = on — логи складываем в pg_logЗапускаем наш кластер:

sudo -u postgres /usr/local/pgsql/bin/pg_ctl -D /data/db start Смотрим состояние процессов: ps aux | grep postgres postgres 21295 0.0 0.0 23700 604? Ss 13:39 0:00 postgres: logger process postgres 21297 0.0 13.6 170880 138408? Ss 13:39 0:01 postgres: checkpointer process postgres 21298 0.0 5.0 170784 51076? Ss 13:39 0:00 postgres: writer process postgres 21299 0.0 0.5 170648 5148? Ss 13:39 0:00 postgres: wal writer process postgres 21300 0.0 0.1 171052 1836? Ss 13:39 0:00 postgres: autovacuum launcher process postgres 21301 0.2 0.1 25924 1060? Ss 13:39 0:17 postgres: stats collector process Создадим пользователя replica с правами на репликацию: /usr/local/pgsql/bin/psql -U postgres -c «create user replica with replication encrypted password '123'» Создадим тестовую базу с данными: /usr/local/pgsql/bin/createdb -U postgres testdb /usr/local/pgsql/bin/psql -U postgres -d testdb -c «create table testtable (id serial, data text)» /usr/local/pgsql/bin/psql -U postgres -d testdb -c «insert into testtable select nextval ('testtable_id_seq':: regclass), md5(generate_series (1,1000000):: text)» Настроим резервный сервер.Создадим директорию для кластера: sudo mkdir -p /data/db sudo chmod 0700 /data/db sudo chown postgres: postgres /data/db При помощи утилиты pg_basebackup сделаем резервную копию основного сервера: sudo -u postgres /usr/local/pgsql/bin/pg_basebackup -h 192.168.1.103 -U replica -D /data/db -X s pg_basebackup копирует всё содержимое кластера, включая конфиги, поэтому изменяем параметр hot_standby в состояние on в postgresql.confСоздадим файл recovery.conf в директории кластера, в котором укажем параметры подключения к основному серверу: standby_mode='on' primary_conninfo='host=192.168.1.103 port=5432 user=replica password=123' Запускаем кластер на резервном сервере: sudo -u postgres /usr/local/pgsql/bin/pg_ctl -D /data/db start После этого на основном сервере должен запуститься процесс wal_sender, а на резервном wal_receiver: Скрытый текст postgres 21295 0.0 0.0 23700 604? Ss 13:39 0:00 postgres: logger process postgres 21297 0.0 0.2 170756 2312? Ss 13:39 0:00 postgres: checkpointer process postgres 21298 0.0 0.2 170784 2252? Ss 13:39 0:00 postgres: writer process postgres 21299 0.0 0.5 170648 5148? Ss 13:39 0:00 postgres: wal writer process postgres 21300 0.0 0.1 171052 1804? Ss 13:39 0:00 postgres: autovacuum launcher process postgres 21301 0.0 0.1 25924 1060? Ss 13:39 0:00 postgres: stats collector process postgres 21323 0.0 0.2 171048 2108? Ss 13:46 0:00 postgres: wal sender process replica 192.168.1.108(56673) streaming 0/4E000210

postgres 15150 0.0 0.0 23700 612? Ss 13:46 0:00 postgres: logger process postgres 15151 0.0 0.1 170788 1496? Ss 13:46 0:00 postgres: startup process recovering 00000001000000000000004E postgres 15152 0.0 0.0 170680 944? Ss 13:46 0:00 postgres: checkpointer process postgres 15153 0.0 0.1 170680 1204? Ss 13:46 0:00 postgres: writer process postgres 15154 0.0 0.0 25792 648? Ss 13:46 0:00 postgres: stats collector process postgres 15155 0.6 0.1 174956 1660? Ss 13:46 0:00 postgres: wal receiver process streaming 0/4E000138 Посмотреть состояние репликации можно через представление pg_stat_replication на основном сервере testdb=# select * from pg_stat_replication; -[ RECORD 1 ]----±----------------------------- pid | 21987 usesysid | 16384 usename | replica application_name | walreceiver client_addr | 192.168.1.108 client_hostname | client_port | 56674 backend_start | 2014–11–25 18:30:09.206434+03 backend_xmin | state | streaming sent_location | 0/5A2D8E60 write_location | 0/5A2D8E60 flush_location | 0/5A2D8E60 replay_location | 0/5A2D8E60 sync_priority | 0 sync_state | async Видно, что основной и резервный сервер синхронизированны. Теперь сгенерим еще немного тестовых данных и сразу после этого посмотрим состояние репликации. testdb=# insert into testtable select nextval ('testtable_id_seq':: regclass), md5(generate_series (1,1000000):: text); INSERT 0 1000000 testdb=# select * from pg_stat_replication; -[ RECORD 1 ]----±----------------------------- pid | 21987 usesysid | 16384 usename | replica application_name | walreceiver client_addr | 192.168.1.108 client_hostname | client_port | 56674 backend_start | 2014–11–25 18:30:09.206434+03 backend_xmin | state | streaming sent_location | 0/63800000 write_location | 0/63680000 flush_location | 0/63680000 replay_location | 0/6136E160 sync_priority | 0 sync_state | async Здесь мы наблюдаем, что резервный сервер забрал с основного все WAL-логи, но еще не успел их все применить, поэтому он отстает от основного. По умолчанию, в postgres репликация происходит асинхронно при помощи WAL-логов, это бинарные файлы фиксированного размера в 16Мб, которые находятся в директории pg_xlog. Их количество можно менять при помощи параметров checkpoint_segments и wal_keep_segments. Когда количество измененных данных в кластере превышает общий размер WAL-логов, запускается процесс checkpointer, который сбрасывает WAL-логи в дата-файлы. После этого WAL-логи пересоздаются заново. В текущей стабильной версии postgres основной сервер не учитывает состояние резервного сервера. Поэтому если резервный слишком «отстал» от основного, то на основном WAL-логи пересоздадутся до того, как их заберет резервный. Попробуем смоделировать эту ситуацию.Временно запретим резервному серверу подключаться на порт 5432: sudo iptables -A OUTPUT -m tcp -p tcp —dport 5432 -j DROP Сгенерим еще данных на основном сервере: testdb=# insert into testtable select nextval ('testtable_id_seq':: regclass), md5(generate_series (1,1000000):: text); INSERT 0 1000000 Сбросим правило iptables и посмотрим логи резервного сервера, в которых наблюдаем такую неприятную картину. LOG: started streaming WAL from primary at 0/78000000 on timeline 1 FATAL: could not receive data from WAL stream: ERROR: requested WAL segment 000000010000000000000078 has already been removed Основной сервер удалил WAL-логи до того, как их успел забрать резервный. Теперь придется заново делать резервную копию основного сервера. Проблема в том, что основной сервер не учитывает состояние резервного. Поэтому если есть проблемы с сетью или просто медленный канал между серверами, то при интенсивной загрузке и/или изменении данных на основном сервере, возникает риск поломки репликации. Частичным решением является увеличение значения параметра wal_keep_segments, и включение WAL archiving. Но в версии 9.4 появятся replication slots. Рассмотрим как это работает: На основном сервере создадим репликационный слот: testdb=# SELECT pg_create_physical_replication_slot ('standby_slot'); -[ RECORD 1 ]-----------------------±--------------- pg_create_physical_replication_slot | (standby_slot,) testdb=# select * from pg_replication_slots; -[ RECORD 1 ]±------------ slot_name | standby_slot plugin | slot_type | physical datoid | database | active | f xmin | catalog_xmin | restart_lsn | На резервном добавим к существующему содержимому файла recovery.conf строкуprimary_slot_name = 'standby_slot’После перезапуска резервного сервера снова отключим его от основного и сгенерим на основном тестовые данных, которые превышают объем WAL-логов:

testdb=# insert into testtable select nextval ('testtable_id_seq':: regclass), md5(generate_series (1,10000000):: text); Посмотрим параметры WAL-логов в системе, а потом реальное количество журнальных файлов в каталоге pg_xlog: testdb=# show checkpoint_segments; -[ RECORD 1 ]-------±- checkpoint_segments | 3 testdb=# show wal_keep_segments; -[ RECORD 1 ]-----±- wal_keep_segments | 0 testdb=#\! ls /data/db/pg_xlog | wc -l 50 Для расчета максимального количества WAL-файлов в системе используют формулу: (2 + checkpoint_completion_target) * checkpoint_segments + 1.Однако текущее количество WAL-логов в системе значительно выше. Репликационные слоты сохраняют информацию о количестве скаченных WAL-логов каждым резервным сервером. Теперь WAL-логи будут копиться до тех пор, пока последний резервный сервер не заберет их или пока не будет удален репликационный слот. По мере скачивания WAL-логов, директория pg_xlog на основном сервере будет уменьшаться. Сбросив правило iptables на резервном сервере, в логах видим, что репликация возобновилась. testdb=#\! tail -f /data/db/pg_log/postgresql-2014–11–27_191036.log Is the server running on host »192.168.1.103» and accepting TCP/IP connections on port 5432? LOG: started streaming WAL from primary at 0/A0000000 on timeline 1 Replication slots это отличный инструмент, повышающий надежность и удобство репликации в PostgreSQL.Описание replication slots на официальном сайте PostgreSQL: www.postgresql.org/docs/9.4/static/warm-standby.html#STREAMING-REPLICATION-SLOTS

© Habrahabr.ru