Welcome to pgpool -II page |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
pgpool-IIとはpgpool-IIはPostgreSQL専用のミドルウェアで、PostgreSQLのデータベースクライアントとPostgreSQLサーバの間に割り込む形で動作し、PostgrSQLに以下のような機能を追加します。
pgpool-IIはPostgreSQLバックエンドとフロントエンドの通信プロトコルを理解してその間を中継します。すなわち、PostgreSQLのデータベースアプリケーションからはPostgreSQLサーバに、PostgreSQLからはデータベースアプリケーションに見えるように設計されています。そのため、PostgreSQLそのものはもちろん、アプリケーションの開発言語によらず、PostgreSQLのデータベースアプリケーションにほとんど手を加えることなく、pgpool-IIの機能が利用できます。 pgpool-IIの稼働環境pgpool-IIは、Linuxをはじめ、SolarisやFreeBSDなどのほとんどのUNIX環境で動作します。Windowsでは動きません。対応するPostgreSQLのバージョンは、PostgreSQLの6.4以降です。ただしパラレルクエリモードを使用するときはPostgreSQL 7.4以降をお使いください。 pgpool-IIのインストールpgpool-II のソースコードはpgpool開発ページ からダウンロードできます。 pgpool-IIのインストールには、gcc 2.9以上、およびGNU makeが必要です。 また、pgpool-IIはlibpq(PostgreSQL付属のクライアントライブラリ)を使用するので、ビルドを行うマシン上にlibpqがインストールされていることが必要です。
pgpool-IIの設定pgpool-IIの設定ファイルはデフォルトでは/usr/local/etc/pgpool.confおよび /usr/local/etc/pcp.confです。pgpool-IIは動作モードによって使用できる機能と、 必要な設定項目が異なります。
pcp.confの設定どの動作モードでも、pcp.confの設定は必要です。pgpool-IIには管理者がpgpool-IIの 停止や情報取得などの管理操作を行うためのインターフェイスが用意されていま す。そのインターフェイスを利用するためにはユーザ認証が必要になるので、そ のユーザ名とパスワードをpcp.confに登録します。 pgpool-IIをインストールすると、$prefix/etc/pcp.conf.sampleができるので、それを $prefix/etc/pcp.confという名前でコピーします。 cp $prefix/etc/pcp.conf.sample $prefix/etc/pcp.conf pcp.confでは空白行や#で始まる行はコメントと見なされます。 ユーザとパスワードは、 ユーザ名:[md5暗号化したパスワード] のように指定します。 [md5暗号化したパスワード]は、$prefix/bin/pg_md5コマンドで作成できます。 ./pg_md5 foo acbd18db4cc2f85cedef654fccc4a4d8パスワードを引数に渡したくない場合は pg_md5 -p を実行してください。 ./pg_md5 -p password: <パスワードを入力> pcp.confは、pgpool-IIを動作させるユーザIDで読み取り可能になっていなければ なりません。 pgpool.confの設定前述のように、動作モードによって、pgpool.confの設定項目が異なります。 pgpool-IIをインストールすると、$prefix/etc/pgpool.conf.sampleができるので、それを $prefix/etc/pgpool.confという名前でコピーします。 cp $prefix/etc/pgpool.conf.sample $prefix/etc/pgpool.conf pgpool.confでは空白行や#で始まる行はコメントと見なされます。 rawモード単にpgpool-IIを経由して接続するだけのモードです。単にPostgreSQLサーバへの接 続セッション数を制限したり、2台以上のPostgreSQLサーバを用意してフェイル オーバ動作をさせたいときに利用します。
rawモードにおけるフェイルオーバ動作について rawモードにおいて、2台以上のPostgreSQLサーバを指定すると、フェイルオーバ が可能です。フェイルオーバでは、正常時にはbackend_hostname0で指定した PostgreSQLのみを使用し、ほかのサーバにはアクセスしません。 backend_hostname0のサーバがダウンすると、次にbackend_hostname1で指定した サーバにアクセスをこころみ、成功すればそれを使用します。以下、 backend_hostname2...でも同様になります。 コネクションプールモードrawモードに加え、コネクションプーリングが利用できるようになります。 設定項目は、rawモードでの設定項目の他に以下を設定します。
コネクションプールモードにおけるフェイルオーバ動作について rawモードと同様の動作をします。 レプリケーションモードレプリケーションを有効にするモードです。 rawモード、コネクションプールモードに加え、以下を設定します。
ロードバランスの条件について load_balance_mode = true を設定した場合、以下の条件のすべてを満たした時にSELECTなどの問い合わせがロードバランスされます。
(replicate_selectの項目も参考にしてください) また、詳細な判定条件をフローチャートにしたものもご覧下さい。 なお、 /*REPLICATION*/ SELECT ... とすることによって、本来負荷分散されたり、マスタのみに送信されるべき問合わせがすべてのバックエンドに送信される(レプリケーションされる)ようになります。副作用がある関数を含む問合わせに対してはこのテクニックが利用できます。 注意: JDBC ドライバなどのように、ドライバ内で autocommit の有効・無効のオプションがある場合、 autocommit を無効にすると、pgpoolが内部で BEGIN コマンドを実行する関係上、正しくロードバランスされない可能性があります。 クエリをロードバランスさせたい場合は autocommit を有効にしてください。 たとえばJDBCであれば setAutoCommit(true) を実行してください。 レプリケーションモードにおける縮退運転について PostgreSQLサーバのうち、1台がダウンすると、そのサーバを切り離して縮退運 転に入ります。1台でもサーバが生き残っていれば、システムとしての運用を継 続できます。 レプリケーションモード固有のエラーについて データの整合性を保つために、pgpoolはレプリケーション時に INSERT, UPDATE, DELETE の更新件数がすべてのノードが同じでない場合、意図的に構文エラーを起すSQLを送信することによって、トランザクションをアボートさせます(フェイルオーバは起きません)。以下のような感じになります。 =# UPDATE t SET a = a + 1; ERROR: pgpool detected difference of the number of update tuples HINT: check data consistency between master and other db node マスタースレーブモードmaster/slaveモードは、Slony-Iのような、master/slave式のレプリケーショ ンソフトにレプリケーションをまかせるモードです。このモードで使うために は、レプリケーションモードと同じように、DBノードのホスト情報 をセットし、master_slave_modeとload_balance_modeをtrueにします。このと き、問い合わせによってマスターDBだけに問い合わせが送られる場合と、DB ノードの間でロードバランスされて問い合わせが送られる場合があります。 ロードバランスの条件はレプリケーションモードと同じです。 マスタースレーブモードでは、pgpool.confのreplication_modeをfalseに、master_slave_mode をtrueにします。 このパラメータを変更した時には pgpool-II を再起動してください。 パラレルモードパラレルクエリ機能が利用できるモードです。テーブルを分割させ、各ノードにデータ を持たせることができます。またレプリケーションや負荷分散機能も同時に使うことが できます。 パラレルモードでは、pgpool.confのreplication_modeまたはloadbalance_modeにtrue を設定し、master_slave をfalseにし、parallel_mode をtrueにします。 このパラメータを変更した時には pgpool-II を再起動してください。 システムDBの設定 パラレルモードを利用するためには、システムDBを設定する必要があります。 システムDBはデータを各PostgreSQLサーバで分割するためのルールを PostgreSQLのテーブルの形で保持します。システムDBはpgpoolが動作するホスト と同じホストに置く必要はありません。システムDBの設定はpgpool.confで行い ます。
システムDBの初期設定 システムDBにスキーマとテーブルを作成します。初期設定用のスクリプトが $prefix/share/system_db.sqlにあるのでそれを利用します。ただし、このスク リプトではスキーマ名が"pgpool_catalog"となっているので、違うスキーマを使 う場合は適当に書き換えてください。また、データベース名として"pgpool"以外 を使う場合は以下を適当に読み替えてください。 psql -f $prefix/share/system_db.sql pgpool dblinkのインストール パラレルモードではdblinkを使います。dblinkはPostgreSQLソースファイル ($POSTGRES_SRC) $(POSTGRES_SRC)/contrib/dblink にあります。$POSTGRES_SRC/contrib/dblink/README.dblinkを参考にシステム DBにdblinkをインストールしてください。 また、pgpoolデータベースに関数の登録が必要です。 psql pgpool < $POSTGRES_SRC/contrib/dblink/dblink.sql コネクション数の設定 パラレルモードでは、クエリによりシステムDBからdblink経由でpgpoolに接続 するので、想定される同時接続数以上のコネクションが必要になる場合があり ます。そのため、pgpool.confのnum_init_childrenには同時接続数より十分大 きい値を設定して下さい。 目安として以下の式でnum_init_childrenを設定してください。 num_init_children = 想定される同時接続数 * ( 1 + クエリの中で使われているテーブルの最大数) データ分割ルールの登録 データ分割を行うテーブルに対しては、テーブル情報をあらかじめ pgpool_catalog.dist_def というテーブルに登録しておきます。 CREATE TABLE pgpool_catalog.dist_def( dbname TEXT, -- DB名 schema_name TEXT, --schema名 table_name TEXT, -- テーブル名 col_name TEXT NOT NULL CHECK (col_name = ANY (col_list)), -- 分散キー列名 col_list TEXT[] NOT NULL, -- tableの属性名 type_list TEXT[] NOT NULL, -- 属性のタイプ名 dist_def_func TEXT NOT NULL, -- 分散先のDBノードを決定する関数名 PRIMARY KEY (dbname,schema_name,table_name) ); レプリケーションテーブルのルール登録 一つのSQL文にJOIN等でデータ分割ルールに登録したテーブルと共に レプリケーションを行うテーブルを指定する場合には、レプリケーション を行うテーブルの情報をあらかじめ、pgpool_catalog.replicate_def というテーブルに登録しておきます。 CREATE TABLE pgpool_catalog.replicate_def( dbname TEXT, -- DB名 schema_name TEXT, --schema名 table_name TEXT, -- テーブル名 col_list TEXT[] NOT NULL, -- tableの属性名 type_list TEXT[] NOT NULL, -- 属性のタイプ名 PRIMARY KEY (dbname,schema_name,table_name) ); pgbenchのテーブルを分割するルールの例を示します。 この例では、accountsテーブルに対しては分割を行い、branchesテーブル とtellersテーブルに対してはレプリケーションを行うことにします。 また、accountsテーブルとbanchesテーブルはbidで結合されることを想定し branchesテーブルはレプリケーションテーブルのルール登録を行います。 もし、accountsテーブル、branchesテーブルとtellersテーブルの3つの テーブルの結合が行われる場合には、あらかじめtellersテーブルに対しても レプリケーションテーブルのルール登録を行う必要があります。 INSERT INTO pgpool_catalog.dist_def VALUES ( 'pgpool', 'public', 'accounts', 'aid', ARRAY['aid','bid','abalance','filler'], ARRAY['integer','integer','integer','character(84)'], 'pgpool_catalog.dist_def_accounts' ); INSERT INTO pgpool_catalog.replicate_def VALUES ( 'pgpool', 'public', 'branches', ARRAY['bid','bbalance','filler'], ARRAY['integer','integer','character(84)'] ); ここで、pgpool_catalog.dist_def_accountsは、引数として分割キーの値を受け取り、 どのPostgreSQLサーバ(「DBノード」と呼びます)を0からの番号で返す関数です。こ こでは、3台のDBノードにデータを分割する関数の例を示します。 CREATE OR REPLACE FUNCTION pgpool_catalog.dist_def_accounts (val ANYELEMENT) RETURNS INTEGER AS ' SELECT CASE WHEN $1 >= 1 and $1 <= 30000 THEN 0 WHEN $1 > 30000 and $1 <= 60000 THEN 1 ELSE 2 END' LANGUAGE SQL; クライアント認証(HBA)のための pool_hba.conf 設定方法PostgreSQLのpg_hba.confと同じようにpgpoolでもpool_config.confファイ ルを使ったクライアント認証がサポートされています。 pgpoolをインストールするとデフォルトインストール先の設定ファイルディ レクトリ"/usr/local/etc"にpool_hba.conf.sampleが一緒にインストール されます。このpool_hba.conf.sampleファイルをpool_hba.confとしてコピー し、必要であれば編集してください。デフォルトではpool_hbaによる認証は有 効になっています。 pool_hba.confのフォーマットはpg_hba.confのものとほとんど同じです。 local DATABASE USER METHOD [OPTION] host DATABASE USER CIDR-ADDRESS METHOD [OPTION] 各フィールドで設定できる値の詳細は"pool_hba.conf.sample"を参照して ください。 以下はpool_hbaの制限事項です。
現在pgpoolはSSL接続をサポートしていないので"hostssl"は指定するこ とができません。 pgpoolはバックエンドサーバにあるユーザ情報を事前に知る事ができな いため、データベース名はpool_hba.confにある値のみと比較されます。 なのでグループに関する認証はpool_hbaで行うことができません。 上記の"samegroup"と同じ理由で、ユーザ名はpool_hba.confにある値の みと比較されます。グループに関する認証はpool_hbaで行うことはでき ません。 現在pgpoolはIPv6をサポートしていません。 これも上記の"samegroup"と同じ理由によるものです。pgpoolはバックエ ンドのユーザ/パスワード情報を持っていないので、バックエンドに保存 されているパスワードを使った認証を行うことができません。 ここで説明された機能、制限はクライアントとpgpool間で行われるクライ アント認証についてだということに注意してください。クラインアントは pgpoolのクライアント認証に成功したとしても、PostgreSQLによるクライ アント認証に成功しないと接続状態となりません。pool_hbaにとってはク ライアントに指定されたユーザ名やデータベース名 (例. psql -U testuser testdb)が実際にバックエンド上に存在するかどう かは問題ではありません。それがpool_hba.confの値とマッチするかどうか でチェックが行われます。 pgpoolが稼働するホスト上のユーザ情報を使ったPAM認証を利用することが できます。pgpoolをPAMサポート付きでビルドするにはconfigureオプショ ンに"--with-pam"を指定してください。 ./configure --with-pam 実際にPAM認証を有効にするには、pool_hba.confで"pam"メソッドを設定す るのに加え、pgpoolのサービス設定ファイルをシステムのPAM設定ディレクト リ(通常は /etc/pam.d に作成する必要があります。サービス設定ファイ ルの例はインストールディレクトリの"share/pgpool.pam"を参考にしてく ださい。 クエリキャッシュの設定方法pgpool-IIでは、すべてのモードでクエリキャッシュを利用することができます。 利用する場合には、pgpool.confの設定を以下のように設定します。 enable_query_cache = true また、システムDBに以下のテーブルを作成してください。 CREATE TABLE pgpool_catalog.query_cache ( hash TEXT, query TEXT, value bytea, dbname TEXT, create_time TIMESTAMP WITH TIME ZONE, PRIMARY KEY(hash, dbname) ); ただし、この例ではスキーマ名が"pgpool_catalog"となっているので、違うスキーマを使う場合は適当に書き換えてください。 pgpool-IIの起動と停止以上で設定が終わったので、各DBノードを起動し、必要ならばシステムDBも起動 してからpgpool-IIを起動します。 pgpool [-c][-f config_file][-a hba_file][-F pcp_config_file][-n][-d]
pgpool-IIの停止は後述のpcpコマンドでもできますが、pgpool-IIコマンドを使うこと もできます。 pgpool [-f config_file][-F pcp_config_file] [-m {s[mart]|f[ast]|i[mmediate]}] stop
pgpool-IIの設定ファイルの再読み込みpgpool-IIの設定ファイルは、pgpool-IIを再起動することなく読み直すことができます。 pgpool [-f config_file][-a hba_file][-F pcp_config_file] reload
設定項目によっては、再読み込みを行なっても反映されないものがあるので、ご注意下さい。 また、設定の変更はすでに接続中のセッションには反映されません。次回、クライアントがpgpool-IIに接続したときから反映されます。 オンラインリカバリオンラインリカバリ概要レプリケーションモードで pgpool が動作している場合、ダウンしたノー ドのデータを再同期させた上で、ノードを復帰させることができます。こ の機能を「オンラインリカバリ」と呼びます。 オンラインリカバリを実施するためには、ノードが切り離されていると pgpool が検知している必要があります。ノードを動的に追加したい場合に は pgpool.conf の backend_hostnameなどのパラメータを追加しておき、設定ファイル を再読み込みさせると、ノードが切り離された状態で pgpool にノード情報が登録されます。 また、リカバリするノードの PostgreSQL がすでに動作中であれば、あら かじめ PostgreSQL をシャットダウンさせておいてください。 注意: オンラインリカバリを実行する前に、マスタノード (稼働中のノードのうち、一番番号が若いノード)上でのautovacuumを停止してください。 オンラインリカバリ中にautovacuumが動くと、データベースの内容が変化する可能性があるため、 ノード間の同期がきちんと取れなくなる可能性があります。 pgpool ではオンラインリカバリを 2 段階に分けて実施します。pgpool の クライアントからは完全なデータの同期を取るために若干の接続待ちが発 生します。リカバリ手順で以下の通りです。
データ同期の第一段階を「ファーストステージ」と呼びます。ファースト ステージ中に1 回目のデータ同期を行います。ファーストステージ中はデー タの更新や参照を並行して行うことができます。 ファーストステージで処理する内容はユーザが定義することができます。 スクリプトでは 3 つの引数を受け取ることができます。
次に 2 回目のデータ同期を行います。これを「セカンドステージ」と呼び ます。pgpool ではセカンドステージに入る前に接続中のクライアントがす べて接続が終了されるまで待ちます。その間に接続リクエストが来た場合 には、その接続をすべてブロックします。 セカンドステージで処理する内容はユーザが定義することができます。 スクリプトでは 3 つの引数を受け取ることができます。
すべての接続が終了されると、ファーストステージ以降に更新されたデー タを同期するためのセカンドステージが開始されます。そこで最終的なデー タの同期を行います。この間はクライアントからは pgpool への接続が待 たされる状態になります。 なお、オンラインリカバリの制限事項として、複数のホストに pgpool を 配置してレプリケーションさせている場合には、オンラインリカバリは正 しく動作しません。どれかの pgpool にリカバリリクエストを出した時に、 他の pgpool から更新が伝搬すると、データを同期させることができなく なります。 pgpool の設定オンラインリカバリを設定するためには、pgpool.conf の以下の値を設定 してください。
C 言語関数のインストール次に、リカバリを実施するための PostgreSQL の C 言語関数を各ノードの template1 データベースにインストールします。ソースコードは pgpool-II-x.x.x/sql/pgpool-recovery/ にあります。ディレクトリを移動し、make install してください。 % cd pgpool-II-x.x.x/sql/pgpool-recovery/ % make install C 言語関数のモジュールをインストールしたら、続いて C 言語関数を呼びだ すための SQL をインストールします。 % cd pgpool-II-x.x.x/sql/pgpool-recovery/ % psql -f pgpool-recovery.sql template1 リカバリスクリプトの配置データを同期させるためのスクリプトと、リモートから postmaster を再起動 させるためのスクリプトを各ノードの $PGDATA 以下に配置します。あらかじ めpgpool-II-x.x.x/sample 以下にサンプルスクリプトも用意してありますの で参考にしてください。ここではサンプルスクリプトを使って、PITR による リカバリ方法と、rsync によるリカバリ方法を説明します。 PITR によるリカバリここでは PostgreSQL の PITR 機能を使ってリカバリをする設定例を説明しま す。PITR によるリカバリをする場合にはあらかじめ PostgreSQL の設定でロ グをアーカイブさせるようにしておいてください。 まずファーストステージでベースバックアップを取得し、リカバリ先へコピー するスクリプト(ここではファイル名を copy-base-backup とします)を用意し ます。例えば以下のようなスクリプトで取得することができます。 #! /bin/sh DATA=$1 RECOVERY_TARGET=$2 RECOVERY_DATA=$3 psql -c "select pg_start_backup('pgpool-recovery')" postgres echo "restore_command = 'scp $HOSTNAME:/data/archive_log/%f %p'" > /data/recovery.conf tar -C /data -zcf pgsql.tar.gz pgsql psql -c 'select pg_stop_backup()' postgres scp pgsql.tar.gz $RECOVERY_TARGET:$RECOVERY_DATA ベースバックアップ取得時に recovery.conf を生成しておきます。 restore_command = 'scp master:/data/archive_log/%f %p' セカンドステージでは最新の状態まで PITR によるリカバリを実施できるよう にするために、pgpool_recovery_pitr スクリプトを$PGDATA にコピーします。 このスクリプトでは XLOG を強制的にスイッチさせるようにします。 #! /bin/sh psql -c 'select pg_switch_xlog()' postgres スクリプトの配置が完了したら pgpool.conf に設定します。 recovery_1st_stage_command = 'copy-base-backup' recovery_2nd_stage_command = 'pgpool_recovery_pitr' これで PITR によるオンラインリカバリの準備が完了です。 pgpool_remote_start データ再同期後に postmaster を起動させるスクリプトです。 pgpool からは以下の形式でスクリプトを実行します。 % pgpool_remote_start remote_host remote_datadir remote_host: リカバリノードのホスト名 remote_datadir: リカバリノードのデータベースクラスタパス サンプルスクリプトでは ssh 経由で postmaster を起動しています。こちら もあらかじめパスフレーズ無しで ssh 経由でログインできるように設定して おく必要があります。 PITR によるリカバリであれば、pgpool_remote_start 内でベースバックアッ プを展開し、recovery.conf の内容にしたがってリカバリした後に postmaster が接続可能状態になります。 #! /bin/sh DEST=$1 DESTDIR=$2 PGCTL=/usr/local/pgsql/bin/pg_ctl # Expand a base backup ssh -T $DEST 'cd /data/; tar zxf pgsql.tar.gz' 2>/dev/null 1>/dev/null < /dev/null # Startup PostgreSQL server ssh -T $DEST $PGCTL -w -D $DESTDIR start 2>/dev/null 1>/dev/null < /dev/null & rsync によるリカバリ7.4 以前の場合は PITR 機能がありません。そこで PITR を使わずに rsync を使ったリカバリ方法を説明します。sample ディレクトリに pgpool_recovery というファイルがあります。マスタから復帰させるノードへ のデータの物理コピーを行うスクリプトです。pgpool からは以下の形式でス クリプトを実行します。 % pgpool_recovery datadir remote_host remote_datadir datadir: マスタのデータベースクラスタパス remote_host: リカバリノードのホスト名 remote_datadir: リカバリノードのデータベースクラスタパス サンプルスクリプトでは rsync を使って物理コピーをしています。もし rsync を使う場合は、パスフレーズ無しで ssh 経由でログインできるように あらかじめ設定しておく必要があります。 rsyncに関する注記:
pgpool_recovery を使う場合は pgpool.conf に以下の行を追加してください。 recovery_1st_stage_command = 'pgpool_recovery' recovery_2nd_stage_command = 'pgpool_recovery' リカバリの実行以上でオンラインリカバリの準備が整いました。 オンラインリカバリを実行するには pcp_recovery_node コマンドを使うか、 pgpool 管理ツールから実行してください。 注意点として、pcp_recovery_node を実行する際に、タイムアウトを長くして ください。pgpoolAdmin から実行する場合は pgmgt.conf.php 内の _PGPOOL2_PCP_TIMEOUT を大きくしてください。 制限事項
認証・アクセス制御方式
レプリケーションモードで注意が必要な関数などpgpool-IIでは同じ問い合わせを送っても異なる結 果を返すようなデータ、たとえば乱数やトランザクションID、OID、SERIAL、 シーケンス、CURRENT_TIMETSTAMPのようなものに関してはレプリケーショ ンはしますが、2台のホストでまったく同じ値がコピーされる保証はありま せん。 CREATE TEMP TABLEで作成されたテーブルはフロントエンドがセッショ ンを終了しても削除されません。これは、コネクションプールの効 果でバックエンドから見るとセッションが継続しているように見え るからです。セッションの終了時に明示的にDROP TABLEするか、ト ランザクションブロックの中でCREATE TEMP TABLE ... ON COMMIT DROPをお使い下さい。 クエリについてpgpool-II では扱うことができないクエリについて説明します。 マルチバイト文字について制限対象:全モード 現在の実装では、マルチバイト文字の変換処理を行いません。クライアントエ ンコーディング、バックエンドノードのサーバエンコーディング、システム DB のサーバエンコーディングを一致させるようにしてください。 マルチステートメント制限対象:全モード マルチステートメント(';' で区切って複数の文をまとめた SQL)を pgpool が 正しく処理することができません。必ず文を分けて送信してください。 なお、psql を使って pgpool に接続した場合は、psql 内部でマルチステート メントを分解し、1 つずつ送信します。 拡張問い合わせプロトコル制限対象:パラレルモード JDBC ドライバなどのような拡張問い合わせプロトコルには対応していません。 必ず簡易問い合わせプロトコルを使用してください。 SELECT制限対象:パラレルモード postgresql.conf の add_missing_from設定値を off (デフォルト値)に設定してください。 add_missing_from 設定値が on の時に使えるクエリは正しくpgpoolで処理されない可能性 があります。 INSERT制限対象:パラレルモード データ分割をしているテーブルに対してINSERT を行う際には、分割ルールとなる値を DEFAULT にはできません。例え ばテーブル t に x というカラムがあり、x が分割ルールの対象カラムだった 場合には、 INSERT INTO t(x) VALUES (DEFAULT); はできません。また、分割ルールとなる値が関数呼び出しの場合も 対応していません。 INSERT INTO t(x) VALUES (func()); 必ず明示的に値を与える必要があります。 また、SELECT INTO や INSERT INTO ... SELECT という形式もサポートしてい ません。 UPDATE制限対象:パラレルモード 分割ルールとなるカラムを更新すると分割ルールに従ったデータの整合性が崩 れる可能性があります。pgpool-II では特にデータの再配置ということは行い ません。 もし制約違反などにより一部のノードでエラーになった場合にロールバックす ることはできません。 WHERE 句にデータ分割を行ったテーブルを参照するサブクエリや関数呼び出しがある場合には正しく動かない可能性が あります。 例:UPDATE branches set bid = 100 where bid = (select max(bid) from beances); SELECT ... FOR UPDATE制限対象:パラレルモード WHERE 句にデータ分割を行ったテーブルを参照するサブクエリや関数呼び出しがある場合には正しく動かない可能性が あります。 例:SELECT * FROM branches where bid = (select max(bid) from beances) FOR UPDATE; COPY制限対象:パラレルモード COPY BINARY には対応していません。また、ファイルからのコピーにも対応し ていません。COPY FROM STDIN と COPY TO STDOUT のみ対応しています。 ALTER/CREATE TABLE について制限対象:パラレルモード pgpool に情報を更新させるためには、pgpool を再起動する必要があります。 トランザクション制限対象:パラレルモード トランザクション中に発行される SELECT は dblink を経由する場合には別ト ランザクションになります。以下に例を示します。 BEGIN; INSERT INTO t(a) VALUES (1); SELECT * FROM t ORDER BY a; <-- 上の INSERT した値は見えない END; また制約違反などにより一部のノードでエラーになった場合にロールバックすることはできません。 View/Rule制限対象:パラレルモード View や Rule は各ノードに同じ内容が定義されます。 CREATE VIEW sample AS SELECT * FROM a, b where a.i = b.i 上記のような テーブル結合を含んだVIEWは、a と b は同じノード内でのみ結合処理を行い、 各ノードからの実行結果を統合します。ノードをまたがった JOIN を行う View を作成する ことはできません。Rule についても同様になります。ただし、データ分割したテーブルを同 じノード内でのみ結合したい場合に、VIEWを作成することは可能です。この場合にはVIEWを pgpool_catalog.dist_defテーブルにVIEWを登録しておきます。 また、pgpool_catalog.dist_defテーブルのcol_nameとdist_def_funcには、VIEWで定義した カラムとVIEWに対してINSERTが発行された場合に何処のノードにクエリを問い合わせるのかを決定 する関数を登録してください。 関数/トリガについて制限対象:パラレルモード 関数は各ノードに同じ内容が定義されます。関数内で JOIN や他のノードのデー タ操作を行うことはできません。 Natural Join について制限対象:パラレルモード Natural Joinは利用できません。ON 結合条件 または、USING(結合カラム)を明示的に 指定する必要があります。 USING句 について制限対象:パラレルモード JOIN 構文の中で利用される USING 句はクエリの書き換え処理によってON 句に 変換されます。そのため、ターゲットリストに"*"を利用する問い合わせを行う 場合には、同じ列名が出力されます。 デッドロックについて制限対象:パラレルモード ノード間をまたがるデッドロックを検出することができません。 例:accountsテーブルは以下のルールで分割されている。 aid <= 100000 ノード 0 aid >= 100000 ノード 1 A) BEGIN; B) BEGIN; A) SELECT * FROM accounts WHERE aid = 100001 FOR UPDATE; B) SELECT * FROM accounts WHERE aid = 100000 FOR UPDATE; A) SELECT * FROM accounts WHERE aid = 100000 FOR UPDATE; B) SELECT * FROM accounts WHERE aid = 100001 FOR UPDATE; この場合、単一のノードではデッドロックを検知できないため、pgpool は待 たされた状態になります。この現象は SELECT FOR UPDATE 以外にも行ロック を獲得するクエリで発生する可能性があります。 また、あるノードでデッドロックが発生した場合は、各ノードのトランザクショ ンの状態が異なる状況になります。そのため、デッドロックを検知した時点で 以下のログを出力して pgpool は該当のプロセスを終了させます。 pool_read_kind: kind does not match between master(84) slot[1] (69) スキーマについて制限対象:パラレルモード public 以外のスキーマに属すようなオブジェクトの参照は必ず スキーマ.オブジェクト と指定するようにしてください。 set search_path = xxx を指定し、スキーマ名を省略すると、pgpool がどの分散ルールを適用するか 判断できません。 テーブル名、カラム名について制限対象:パラレルモード pool_で始まるテーブル、カラム名は使えません。クエリ書き換えの際に内部処理で使用します。 システム DB分割ルールpgpool-II では分割ルールの対象のカラムは 1 つのみとします。x と y の OR 条件などといったものには対応していません。 ビルドに必要な環境libpqpgpool-II では libpq をリンクします。libpq のバージョンは 2.0 の場合、 configure に失敗します。必ず libpq 3.0 (PostgreSQL 7.4) をリンクするよ うにしてください。また、SystemDB のバージョンも PostgreSQL 7.4 以降が 必須になります。 クエリキャッシュ現在のクエリキャッシュの実装では、キャッシュの無効化を手動で行う必要が あります。 リファレンスPCPコマンドリファレンスPCPコマンド一覧pgpool-IIを操作するUNIXコマンドとして、以下のものがあります。 * pcp_node_count - ノード数を取得する * pcp_node_info - ノード情報を取得する * pcp_proc_count - プロセス一覧を取得する * pcp_proc_info - プロセス情報を取得する * pcp_systemdb_info - システムDB情報を取得する * pcp_detach_node - ノードを切り離す * pcp_attach_node - ノードを復帰させる * pcp_stop_pgpool - pgpool-IIを停止させる * pcp_recovery_node - マスタノードを使ってノードのデータを再同期、ノード起動させる 共通引数全てのコマンドには共通する引数があります。これは接続するpgpool-IIの情報や認証 情報などです。 ex) $ pcp_node_count [-d] 10 localhost 9898 postgres hogehoge 第一引数 - タイムアウト値 秒数でタイムアウト値を指定します。この時間内にpgpool-IIから応 答がない場合はコネクションを切断して終了します。なお、 このオプションは 2.1 からは無視するようになっています。 第二引数 - pgpool-IIが稼動しているホスト名 第三引数 - pgpool-IIが受け付けているポート番号 第四引数 - PCPユーザ名 第五引数 - PCPパスワード オプション引数として、-dがあります。-dが指定されるとデバッグ情報を出力します。 PCPユーザ名とパスワードは ./configure 時に --prefix で指定した 'インストールディレクトリ/etc' にある pcp.conf 内に記述されているものを指定 します。pcp.conf ファイルの場所がデフォルト以外の場所にある場合、pgpool の -F オプションでその位置を指定することができます。 パスワードはコマンドに渡す時点でmd5化されている必要はありません。 コマンド群全てのコマンドは、実行した結果が標準出力に表示されます。 pcp_node_count書式: pcp_node_count _timeout_ _host_ _port_ _userid_ _passwd_ pgpool-IIの pgpool.conf で定義されたノードの総数を表示します。切り離されている ノードの区別はしません。 pcp_node_info書式: pcp_node_info _timeout_ _host_ _port_ _userid_ _passwd_ _nodeid_ pgpool-IIの pgpool.conf で定義されたノードの情報を表示します。出力結果は以下の 例の通りです。 ex) $ pcp_node_info 10 localhost 9898 postgres hogehoge 0 host1 5432 1 1073741823.500000 結果は以下の順の通りです。 1. ノードのホスト名 2. ノードのポート番号 3. ステータス 4. ロードバランスウェイト ステータスは[0..3]までの数字で表わされます。各数字の意味は: 0 - 初期化時のみに表われる。PCPコマンドで表示されることはない。 1 - ノード稼働中。接続無し 2 - ノード稼働中。接続有り 3 - ノードダウン ロードバランスウェイトはNormalizeされたフォーマットで出力されます。 定義されていないノードIDを指定するとBackendErrorと表示され、終了コード12で終 了します。 pcp_proc_count
書式: pcp_proc_count _timeout_ _host_ _port_ _userid_ _passwd_ pgpool-IIの子プロセスのプロセスIDを一覧表示します。複数ある場合は空白文字で区 切られます。 pcp_proc_info
書式: pcp_proc_info _timeout_ _host_ _port_ _userid_ _passwd_ _processid_ pgpool-IIの子プロセス情報を表示します。出力結果は以下の例の通りです。 ex) $ pcp_proc_info 10 localhost 9898 postgres hogehoge 3815 postgres_db postgres 1150769932 1150767351 3 0 1 結果は以下の順の通りです。 1. 接続しているデータベース名 2. 接続しているユーザ名 3. プロセススタート時刻 4. コネクション作成時刻 5. プロトコルメジャーバージョン 6. プロトコルマイナーバージョン 7. コネクション使用回数 コネクションがバックエンドに対して張られていない場合、データは表示されません。 コネクション情報が複数ある場合、複数行に1行1コネクション情報で表示されます。 時刻はEPOCHタイムからの秒数で表わされます。 定義されていないプロセスIDを指定するとBackendErrorと表示され、終了コード12で 終了します。 pcp_systemdb_info
書式: pcp_systemdb_info _timeout_ _host_ _port_ _userid_ _passwd_ pgpool-IIのシステムDB情報を表示します。出力結果は以下の通りです。 $ pcp_systemdb_info 10 localhost 9898 postgres hogehoge localhost 5432 yamaguti '' pgpool_catalog pgpool 3 yamaguti public accounts aid 4 aid bid abalance filler integer integer integer character(84) dist_def_accounts yamaguti public branches bid 3 bid bbalance filler integer integer character(84) dist_def_branches yamaguti public tellers bid 4 tid bid tbalance filler integer integer integer character(84) dist_def_tellers まず一行目にシステムDBの情報が表示されます。結果は以下の順の通りです。 1. ホスト名 2. ポート番号 3. ユーザ名 4. パスワード。空の場合は''で表示されます。 5. スキーマ名 6. データベース名 7. 分散定義関数の数 二行目以降は分散定義が表示されます。複数の定義がある場合は、一つの定義につき 一行表示されます。結果は以下の順の通りです。 1. 分散対象のデータベース名 2. 分散対象のスキーマ名 3. 分散対象のテーブル名 4. 分散キーカラム名 5. 分散対象テーブル中のカラム数 6. カラム名リスト(5.のカラム数分表示されます) 7. カラム型リスト(5.のカラム数分表示されます) 8. 分散定義関数名 システムDBが定義されていない(pgpool-IIモードでない、かつクエリキャッシュがオ フの)場合に実行すると、BackendErrorと表示され、終了コード12で終了します。 pcp_detach_node
書式: pcp_detach_node _timeout_ _host_ _port_ _userid_ _passwd_ _nodeid_ pgpool-IIのノードを切り離します。 pcp_attach_node
書式: pcp_attach_node _timeout_ _host_ _port_ _userid_ _passwd_ _nodeid_ pgpool-IIのノードを復帰させます。 pcp_stop_pgpool
書式: pcp_stop_pgpool _timeout_ _host_ _port_ _userid_ _passwd_ _mode_ pgpool-IIを指定されたモードでシャットダウンします。指定できるモードは以下の通 りです。 s - smart モード f - fast モード i - immediate モード pgpool-IIが起動していない場合はConnectionErrorと表示され、終了コード8で終了し ます。 ※ 現在は fast モードと immediate シャットダウンの処理に区別はあり ません。命令を送った時点でクライアントがいる・いないに関わらず シャットダウン処理を即座に行います。 pcp_recovery_node
書式: pcp_recovery_node _timeout_ _host_ _port_ _userid_ _passwd_ _nodeid_ pgpool-IIのノードをデータを再同期させた上で復帰させます。 終了ステータスPCPコマンドは正常に処理を終了した場合、ステータス'0'で終了します。エラーが起 きた場合は以下のステータスにより終了します。 UNKNOWNERR 1 不明なエラー EOFERR 2 EOFエラー NOMEMERR 3 メモリ不足 READERR 4 サーバからのデータ読み込みエラー WRITEERR 5 サーバへのデータ書き込みエラー TIMEOUTERR 6 タイムアウト INVALERR 7 PCPコマンドへの不正なオプション CONNERR 8 サーバ接続エラー NOCONNERR 9 接続が存在しない SOCKERR 10 ソケットエラー HOSTERR 11 ホスト名解決エラー BACKENDERR 12 サーバでのPCP処理エラー。存在しないプロセスIDの情報を取 得しようとした場合など AUTHERR 13 認証エラー 内部情報pgpool-IIバージョン 2.0 以降では、1.x バージョンと比べ大幅な改良が加えられています。 1.x バージョンの情報とは互換性がないので注意してください。 パラレル実行エンジンpgpool-IIにはパラレル実行エンジンが組み込まれています。 このエンジンは、パラレルモードのときに、各ノードに同じクエリを問い合 わせ、ノードの応答順に結果をフロントエンドに送信するエンジンのことを 指します。 クエリ書き換えパラレルモードでpgpool-IIが行うクエリ書き換えについて説明します。 パラレルモードでは、クライアントが送信した検索系(SELECT処理)の問い合わせは、大きく分けて以下の 2 つの処理を行います。
これら2つの処理について順に説明致します。 クエリの解析はじめに クライアントが送信した検索系の問い合わせは、SQLパーサを通してからシステムDBに登録されている情報を もとにクエリ解析を行います。クエリの解析には実行ステータスの遷移で評価しています。 ここで実行ステータスというのは、あるデータの集合が何処で取得または処理できるのか判断するものです。 例えば、pgpool_catalog.dist_defテーブルに登録されているテーブルのデータ集合全体は、データが分割さ れているのですべてのノードから取得する必要があります。逆に、pgpool_catalog.replicate_defテーブル に登録されているテーブルのデータ集合全体は、すべてのノードから取得するのではなく、いずれかのノード から取得すれば十分です。 ここですべてのノードで処理する必要がある状態を P 状態、一つのノードで処理する必要がある状態を L 状 態として定義します。 もう一つ、特別な状態として S 状態があります。これは、すべてのノードから取得した全データに対して処理 を行ったときの状態のことを示します。 例えば、ソート処理です。pgpool_catalog.dist_defテーブルに登録されているテーブルのデータに対するソー ト処理は、すべてのノードからデータを取得した後に実行する必要があります。 検索系クエリは、以下の処理順に解析され、実行ステータスが遷移していきます。 実行ステータスが遷移していく過程で S 状態となると、以降の処理は必ず S 状態となります。 そして最後のSELECTの最終実行ステータスの状態により、何処のDBで処理されるかが 決定します。
SELECTの最終実行ステータスと処理される場所との関係は、以下の通りです。
またサブクエリに対しても上記のルールが適応されます。 以下の単純なクエリでは、p1-tableがシステムDBのpgpool_catalog.dist_defテーブルに登録されている場合、つまりデータの分割が 行われている場合には、サブクエリの最終実行ステータスが P となり、その結果サブクエリの呼び出し元である SELECT の実行ステータスも P となります。 SELECT * FROM (SELECT * FROM P1-table) as P2-table; 次に具体的に実行ステータスがどのように遷移するのか説明します。 まず2. From句の実行ステータス から説明します。 FROM 句の実行ステータス 検索系クエリ(SELECT)は FROM 句によりデータの集合を定義します。FROM句から構成せれるデータ集合は P 状態, L 状 態、または S 状態を取ります。FROM句に指定しているテーブルが一つの場合には、単純にテーブルの実行ステータスが FROM句から構成されるデータ集合全体の実行ステータスとなります。FROM句に複数のテーブル、又はサブクエリがある場合 には、結合方法によって以下のように実行ステータスが決定します。
以下の例では、P1-tableが P 状態のテーブルでL1-table,L2-tableが L 状態のテーブルだとします。 すると上記の表により、P1-table (左)とL1-table (右) が結合し P 状態となり、さらに P 状態と L 状態のL2-tableが結合してFROM句の実行ステータスは P 状態となります。 SELECT * FROM P1-table,L1-table,L2-table; TARGETLIST と WHERE句の実行ステータス 基本的なクエリでは、FROM 句と同じ実行ステータスを継承します。 しかし、TARGETLIST と WHERE句の実行ステータスは、以下の場合に変化します。
サブクエリの最終実行ステータスが P 状態、または、S 状態の場合には、TARGETLIST、WHERE句の実行ステータス は、S 状態となります。 下記の例では、サブクエリで使われているテーブルが、P 状態の場合には、サブクエリの最終実行ステータスは P 状態となります。そのため L1-tableの実行ステータスに依存せずに、WHERE句の実行ステータスは S状態となり 、このクエリの実行場所はシステムDBとなります。 SELECT * FROM L1-table where L1-table.column IN (SELECT * FROM P1-table); FROM 句が P 状態の場合、かつ、TARGETLISTに集約関数がある場合は、データを取得後に集計する必要があるため、S状態 に遷移します。 また、特定の条件の下では、集約関数による最適化が行われます。 FROM句で定義したテーブル、サブクエリには存在しないカラムがWHERE句に使われている場合があります。これは以下のような相関サブクエリ内で発生します。 SELECT * FROM L1-table FROM L1-table.col1 IN (SELECT * FROM P1-table WHERE P1-table.col = L1-table.col1); 上記のサブクエリに使われている L1-table.col1は、L1-tableを外部参照しています。この場合にサブクエリのWHERE句の実行ステータスは S 状態となります。 GROUP BY 句、HAVING 句、ORDER BY 句、LIMIT OFFSET 述語の実行ステータス WHERE句の実行ステータスが P 状態の場合に、GROUP BY , HAVING 句、ORDER BY 句、LIMIT OFFSET 述語があるとS状態に遷移します。 GROUP BY句が存在しないクエリはWHERE句の実行ステータスを継承します。また、HAVING句が無い場合にはGROUP BY 句の実行ステータスを継承します。 ORDER BY 句、LIMIT OFFSET 述語も同様です。 UNION、EXTRACT、INTERSECTが使われている場合 UNION、EXTRAT、INTERSECTが使っているクエリは左側のSELECT文と右側のSELECT文の最終実行ステータスに依存します。 左側と右側のSELECT文の最終実行ステータスが共に L 状態の時には、L 状態となります。 また、左側と右側のSELECT文の最終実行ステータスが共に P 状態、かつUNION ALLの場合には P 状態となります。 その他の組み合わせの場合には、S状態となります。 SELECTの最終実行ステータスの取得 実行ステータスがすべて L 状態の場合にはL状態、すべて P 状態の場合には、P 状態となります。 それ以外は、S 状態となります。 L 状態の場合には、pgpool.confのloadbalance_modeがtrueの場合には負荷分散され、それ以外の場合にはMASTERに問い合わせを行います。また、P 状態の場合には、パラレル実行エンジンを使って並列処理が行われます。S 状態の場合には、次のフェーズであるクエリ書き換えを行います。 クエリ書き換えクエリの解析フェーズで取得した実行ステータスを使ってクエリの書き換えを行います。 例として P 状態の P1-table と L 状態の L1-table を使ったクエリで説明します SELECT P1-table.col, L1-table.col FROM P1-table,L1-table where P1-table.col = L1-table.col order by P1-table.col; このクエリでは ORDER BY 句があるため S 状態となり、FROM句、WHERE句、TARGETLISTは P 状態となります。 このようなクエリでは以下のように書き換えられます。 SELECT P1-table.col, L1-table.col FROM dblink(select pool_parallel(SELECT P1-table.col, L1-table.col FROM P1-table,L1-table where P1-table.col = L1-table.col)) order by P1-table.col; ここでdblinkはpgpool-IIに問い合わせを送信します。また、pool_parallelは引数のクエリをパラレル実行エンジンをにわたす関数です。なお、上記はあくまでイメージであり実際に実行可能なクエリではありません。 上記の例のように、FROM句、WHERE句、TARGETLISTがすべて P 状態の場合には、FROM句、WHERE句、TARGETLISTをまとめて並列処理を行います。 次の例を見てみます。 SELECT L1-table.col FROM L1-table WHERE L1-table.col % 2 = 0 AND L1-table.col IN (SELECT P1-table FROM P1-table) ; この例では、FROM 句は L 状態、TARGETLISTも L 状態、WHERE句は P 状態のサブクエリを持っているため S 状態となります。 これは以下のように書き換えが行われます。 SELECT L1-table.col FROM dblink(SELECT loadbalance(SELECT L1-table.col FROM L1-table WHERE L1-table.col % 2 = 0 AND TRUE)) WHERE L1-table.col %2 = 0 AND L1-table.col IN ( SELECT P1-Table FROM dblink(select pool_parallel(SELECT P1-table FROM P1-table)) ) ; ここで、pool_loadbalanceはクエリをいずれかのノードに送信する関数です。 集約によるクエリ書き換え 集計を行うクエリ(集約関数、GROUP BY )は各ノードに計算させ、システムDBで 再集計を行うことにより、システムDBの負荷を減らしパフォーマンスも向上 します。 まず、最初にpgpool-IIが実際に行うクエリの書き換えを見てみます。 FROM 句が P 状態で count(*) を使ったクエリは、以下のように書き換えが行われます。 select count(*) from P1-table; -> クエリ書き換え SELECT sum(pool_c$1) as count FROM dblink(select pool_parallel('select count(*) from P1-table')) AS pool_$1g (pool_c$1 bigint); 各ノードでcount(*) を計算した後に、システムDBで集計(sum)をすることによ り、目的が達成できます。 上記のようなクエリ書き換えが行われる条件は以下の場合です。
例) select P1-table.col,L1-table.col,count(*),avg(P1-table.col) from P1-table,L1-table wehre P1-table.col %2 = 0 group by P1-table.col,L1-table.coli having count(*) < 100 パラレルモードの注意事項パラレルモードでは、クエリの解析の際にカラム名とタイプが必要になります。そのため、サブクエリのTARGETLISTに式、関数を使っている場合には別名と型名をキャストでつける必要があります。式、関数に型のキャストがない場合には、text型として処理されますので注意してください。 なお、集約関数の場合でかつ集約によるクエリ書き換えが行われる場合には、countはbigint型、sumはnumeric型となります。min,maxの場合には、引数が日付型の場合には日付型として計算され、それ以外はnumericとして計算されます。avgはsum/countとして処理されます。 パラレルモードのパフォーマンスについて
SELECTの最終実行ステータスとパフォーマンスのおおよその目安は以下のとおりです。
チュートリアルpgpool-IIのチュートリアルはここにあります。是非ご覧下さい。 リリースノート2.2.5 (urukiboshi) 2009/10/4概要このバージョンでは、2.2.4以前の色々なバグが修正されています。 バグ修正
2.2.4 (urukiboshi) 2009/8/24概要このバージョンでは、2.2.3以前の色々なバグが修正されています。 バグ修正
2.2.3 (urukiboshi) 2009/8/11概要このバージョンでは、2.2.2以前の色々なバグが修正されています。 バグ修正
2.2.2 (urukiboshi) 2009/5/5概要このバージョンでは、2.2.1以前の色々なバグが修正されています。 とりわけ、pgpoolがクライアントとの間でデータのやり取りをしている最中に、pgpoolのクライアントが終了(X)パケットをpgpoolに送信せずに終了した場合に起る可能性があります。 このバグは過去のすべてのpgpoolに存在しています。 バグ修正
2.2.1 (urukiboshi) 2009/4/25概要このバージョンでは、2.2の色々なバグが修正されています。 バグ修正
2.2 (urukiboshi) 2009/2/28概要このバージョンでは、SERIALデータの扱いとオンラインリカバリに改良が行なわれています。 また、トランザクション分離レベルがシリアライザブルの場合に、DBノード間でデータの一貫性がなくなる可能性がある問題、クエリのキャンセルができない問題が修正されました。 新機能
互換性
バグ修正
2.1 (inamiboshi) 2008/7/25新機能
互換性
修正全般
レプリケーション
マスタースレーブ
パラレルクエリ
2.0.1 (hikitsuboshi) 2007/11/21
2.0 (hikitsuboshi) 2007/11/16互換性
全般
レプリケーション
パラレルクエリ
1.3 (sohiboshi) 2007/10/23
1.2.1 (tomoboshi) 2007/09/28
1.2 (tomoboshi) 2007/08/01
1.1.1 (amiboshi) 2007/06/15
1.1 (amiboshi) 2007/05/25
1.0.2 (suboshi) 2007/02/13
|