Slony-I 管理

The Slony Global Development Group

Christopher Browne


1. あなたの最初のデータベースの複製

ここでの例はまっさらな pbbench データベースをレプリケートします。既存のデータベースを複製する機構はここで説明されますが、手つかずの実稼働用ではないデータベースを使用して Slony-I 機能の学習をお勧めします。

Slony-I は同じ postmaster 下で稼働しているデータベース(もしくはその一部)をレプリケートするトリガーを基調としたレプリケーションエンジンです。

この例は localhost(マスター)で動いている pgbench データベースを同じく localhost(スレーブ)で動いている pgbench スレーブデータベースにどのようにレプリケートするかを示すものです。PostgreSQL の構成に関して2つの前提があります。

(訳者注:PostgreSQL バージョン 8.0 から tcpip_socket=true ではなく listen_addresses = 'localhost' のように IP インターフェイスを指定するようになりました。)

REPLICATIONUSERPostgreSQL のスーパユーザである必要があります。通常これは postgres もしくは pgsql ですが、役割りを分離するために複雑な環境では slony ユーザを定義することは悪い考えではありません。

以下のシェル変数を設定します。

通常のシェルでの変数設定の例を示します。

警告

MASTERHOST および SLAVEHOST に対し異なるホストを使用するためにこれらの変数を変更する場合、どちらに対しても localhost を使用しないことを確実にしてください。もしそうであると、以下と似たようなエラーとなります。

ERROR remoteListenThread_1: db_getLocalNodeId() returned 2 - wrong database?

1.1. pgbenchuser の作成

createuser -A -D $PGBENCHUSER

1.2. データベースの用意

createdb -O $PGBENCHUSER -h $MASTERHOST $MASTERDBNAME
createdb -O $PGBENCHUSER -h $SLAVEHOST $SLAVEDBNAME
pgbench -i -s 1 -U $PGBENCHUSER -h $MASTERHOST $MASTERDBNAME

Slony-I は手続き言語 pl/pgSQL がデータベースにインストールされていることに依存しますので、いまインストールしておく方がよいと思います。template1 データベースに pl/pgSQL がインストールされている可能性があります。この場合 $MASTERDBNAME に既にインストールされていますのでこの手順は不要です。

createlang -h $MASTERHOST plpgsql $MASTERDBNAME

Slony-I はスレーブがサブスクライブする時にマスターからテーブル定義をコピーないので、このデータをインポートする必要があります。pg_dump で行います。

pg_dump -s -U $REPLICATIONUSER -h $MASTERHOST $MASTERDBNAME | psql -U $REPLICATIONUSER -h $SLAVEHOST $SLAVEDBNAME

どのようにして Slony-I が稼働中にレプリケーションのサブスクリプションをさせるのかを実例として説明するため pgbench を立ち上げましょう。pgbench アプリケーションを別の端末のウィンドウでフォアグラウンドで動かしているのであれば、いつでも異なるパラメータを与えて停止、再開始できます。このセッションを有効にするために変数を再エクスポートする必要もあります。

pgbench を走らせるには通常以下のようなコマンドを入力します。

pgbench -s 1 -c 5 -t 1000 -U $PGBENCHUSER -h $MASTERHOST $MASTERDBNAME

この指定は pgbench を pgbench ユーザで localhost 上で pgbench データベースに対し、5つの同時ユーザがそれぞれ1000トランザクションを処理するように走らせます。

1.3. レプリケーション用にデータベースを構成

構成テーブルの作成、ストアドプロシージャ、トリガ、および構成は全て slonik ツールで行います。これは大概ストアドプロシージャを呼び出すための特殊なスクリプト用の補助ツールです。

#!/bin/sh

slonik <<_EOF_
        #--
        # 今回の例である slony_example でレプリケーションシステムが使用する
        # 名前空間の定義
        #--       
	cluster name = $CLUSTERNAME;

        #--
        # クラスタのそれぞれの側のそれぞれのノードにノード1が接続するために slonik
        # によって admin conninfo が使用されます。C-API による PQconnectdb の構文です。
        #--
	node 1 admin conninfo = 'dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER';
	node 2 admin conninfo = 'dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER';

        #--
        # 最初のノードを初期化します。1でなければなりません。これは全ての
        # レプリケーションシステムに特有なデータベースオブジェクトを含む
        # スキーマ _$CLUSTERNAME を作成します。
	#--
	init cluster ( id=1, comment = 'Master Node');
 
        #--
        # 行を特定するのに使用される主キーもしくは他の一意制約が履歴テーブルに存在
        # しないので、1つ追加する必要があります。次のコマンドでテーブルに
        # _Slony-I_$CLUSTERNAME_rowI と言う名前の bigint 列を追加します。そこには
        # デフォルト値として nextval('_$CLUSTERNAME.s1_rowid_seq') が挿入され、
        # UNIQUE および NOT NULL 制約が適用されます。全ての既存の行はこの番号で
        # 初期化されます。
        #--
	table add key (node id = 1, fully qualified name = 'public.history');

        #--
        # Slony-I はテーブルをセットに編成します。ノードがサブスライブできる最小単位
        # はセットです。以下のコマンドで全ての4つの pgbench テーブルを含む1つの
        # セットが作成されます。
        #--
	create set (id=1, origin=1, comment='All pgbench tables');
	set add table (set id=1, origin=1, id=1, fully qualified name = 'public.accounts', comment='accounts table');
	set add table (set id=1, origin=1, id=2, fully qualified name = 'public.branches', comment='branches table');
	set add table (set id=1, origin=1, id=3, fully qualified name = 'public.tellers', comment='tellers table');
	set add table (set id=1, origin=1, id=4, fully qualified name = 'public.history', comment='history table', key = serial);

        #--
        # 二番目のノード(スレーブ)を作成し、2つのノードがどのようにして接続し、また
        # どのように事象を監視するかを指示します。
        #--

	store node (id=2, comment = 'Slave node');
	store path (server = 1, client = 2, conninfo='dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER');
	store path (server = 2, client = 1, conninfo='dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER');
	store listen (origin=1, provider = 1, receiver =2);
	store listen (origin=2, provider = 2, receiver =1);
_EOF_

pgbench はまだ動いていますか?もし停止していれば再起動してください。

この時点で2つのデータベースが完全に用意されています。1つは pgbench が忙しくアクセスして行の変更を行っているマスターデータベースです。それではレプリケーションデーモンを起動させましょう。

$MASTERHOST 上で以下のコマンドを入力します。

slon $CLUSTERNAME "dbname=$MASTERDBNAME user=$REPLICATIONUSER host=$MASTERHOST"

同様にノード2のレプリケーションシステムを起動します。

slon $CLUSTERNAME "dbname=$SLAVEDBNAME user=$REPLICATIONUSER host=$SLAVEHOST"

マスターとスレーブ双方で稼働している slon が存在し、共に診断その他のデータを吐き出しているとしても、まだどんなデータもレプリケートしていません。見えている警告は2つの slon プロセス間でクラスタ構成の同期を取っていることです。

マスター(ノード id 1)から4つの pgbench テーブル(セット 1)のレプリケーションを開始するには、スレーブ(ノード id 2)で以下のスクリプトを実行します。

#!/bin/sh
slonik <<_EOF_
	 # ----
	 # レプリケーションシステムがどの名前空間を使用するか定義します。
	 # ----
	 cluster name = $CLUSTERNAME;

	 # ----
         # admin conninfo は slonik プログラムがノードデータベースに接続するのに
         # 使用されます。ですから(slonik が実行される)管理者ワークステーション
         # から接続する PQconnectdb 引数が付いています。
	 # ---- 
	 node 1 admin conninfo = 'dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER';
	 node 2 admin conninfo = 'dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER';

	 # ----
	 # ノード2がセット1をサブスクライブ
	 # ----
	 subscribe set ( id = 1, provider = 1, receiver = 2, forward = no);
_EOF_

数秒もたたずに $SLAVEHOST 上のレプリケーションデーモが4つのレプリケーションテーブルの現在の内容のコピーを開始します。もちろん、その間でも pgbench アプリケーションはデータベースを変更し続けます。コピー操作が終了すると $SLAVEHOST 上のレプリケーションデーモンは蓄積されたレプリケーションログを適用して捕捉を開始します。1回あたりアプリケーションが10秒ほど動く小さな手順です。係わっている2つのシステムの性能にも依存しますが、実際のトランザクション負荷といかに良く2つのデータベースを調整・整備するかの2つのデータベースを関連付けるこの捕捉手順は数分、数時間もしくは未来永劫かかります。

ここで最初の基本のマスター/スレーブレプリケーションシステムが首尾良く設定され、スレーブが捕捉すると2つのデータベースには同じデータが含まれているはずです。これが最低限の理論です。演習としてデータセットが実際に同一かどうかを検証し確信を持つと良いでしょう。

以下のスクリプトは2つのデータベースの順序づけられたダンプを作成し、そして比較します。pgbench がテストを完了し、slon セッションは捕捉されていることを確認してください。

#!/bin/sh
echo -n "**** comparing sample1 ... "
psql -U $REPLICATIONUSER -h $MASTERHOST $MASTERDBNAME >dump.tmp.1.$$ <<_EOF_
	 select 'accounts:'::text, aid, bid, abalance, filler
		  from accounts order by aid;
	 select 'branches:'::text, bid, bbalance, filler
		  from branches order by bid;
	 select 'tellers:'::text, tid, bid, tbalance, filler
 	          from tellers order by tid;
	 select 'history:'::text, tid, bid, aid, delta, mtime, filler,
		  "_Slony-I_${CLUSTERNAME}_rowID"
		  from history order by "_Slony-I_${CLUSTERNAME}_rowID";
_EOF_
psql -U $REPLICATIONUSER -h $SLAVEHOST $SLAVEDBNAME >dump.tmp.2.$$ <<_EOF_
	 select 'accounts:'::text, aid, bid, abalance, filler
		  from accounts order by aid;
	 select 'branches:'::text, bid, bbalance, filler
		  from branches order by bid;
	 select 'tellers:'::text, tid, bid, tbalance, filler
		  from tellers order by tid;
	 select 'history:'::text, tid, bid, aid, delta, mtime, filler,
		  "_Slony-I_${CLUSTERNAME}_rowID"
		  from history order by "_Slony-I_${CLUSTERNAME}_rowID";
_EOF_

if diff dump.tmp.1.$$ dump.tmp.2.$$ >$CLUSTERNAME.diff ; then
	 echo "success - databases are equal."
	 rm dump.tmp.?.$$
	 rm $CLUSTERNAME.diff
else
	 echo "FAILED - see $CLUSTERNAME.diff for database differences"
fi

本操作に関して多少洗練されたドキュメントが Slony-I ソースコードツリーの中に slony-I-basic-mstr-slv.txt というファイル名であります。

このスクリプトが FAILED を返すような場合には http://slony.info/ の開発者に連絡してください。