PostgreSQL BDR (Bi-Directional Replication)とは
PostgreSQL9.0 から Streaming Replication が実装されています。マスタからDBの更新情報(WAL) を複数のスレーブに対して送信・適用する事で、マスタとスレーブのデータ同期を行います。
マスタは参照/更新が可能ですが、スレーブでは参照のみで更新を行う事はできません。
また、マスタ側が障害となった場合SPOFとなる為、PGPOOL等の外部ツールにより可用性を向上させる必要があります。
9.4 からは logical replication が追加されました。BDRはこの logical replication を使うことで、1マスタNスレーブ構成だった Streaming Replication を Nマスタに拡張することができる外部ツールです。
BDR のマスタはそれぞれ、参照/更新が可能です。
環境
・Cent OS 6.7(2台)
・PostgreSQL9.4
インストール
マニュアルを参考にインストールします。BDR 0.10.0 Documentation
# chkconfig --level 2345 iptables off
# service iptables stop
# groupadd -g 301 postgres
# useradd -d /home/postgres -m -u 301 -g postgres postgres
# id postgres
uid=301(postgres) gid=301(postgres) groups=301(postgres)
# cd /usr/local/src
# wget http://yum.postgresql.org/9.4/redhat/rhel-6-x86_64/pgdg-centos94-9.4-2.noarch.rpm
# rpm -ihv pgdg-centos94-9.4-2.noarch.rpm
# yum check-update
# yum groupinstall "Development Tools"
# yum install yum-utils openjade docbook-dtds docbook-style-dsssl docbook-style-xsl
# yum-builddep postgresql94
# git clone -b bdr-pg/REL9_4_STABLE git://git.postgresql.org/git/2ndquadrant_bdr.git postgresql-bdr
# mkdir /usr/local/pgsql
# chown -R postgres:postgres /usr/local/pgsql
# chown -R postgres:postgres /usr/local/src/postgresql-bdr
# su - postgres
$ cd /usr/local/src/postgresql-bdr
$ ./configure --prefix=/usr/local/pgsql --enable-debug --with-openssl
$ make -j4 -s install-world
$ exit
#
# id
uid=0(root) gid=0(root) groups=0(root)
# cd /usr/local/src
# git clone -b bdr-plugin/REL0_9_STABLE git://git.postgresql.org/git/2ndquadrant_bdr.git bdr-plugin
# chown -R postgres:postgres /usr/local/src/bdr-plugin
# su - postgres
$ cd /usr/local/src/bdr-plugin
$ PATH=/usr/local/pgsql/bin:"$PATH" ./configure
$ make -j4 -s all
$ make -s install
$ ll /usr/local/pgsql/bin/ | grep bdr
-rwxr-xr-x 1 postgres postgres 928885 Apr 18 06:29 bdr_dump
-rwxr-xr-x 1 postgres postgres 169288 Apr 18 06:29 bdr_init_copy
-rwxr-xr-x 1 postgres postgres 2399 Apr 18 06:29 bdr_initial_load
-rwxr-xr-x 1 postgres postgres 89440 Apr 18 06:29 bdr_resetxlog
⇒上記の4ファイルが存在する事を確認
PostgreSQL設定
$ PATH=/usr/local/pgsql/bin:"$PATH"
$ mkdir /usr/local/pgsql/bdr
$ initdb -D /usr/local/pgsql/bdr/node1 -A trust -U postgres
$ PATH=/usr/local/pgsql/bin:"$PATH"
$ mkdir /usr/local/pgsql/bdr
$ initdb -D /usr/local/pgsql/bdr/node2 -A trust -U postgres
$ cd /usr/local/pgsql/bdr
$ cat >> ./node1/postgresql.conf <<EOF
listen_addresses = '*'
shared_preload_libraries = 'bdr'
wal_level = 'logical'
track_commit_timestamp = on
max_connections = 100
max_wal_senders = 10
max_replication_slots = 10
max_worker_processes = 10
log_error_verbosity = verbose
log_min_messages = debug1
log_line_prefix = 'd=%d p=%p a=%a%q '
bdr.default_apply_delay=2000 # milliseconds
bdr.log_conflicts_to_table=on
EOF
$ cat >> ./node1/pg_hba.conf <<EOF
host all all XX.XX.0.0/16 trust
local replication postgres trust
host replication postgres XX.XX.0.0/16 trust
host replication postgres ::1/128 trust
EOF
※XX.XX.XX.XX…node1とnode2が通信可能となるように設定
$ cd /usr/local/pgsql/bdr
$ cat >> ./node2/postgresql.conf <<EOF
listen_addresses = '*'
shared_preload_libraries = 'bdr'
wal_level = 'logical'
track_commit_timestamp = on
max_connections = 100
max_wal_senders = 10
max_replication_slots = 10
max_worker_processes = 10
log_error_verbosity = verbose
log_min_messages = debug1
log_line_prefix = 'd=%d p=%p a=%a%q '
bdr.default_apply_delay=2000 # milliseconds
bdr.log_conflicts_to_table=on
EOF
$ cat >> ./node2/pg_hba.conf <<EOF
host all all XX.XX.0.0/16 trust
local replication postgres trust
host replication postgres XX.XX.0.0/16 trust
host replication postgres ::1/128 trust
EOF
※XX.XX.XX.XX…node1とnode2が通信可能となるように設定
$ pg_ctl -l /usr/local/pgsql/bdr/node1/postgres.log -D /usr/local/pgsql/bdr/node1 -w start
$ createdb -U postgres bdrdemo
$ pg_ctl -l /usr/local/pgsql/bdr/node2/postgres.log -D /usr/local/pgsql/bdr/node2 -w start
$ createdb -U postgres bdrdemo
$ psql -U postgres bdrdemo
CREATE EXTENSION btree_gist;
CREATE EXTENSION bdr;
SELECT bdr.bdr_group_create(local_node_name := 'node1',node_external_dsn := 'host=node1_ip port=5432 dbname=bdrdemo');
SELECT bdr.bdr_node_join_wait_for_ready();
\q
※node1_ipのIP
$ psql -U postgres bdrdemo
CREATE EXTENSION btree_gist;
CREATE EXTENSION bdr;
SELECT bdr.bdr_group_join(local_node_name := 'node2',node_external_dsn := 'host=node2_ip port=5432 dbname=bdrdemo', join_using_dsn := 'host=node1_ip port=5432 dbname=bdrdemo');
SELECT bdr.bdr_node_join_wait_for_ready();
\q
※node1_ipのIP
※node2_ipのIP
$ psql -U postgres bdrdemo
select * from bdr.bdr_nodes;
node_sysid |node_timeline|node_dboid|node_status|node_name| node_local_dsn | node_init_from_dsn
-------------+-------------+----------+-----------+---------+--------------------------------------+-------------------------------------------
XXXXXXXXXXXXX| 1| 16385|r |node1 |host=node1_ip port=5432 dbname=bdrdemo|
XXXXXXXXXXXXX| 1| 16385|r |node2 |host=node2_ip port=5432 dbname=bdrdemo|host=node1_ip port=5432 dbname=bdrdemo
\q
※node1_ipのIP
※node2_ipのIP
PostgreSQL BDR動作確認
$ psql -U postgres bdrdemo
CREATE TABLE bdr_test (c1 INT, c2 text, PRIMARY KEY (c1));
INSERT INTO bdr_test VALUES (1, 'test1');
INSERT INTO bdr_test VALUES (2, 'test2');
SELECT * FROM bdr_test;
c1 | c2
----+-------
1 | test1
2 | test2
(2 row)
$ psql -U postgres bdrdemo
SELECT * FROM bdr_test;
c1 | c2
----+-------
1 | test1
2 | test2
(2 row)
$ psql -U postgres bdrdemo
DELETE FROM bdr_test WHERE c1 = 2;
SELECT * FROM bdr_test;
c1 | c2
----+-------
1 | test1
(1 row)
$ psql -U postgres bdrdemo
SELECT * FROM bdr_test;
c1 | c2
----+-------
1 | test1
(1 row)
更新の重複
・マルチマスタにすると、PrimaryKeyとなっている一意の項目に対し、node1とnode2で同時に更新しようとした場合に競合が発生します。
それを回避する為、BDRは複数のnodeで重複しない値を生成するGlobal Sequencesを提供しています。Global Sequencesを使うと各nodeで1000個ずつの値を割り振り、node1は1~1000、node2は1001~2000の値が使われるようになります。
最後に
・あっさりとPostgreSQLのマルチマスタが組めました。今までは、可用性を高める方法として、streaming replicationかPGPOOL等でやってましたが、PostgreSQL BDRも十分に使えると思いました。