|
必聴講座ご紹介 Cloud Days Tokyo 2012 エムオーテックス Cloud Days Tokyo 2012 ヴイエムウェア Cloud Days Osaka 2012 アマゾン データ サービス ジャパン |
|
![]() |
| 図1●pgpoolのアーキテクチャ |
アプリケーションは常にpgpoolを経由して2つのpostmasterにアクセスする。ただし,pgpoolはアプリケーションから見ると普通のPostgreSQLに見えるので,pgpoolを使っていることを意識する必要はない。
問い合わせはマスタとセカンダリの両方に送られるので,データベースの内容は両者の間で常に一致することになる。データベースの検索結果については常にマスタ側の結果のみが採用される(図1[拡大表示])。
![]() |
| 図2●pgpoolのフェイルオーバー機能 |
pgpoolのサポートページはhttp://www2b.biglobe.ne.jp/~caco/pgpool/である。ソースコードとVine Linux用のRPM/SRPMがここで入手できる。本稿執筆時点の最新版はpgpool-1.0である。
RPMのインストール方法については言うまでもないと思うので,ここではソースからインストールする方法をご紹介する。コンパイルのためにはgccやmakeが必要になるが,PostgreSQLのソースツリーやライブラリは不要である。
pgpoolはconfigureを使用しており,LinuxやFreeBSDならそのままコンパイル,インストールできるはずだ。ここでは,Vine Linux上にインストールする例を示す。ソースコードは /tmp/pgpool-1.0.tar.gzにあらかじめダウンロードしてあるものとする。コンパイルするディレクトリはどこでもよいが,ここでは/usr/local/src/とする。以下,rootで実行する。
# cd /usr/local/src # tar xfz /tmp/pgpool-1.0.tar.gz # cd pgpool-1.0 # ./configure # make # make installこれで,プログラム本体が/usr/local/bin/pgpoolに,設定ファイルのテンプレートが/usr/local/etc/pgpool.conf.sampleにインストールされる。
テンプレートから設定ファイルをコピーする。
# cp /usr/local/etc/pgpool.conf.sample /usr/local/etc/pgpool.confデフォルトの設定では,レプリケーションを行わないことになっている。レプリケーションを実行するための設定をしよう。なお,今回は手軽に実験できると言うことで,1台のマシンでpgpool,2本のpostmasterを動かすものとする。
/usr/local/etc/pgpool.confの以下をviなどの適当なエディタで編集する。
(1) セカンダリ・バックエンド(2本目のpostmaster)の設定
まずセカンダリ・バックエンドが動くホストを指定する。
secondary_backend_host_name = ''今回は同一ホストでの起動なので,デフォルトのままで良い。他ホストでpostmasterが動いている場合は
secondary_backend_host_name = 'another_host'などと,ホスト名を指定する(IPアドレスを書いても良い)。次にそのpostmasterがコネクションを受け付けるポート番号を指定する。
secondary_backend_port = 0デフォルトでは0になっているので,これを適当に変える。今回は同一ホストで2つのpostmasterを動かすため,PostgreSQLのデフォルトの5432では都合が悪いので5433とする(もちろん5444など,5433以外の番号でも構わない)。
secondary_backend_port = 5433最後にレプリケーション機能を有効にする。
replication_mode = falseとなっているのを
replication_mode = trueにすればよい。以上でpgpool.confの修正は完了である。
次にpostmasterを起動する。もしまだPostgreSQLをインストールしていないのであれば,そのインストールが当然必要になるが,ここでは省略する。PostgreSQLのマニュアルや適当な参考書をごらんいただきたい。
pgpoolはPostgreSQL 7.2.x, 7.3.x,7.4.xで動作実績があるので,このうちのどのバージョンを使用しても良い。以後PostgreSQLはデフォルトの/usr/local/pgsqlにインストール済で,PostgreSQLのスーパユーザはpostgresであるものとする。
pgpoolでは,初期状態で2つのPostgreSQLのデータベース・クラスタの内容が一致していることが必要である.一番確実な方法は新たにデータベース・クラスタを作成することであり,今回は実験なのでこの方法を採用することにしよう。
マスタ側のデータベース・クラスタは/usr/local/pgsql/data1,セカンダリ側は/usr/local/pgsql/data2 とする。
以下,postgresユーザで実行する。もしすでにpostmasterが起動済であれば停止しておく。
$ initdb -E EUC_JP --no-locale -D /usr/local/pgsql/data1 $ initdb -E EUC_JP --no-locale -D /usr/local/pgsql/data2 $ postmaster -D /usr/local/pgsql/data1 $ postmaster -p 5433 -D /usr/local/pgsql/data2最初のpostmasterがマスタ側で,ポート5433側がセカンダリになる。念のため,
以上で準備ができたので,さっそくpgpoolにアクセスしてみよう。以下,postgresユーザで実行するものとする。まずデータベースを作ってみよう。
$ createdb -p 9999 testこれでcreatedbがpgpoolにアクセスし,pgpoolは2つのpostmasterに接続してデータベースを作成したはずだ。確認するためには,2つのpostmasterに直接アクセスしてみれば良い。
もちろんテーブルを作ってデータを登録し,検索することもできる。
$ psql -p 9999 test test=# CREATE TABLE t1(i INTEGER, t TEXT); CREATE TABLE test=# INSERT INTO t1 VALUES(1, 'foo'); INSERT 1184657 1 test=# SELECT * FROM t1; i | t ---+----- 1 | foo (1 row)これも2つのpostmasterに直接アクセスして確認してみよう。
$ psql test test=# SELECT * FROM t1; i | t ---+----- 1 | foo (1 row) $ psql -p 5433 test test=# SELECT * FROM t1; i | t ---+----- 1 | foo (1 row)無事にレプリケーションできているようだ。
前述のように,pgpoolには縮退運転の機能がある。たとえば,セカンダリのpostmaster(今回は5433ポートで動いている方)が停止した場合にはセカンダリを切り離してレプリケーションなしで運用を継続する.実際に試してみよう。
まずセカンダリ側を停止する。
$ pg_ctl -D /usr/local/pgsql/data2 -m i stop
ここでpg_ctlの引数に-miを指定しているのは,pgpoolがコネクション・プールを行い,接続を持続しているからである。psqlが終了してもPostgreSQLへのコネクションは切断されず,PostgreSQLから見るとあたかもクライアントが接続中のままに見えるため,強制切断を行う-m iが必要になったわけだ。
この状態でpgpoolにアクセスすると,
$ psql -p 9999 test psql: server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request.となりエラーになる。しかし,心配はいらない。もう一度アクセスすれば今度は接続に成功するはずである。
psql -p 9999 testこのように,2回目で接続に成功した。このとき,pgpoolを起動した端末には,
Welcome to psql 7.3.6, the PostgreSQL interactive terminal. Type: \copyright for distribution terms \h for help with SQL commands \? for help on internal slash commands \g or terminate with semicolon to execute query \q to quit test=#
ERROR: pid 1559: pool_flush: fflush failed (Broken pipe) log: pid 1534: starting degenration. shutdown secondary host (5433) log: pid 1534: degenration done. shutdown secondary host (5433)
ではマスタ側がダウンした場合にはどうなるだろうか?この場合は2回のエラーの後,セカンダリ側だけの運転にはいる.これは,縮退運転では必ずセカンダリを切り離すからである。そしてもう一度マスタ側にアクセスしたときに今度はフェイルオーバー機能が働き,ここでようやくセカンダリ側がマスタにとって代るのである(ログ参照)。
このようにpgpoolが自動的に縮退運転やフェイルオーバーを行うのは便利ではあるが,逆にpgpoolの現在の状態がわかりにくくなる。そこで,pgpoolにはステータスを取得する機能がある。
test=# show pool_status;
item | value | description
-----------------------------+-------+-----------------------------------------------------------------------
inetdomain | 0 | 1 if accepting TCP/IP connection
port | 9999 | pgpool accepting port number
socket_dir | /tmp | pgpool socket directory
backend_host_name | | master backend host name
backend_port | 5432 | master backend port number
secondary_backend_host_name | | secondary backend host name
secondary_backend_port | 5433 | secondary backend port number
num_init_children | 32 | # of children initially pre-forked
child_life_time | 0 | if idle for this seconds, child exits (not implemented yet)
connection_life_time | 0 | if idle for this seconds, connection closes
max_pool | 4 | max # of connection pool per child
logdir | /tmp | logging directory
backend_socket_dir | /tmp | Unix domain socket directory for the PostgreSQL server
replication_mode | 1 | non 0 if operating in replication mode
replication_strict | 0 | non 0 if operating in strict mode
replication_timeout | 5000 | if secondary does not respond in this milli seconds, abort the session
current_backend_host_name | | current master host name
current_backend_port | 5432 | current master port #
replication_enabled | 1 | non 0 if operating in replication mode
(19 rows)
このように,SQLコマンドを使ってpgpoolの内部状態を表示できる(もちろんこのSQLコマンドは「本物」ではなく,pgpoolがそのように見せているだけである)。最後の3行以外は設定ファイルの内容である。最後のreplication_enabledが1ならばレプリケーションモードで運転中ということになる。replication_modehが1であるにも関わらず,これが0なら縮退運転中ということになる。そのときに使用中のpostmasterはcurrent_backend_host_name(空白ならUnixドメインソケットでの接続),current_backend_portでわかる。
PostgreSQLサーバーが故障して縮退運転に入り,その後故障したサーバーを修理してレプリケーション運転に復帰するためには,まず2つのデータベース・クラスタの内容を一致させる必要がある。そのための手順は以下のようになる。
(1) pgpoolを停止する
(2) 生きている方のpostmasterを停止する
(3) 生きていた方のデータベース・クラスタの内容を,rsyncなどを使ってファイル・システム・レベルで,止っていた方にコピーする
(4) マスタとセカンダリのpostmasterを起動する
(5) pgpoolを再起動する
pgpoolは,更新系の問い合わせをマスタ,セカンダリの順に発行することによってレプリケーションを行う。その再,マスタでの更新SQLの完了を待たずにセカンダリにリクエストを投げることにより,処理効率を高めることができる。
しかし,この方式では,単独のPostgreSQLでは発生しないデッドロックが起きることがある。たとえば,pgpoolに2つのセッション(0と1)が接続中のときに,「LOCK t1」コマンドが相次いで2つのセッションから発行されたとしよう。タイミングによっては以下のようになることがある。
master secondary
session 0 session 1 session 0 session 1
------------------------------------------------------
lock
lock
lock
lock
------------------------------------------------------
session1のmasterはsession0のmasterを待っている。一方secondaryではsession1が先にロックを獲得したため,session0がsession1を待ち続けている。このようにしてお互いにお互いを待ち続けるデッドロック状態が発生する。
pgpoolでは,この問題に対処するため,以下の2つの方法を用意している。
(1) pgpool.confのreplication_strictをtrueにする
こうすると,常にsecondaryはmasterの問い合わせ処理が終わってから問い合わせを処理するのでデッドロックは発生しない。しかし,masterとsecondaryの並列処理ができないペナルティがある(それがどの程度のものであるかは後述のベンチマークを参考にしていただきたい)。
(2) replication_strictをfalseにした上で,デッドロックが発生する可能性のある問い合わせの先頭に特別なキーワード「/*STRICT*/」を入れる
/*STRICT*/ LOCK TABLE t1;このように「/*STRICT*/」をSQLコメントとして挿入すると,この問い合わせだけはmasterとsecondaryが並列処理しなくなるので,デッドロックが起きない。必要な個所だけ/*STRICT*/を記入すればパフォーマンスの低下を最小限に押さえられる。
なお,以上の対策を忘れて万が一デッドロック状態になっても, タイムアウト処理により,該当セッションが強制切断されるので,pgpoolが永久にフリーズすることはない。
pgpoolは同じ問い合わせを別々のデータベースサーバに発行することによってレプリケーションを行う。そのため,全く同じ問い合わせでも結果が異なるようなある種の問い合わせは正確にレプリケーションされない可能性がある。そのようなものの例としては,乱数,CURRENT_TIMETSTAMPなどの時刻関係の問い合わせがある。
レプリケーションでは,更新性能が気になるところである。一般にレプリケーションは複数台のデータベースを更新しなければならないため,どうしても単独で動くPostgreSQLよりも性能が低下する。できればこのペナルティは最小限であることが望ましい。そこで,汎用のベンチマーク・ソフトpgbenchを使ってpgpoolの性能を計ってみた。測定環境は以下の通りである。
ハードウェア: Pentium4 2.4GHz x2/メモリ 1GB/ハードディスク IDE 80GB
ソフトウェア: RedHat Linux 9/PostgreSQL 7.3.6
PostgreSQLの設定は以下のようになっている。
![]() |
| 図3●ベンチマーク用システムの構成 |
ホストA: pgbench
ホストB: pgpool
ホストC: PostgreSQL(マスタ側)
ホストD: PostgreSQL(セカンダリ側)
pgbenchのテスト・データベースはスケール・ファクタ1(10万件)の設定である。
(1) 更新系のテスト
pgbenchの-Nオプションを利用して,検索,更新(UPDATE,INSERT)を交えたトランザクションを1秒あたり何回実行できるかで性能を評価する。テストはpgbenchの同時接続数を1, 2, 4 , 8, 16, 32, 64, 128に変えながら,各接続あたり100トランザクションを実行する。なお,pgbenchの1ランごとに毎回pgbench -iを発行してテストデータベースを初期化している。
![]() |
| 図4●更新系のベンチマーク・テスト |
参考のためにreplication_strictをfalseにし,UPDATEコマンドに/*STRICT*/コメントを入れたものも測定してみた(「non strict mode」)。同時接続数が32のときにはオーバヘッドが20%以下と効果が見られたが,一方で64以上ではかえって性能が低下した。原因は不明だが,処理効率がよくなったためにかえってデータベース・サーバーに負荷がかかり過ぎているのかも知れない。
(2) 検索系のテスト
本来レプリケーションの必要のない検索系では,pgpoolによるオーバーヘッドが気になるところである。そこでpgbenchを使って検索のみの性能測定も行ってみた。表に結果を示す。測定条件はすべて同じで,同時接続数10,各接続あたり1000トランザクションを実行している。
表1●検索系のテスト
| 測定条件 | TPS値 | pgpoolなしの場合と比較した相対性能 |
|---|---|---|
| pgpoolなし | 4367.12307 | 100% |
| pgpoolあり(レプリケーションなし) | 4108.920919 | 94.10% |
| pgpoolあり(レプリケーションあり) | 3823.882671 | 87.60% |
| pgpoolあり(レプリケーションあり,strictモード) | 3558.758122 | 81.50% |
pgpoolが他のソフトに勝る点としては,ラージ・オブジェクトのレプリケーションが可能であること,更新性能が比較的良いことが上げられる。一方,pgpoolは負荷分散ができないので,サーバー・ハードウエアを増やして検索性能を狙うような使い方はできない。
pgpoolには縮退運転やフェイルオーバーの機能があるため,1系統のPostgreSQLのダウンには対応できる。しかし,pgpool自体がダウンすると,PostgreSQLが生きていても業務が止ってしまうことになる。もちろんpgpoolを2つ動かし,負荷分散装置を使うことによってこの問題を回避できるが,できればpgpool自体に相互監視機能がついていることが望ましい。
pgpoolは,シンプルかつ手軽に使え,オーバーヘッドが少ないレプリケーション・ソフトである。pgpoolを活用することによって,データの保全要求が厳しい分野にもPostgreSQLを適用できる可能性が広がると考えている。pgpoolを活用していただければ幸いである。
■著者紹介
石井達夫(いしい・たつお)氏
1984年,SRA入社。主にUNIX関連
の開発に従事するかたわら,95年からPostgreSQLのメーリング・リストを主宰。現在はオープンソースソリューション部でPostgreSQL関連のビジネス活動を技術支援。著書に『PostgreSQL完全攻略ガイド』(技術評論社),『PHPxPostgreSQLで作る最強Webシステム』(技術評論社),『PostgreSQL構築・運用ガイド』(日経BP,共著)などがある。日本PostgreSQLユーザ会理事
長。