Pull to refresh

Replication slots в PostgreSQL

Reading time 7 min
Views 46K
До девятой версии в 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
Tags:
Hubs:
+20
Comments 13
Comments Comments 13

Articles