До девятой версии в 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
Автор: zhigaev