Настройка репликации в PostgreSQL с помощью системы Slony-I.

eugenek, JID:gnudist@jabber.ru, 2005

Последнюю версию этого документа можно попробовать получить по адресу: http://www.kuzin.net/work/sloniki-privet.html

Этот документ описывает настройку репликации базы данных PostgreSQL с главного сервера на один ведомый сервер. Будет рассмотрено также добавление ещё одного ведомого узла в имеющуюся систему репликации.

Рекомендуется прочитать документацию на Slony, которая располагается по адресу: http://linuxfinances.info/info/slony.html, README из дистрибутива, также полный список команд консоли slonik находится в файле slonik-commands.html, в поставке.

Home page проекта: http://www.slony.org

Далее подразумевается что читатель знаком с терминами "кластер", "узел" (node), "набор" (replication set). Если нет, некоторые моменты могут быть не ясны. Не помешает также некоторое знание SQL ,)

0. Установка

Вопрос установки системы Slony не рассматривается в данном документе. В документации, поставляемой с системой рассмотрена базовая установка.

У меня используется Slony на FreeBSD с PostgreSQL 7.4.7. Slony, как и СУБД, установлены из портов.

1. Введение

Slony это система репликации реального времени, позволяющая организовать синхронизацию нескольких серверов PostgreSQL по сети. Slony использует триггеры Postgre для привязки к событиям INSERT/DELETE/UPDATE и хранимые процедуры для выполнения действий.

Система Slony с точки зрения администратора состоит из двух главных компонент, репликационного демона slony и административной консоли slonik. Администрирование системы сводится к общению со slonik-ом, демон slon только следит за собственно процессом репликации. А админ следит за тем, чтобы slon висел там, где ему положено.

1.1. О slonik-e

Все команды slonik принимает на свой stdin. До начала выполнения скрипт slonik-a проверяется на соответствие синтаксису, если обнаруживаются ошибки, скрипт не выполняется, так что можно не волноваться если slonik сообщает о syntax error, ничего страшного не произошло. И он ещё ничего не сделал. Скорее всего.

2. Установка Slony

Рассмотрим теперь установку на гипотетическую базу данных customers (названия узлов, кластеров и таблиц являются вымышленными).

Наши данные

БД: customers
master_host: customers_master.com
slave_host_1: customers_slave.com
cluster name (нужно придумать): customers_rep

2.0 Подготовка master-сервера

Важно: не рекомендую использовать идущий в комплекте скрипт slony_setup.pl на момент написания этого документа (версия Slony 1.0.5) этот скрипт не делал ряд важных проверок при генерации репликационных скриптов, из-за чего могли возникать труднообъяснимые и трудноуловимые глюки.

Для начала нам нужно создать пользователя Postgres, под которым будет действовать Slony. По умолчанию, и отдавая должное системе, этого пользователя обычно называют slony.

pgsql@customers_master$ createuser -a -d slony
pgsql@customers_master$ psql -d template1 -c "alter user slony with \
password 'slony_user_password';"

Также на каждом из узлов лучше завести системного пользователя slony, чтобы запускать от его имени репликационного демона slon. В дальнейшем подразумевается, что он (и пользователь и slon) есть на каждом из узлов кластера.

2.1 Подготовка одного slave-сервера

Здесь я рассматриваю, что серверы кластера соединены посредством сети Internet (как в моём случае), необходимо чтобы с каждого из ведомых серверов можно было установить соединение с PostgreSQL на мастер-хосте, и наоборот. То есть, команда:

anyuser@customers_slave$ psql -d customers -h customers_master.com -U slony

должна подключать нас к мастер-серверу (после ввода пароля, желательно). Если что-то не так, возможно требуется поковыряться в настройках firewall-a, или файле pg_hba.conf, который лежит в $PGDATA.

Теперь устанавливаем на slave-хост сервер PostgreSQL. Следующего обычно не требуется, сразу после установки Postgres "up and ready", но в случае каких-то ошибок можно начать "с чистого листа", выполнив следующие команды (предварительно сохранив конфигурационные файлы и остановив postmaster):

pgsql@customers_slave$ rm -rf $PGDATA
pgsql@customers_slave$ mkdir $PGDATA
pgsql@customers_slave$ initdb -E UTF8 -D $PGDATA
pgsql@customers_slave$ createuser -a -d slony
pgsql@customers_slave$ psql -d template1 -c "alter user slony with password \
'slony_user_password';"

Запускаем postmaster.

Внимание! Обычно требуется определённый владелец для реплицируемой БД. В этом случае необходимо завести его тоже!

pgsql@customers_slave$ createuser -a -d customers_owner
pgsql@customers_slave$ psql -d template1 -c "alter user customers_owner with password \
'customers_owner_password';"

Эти две команды можно запускать с customers_master, к командной строке в этом случае нужно добавить "-h customers_slave", чтобы все операции выполнялись на slave.

На slave, как и на master, также нужно установить Slony.

2.2 Инициализация БД и plpgsql на slave

Следующие команды выполняются от пользователя slony. Скорее всего для выполнения каждой из них потребуется ввести пароль (slony_user_password). Итак:

slony@customers_master$ createdb -O customers_owner -h customers_slave.com customers
slony@customers_master$ createlang -d customers -h customers_slave.com plpgsql

Внимание! Все таблицы, которые будут добавлены в replication set должны иметь primary key. Если какая-то из таблиц не удовлетворяет этому условию, задержитесь на этом шаге и дайте каждой таблице primary key командой ALTER TABLE ADD PRIMARY KEY.

Если столбца который мог бы стать primary key не находится, добавьте новый столбец типа serial (ALTER TABLE ADD COLUMN), и заполните его значениями. Настоятельно НЕ рекомендую использовать "table add key" slonik-a.

Продолжаем.
Создаём таблицы и всё остальное на slave:

slony@customers_master$ pg_dump -s customers | psql -U slony -h customers_slave.com customers

pg_dump -s сдампит только структуру нашей БД.

pg_dump -s customers должен пускать без пароля, а вот для psql -U slony -h customers_slave.com customers придётся набрать пароль (slony_user_pass). Важно: я подразумеваю что сейчас на мастер-хосте ещё не установлен Slony (речь не про make install), то есть в БД нет таблиц sl_*, триггеров и прочего. Если есть, то возможно два варианта:

2.3 Инициализация кластера

Если Сейчас мы имеем два сервера PgSQL которые свободно "видят" друг друга по сети, на одном из них находится мастер-база с данными, на другом - только структура.

На мастер-хосте запускаем такой скрипт:

#!/bin/sh

CLUSTER=customers_rep

DBNAME1=customers
DBNAME2=customers

HOST1=customers_master.com
HOST2=customers_slave.com

SLONY_USER=slony

slonik <<EOF
cluster name = $CLUSTER;
node 1 admin conninfo = 'dbname=$DBNAME1 host=$HOST1 port=5432 user=slony password=slony_user_password';
node 2 admin conninfo = 'dbname=$DBNAME2 host=$HOST2 port=5432 user=slony password=slony_user_password';
init cluster ( id = 1, comment = 'Customers DB replication cluster' );

echo 'Create set';

create set ( id = 1, origin = 1, comment = 'Customers DB replication set' );

echo 'Adding tables to the subscription set';

echo ' Adding table public.customers_sales...';
set add table ( set id = 1, origin = 1, id = 4, full qualified name = 'public.customers_sales', comment = 'Table public.customers_sales' );
echo ' done';

echo ' Adding table public.customers_something...';
set add table ( set id = 1, origin = 1, id = 5, full qualified name = 'public.customers_something, comment = 'Table public.customers_something );
echo ' done';

echo 'done adding';
store node ( id = 2, comment = 'Node 2, $HOST2' );
echo 'stored node';
store path ( server = 1, client = 2, conninfo = 'dbname=$DBNAME1 host=$HOST1 port=5432 user=slony password=slony_user_password' );
echo 'stored path';
store path ( server = 2, client = 1, conninfo = 'dbname=$DBNAME2 host=$HOST2 port=5432 user=slony password=slony_user_password' );

store listen ( origin = 1, provider = 1, receiver = 2 );
store listen ( origin = 2, provider = 2, receiver = 1 );
EOF

Здесь мы инициализируем кластер, создаём репликационный набор, включаем в него две таблицы. And something else. Важно: нужно перечислить все таблицы, которые нужно реплицировать, id таблицы в наборе должен быть уникальным, таблицы должны иметь primary key.

Важно: replication set запоминается раз и навсегда. Чтобы добавить узел в схему репликации не нужно заново инициализировать set.

Важно: если в набор добавляется или удаляется таблица нужно переподписать все узлы. То есть сделать unsubscribe и subscribe заново (см slonik-commands.html).

2.4 Подписываем slave-узел на replication set

Скрипт:

#!/bin/sh

CLUSTER=customers_rep

DBNAME1=customers
DBNAME2=customers

HOST1=customers_master.com
HOST2=customers_slave.com

SLONY_USER=slony

slonik <<EOF
cluster name = $CLUSTER;
node 1 admin conninfo = 'dbname=$DBNAME1 host=$HOST1 port=5432 user=slony password=slony_user_password';
node 2 admin conninfo = 'dbname=$DBNAME2 host=$HOST2 port=5432 user=slony password=slony_user_password';

echo'subscribing';
subscribe set ( id = 1, provider = 1, receiver = 2, forward = no);

EOF

2.5 Старт репликации

Теперь, на обоих узлах необходимо запустить демона репликации.

slony@customers_master$ slon customers_rep "dbname=customers user=slony"

и

slony@customers_slave$ slon customers_rep "dbname=customers user=slony"

Сейчас слоны обменяются сообщениями и начнут передачу данных. Начальное наполнение происходит с помощью COPY, slave DB на это время полностью блокируется.

В среднем время актуализации данных на slave-системе составляет до 10-ти секунд. slon успешно обходит проблемы со связью и подключением к БД, и вообще требует к себе достаточно мало внимания.

5. Добавление ещё одного узла в работающую схему репликации

по пунктам:
     выполнить 2.1
     выполнить 2.2

Новый узел имеет id = 3. Находится на хосте customers_slave3.com, "видит" мастер-сервер по сети и мастер может подключиться к его PgSQL.

после дублирования структуры (п 2.2) делаем следующее:

slonik <<EOF
cluster name = customers_slave;
node 3 admin conninfo = 'dbname=customers host=customers_slave3.com port=5432 user=slony password=slony_user_pass';
uninstall node (id = 3);
echo 'okay';
EOF

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

Инициализировать кластер не надо. Вместо этого записываем информацию о новом узле в сети:

#!/bin/sh

CLUSTER=customers_rep

DBNAME1=customers
DBNAME3=customers

HOST1=customers_master.com
HOST3=customers_slave3.com

SLONY_USER=slony

slonik <<EOF
cluster name = $CLUSTER;
node 1 admin conninfo = 'dbname=$DBNAME1 host=$HOST1 port=5432 user=slony password=slony_user_pass';
node 3 admin conninfo = 'dbname=$DBNAME3 host=$HOST3 port=5432 user=slony password=slony_user_pass';

echo 'done adding';

store node ( id = 3, comment = 'Node 3, $HOST3' );
echo 'sored node';
store path ( server = 1, client = 3, conninfo = 'dbname=$DBNAME1 host=$HOST1 port=5432 user=slony password=slony_user_pass' );
echo 'stored path';
store path ( server = 3, client = 1, conninfo = 'dbname=$DBNAME3 host=$HOST3 port=5432 user=slony password=slony_user_pass' );

echo 'again';
store listen ( origin = 1, provider = 1, receiver = 3 );
store listen ( origin = 3, provider = 3, receiver = 1 );

EOF

Новый узел имеет id 3, потому что 2 уже есть и работает. Подписываем новый узел 3 на replication set:

#!/bin/sh

CLUSTER=customers_rep

DBNAME1=customers
DBNAME3=customers

HOST1=customers_master.com
HOST3=customers_slave3.com

SLONY_USER=slony

slonik <<EOF
cluster name = $CLUSTER;
node 1 admin conninfo = 'dbname=$DBNAME1 host=$HOST1 port=5432 user=slony password=slony_user_pass';
node 3 admin conninfo = 'dbname=$DBNAME3 host=$HOST3 port=5432 user=slony password=slony_user_pass';

echo'subscribing';
subscribe set ( id = 1, provider = 1, receiver = 3, forward = no);

EOF

Теперь запускаем slon на новом узле, так же как и на остальных. Перезапускать slon на мастере не надо.

slony@customers_slave3$ slon customers_rep "dbname=customers user=slony"

Репликация должна начаться как обычно.


Last modified: Wed Mar 30 19:51:35 MSD 2005

Valid HTML 4.01!