このドキュメントは、PostgresForest5.1を使用して動作するアプリケーション プログラムの設計および開発を行う開発者を対象としています。
本ドキュメントでは、読者がPostgreSQLの管理に関する一般的な知識を持って いることを前提としています。PostgreSQLに関する情報は、 PostgreSQLのドキュメント (http://www.postgresql.jp/document/) 等を 参照してください。
PostgresForest5.1はJDBCに準拠したAPIを提供しています。実装しているAPIは JDBC1, JDBC2で定義されたもののみとなっています。
サポートされるAPIの詳細は、API一覧をご確認ください。
PostgresForest5.1はJDBCドライバとして実装されているため、使用する際には 一般的なJDBCドライバを呼び出すための処理を行い、java.sql.Connectionの オブジェクトを取得する必要があります。
PostgresForest5.1を使用するJavaアプリケーションは、以下のものを用意する 必要があります。
クラスタ環境については、管理者ガイドを参照してください。
PostgresForest5.1は、内部でPostgreSQLのJDBCドライバを使用します。そのため PostgresForestをJavaアプリケーションから使用する際には、PostgresForestの JDBCドライバと、PostgreSQLのJDBCドライバを、クラスパスに加える必要があります。
PostgresForest5.1は、 postgresql-8.3-603.jdbc3.jar を使用して開発・ 試験が実施されています。
PostgresForest5.1が提供するJDBCドライバをロードする際に指定する「java.sql.Driver」 インターフェースの実装は、以下の完全修飾名となっています。
Driverクラスの完全修飾名:
org.postgresforest.Driver
Driverクラスのロードサンプル:
Class.forName("org.postgresforest.Driver");
PostgresForest5.1のConnectionオブジェクトを取得するために必要なJDBC 接続文字列は、次の情報を含める必要があります。
設定内容 | 書式 | 備考 |
---|---|---|
接続先サーバ情報 | [サーバ名]:[ポート番号] | 2組必須 |
ユーザDB名 | PostgreSQLのDB名書式(英小文字および数字のみ) | 必須 |
コンフィグレーション名 | 文字列(英数字のみ) | 省略可(省略時はDEFAULT) |
接続文字列は以下のようになります。
書式:
jdbc:postgresforest://<接続先サーバ情報>,<接続先サーバ情報>/<ユーザDB名>[?configid=<コンフィグレーション名>]
設定方法サンプル:
Connection con = DriverManager.getConnection( "jdbc:postgresforest://host1:5432,host2:5432/sampledb;configid=sample_config", "forest", "passwd");
接続文字列の「configid=」を変更することで、コンフィグレーションを 使い分けることができます。例えば、オンラインのWebトランザクションの場 合には縮退までのタイムアウトを短く設定し、バッチ処理の場合には縮退まで のタイムアウトを長時間にする、などの切り替えが可能となります。
PostgresForestのConnectionオブジェクトを取得した後は、一般的なJDBC コネクションとほぼ同様に使用することができます。
但し、SQLを実行するAPIの種類によって、片方のPostgreSQLに対してのみSQLを 発行するのか、両方のPostgreSQLに対してSQLを発行するのかが異なるため、 アプリケーション開発者は適切にAPIを選択する必要があります。
- Statement#executeQuery
片方のPostgreSQLに対してのみ問い合わせが行われます。負荷分散される ため、参照系のSQLを実行したい場合に有効です。但し、実行するSQLが SELECT ... FOR UPDATE の場合のみ、両方のPostgreSQLに対し問い合わせ が実施されます。
※注意:更新系のSQLを本APIを使って実行すると、片方のPostgreSQLのみ が更新されてしまい、不整合が生じます。
- Statement#executeUpdate
- 両方のPostgreSQLに対して問い合わせが行われます。更新系のSQLを実行 したい場合に有効です。
- Statement#execute
- 両方のPostgreSQLに対して問い合わせが行われます。
SELECT文中でストアドプロシージャを呼び出す場合などは、execute()で実行 するか、executeQuery()でSQLに「FOR UPDATE」を付加すると、双方の PostgreSQLでプロシージャが実行されます。
PostgresForest5.1を用いたアプリケーションを開発する場合、基本的には PostgreSQLのJDBCを使用したアプリケーションと同様に開発をすることができ ますが、一部アプリケーション開発者が留意すべき、PostgresForest5.1特有 の制約等があります。
PostgresForest5.1は、原則としてautocommitモードをfalseとして使用する 必要があります。:
con.setAutoCommit(false);
PostgresForestはautocommitモードがtrueの場合、双方のDBノード間の整合性 を保つことができません。これは、ほぼ同時に2つのアプリケーションが1つ のデータに対して更新を行った場合に、それらを互いに抑制する手段がない ためです。それぞれのDBノードへ到着するSQLの順序によっては、データの 整合性が崩れる結果となってしまいます。
autocommitモードがfalseの場合、トランザクションごとにロックを取得する という形で、ほぼ同時に発生した更新を抑制することができ、双方のDBノード 間で不整合が起きることを防ぐことができます。
上記のようにautocommitモードをfalseとしてSQLを実行すると、複数のアプリ ケーション(あるいは複数のスレッド)が同一のレコードの更新を行う場合に ノード間デッドロックが発生する場合があります。ノード間のデッドロックが 発生すると、アプリケーションに制御が戻らず、そのアプリケーションは処理 を継続することができなくなります。
対処方法としては2つになります。
SQL文にNOWAITをつけて実行すると、ロックの取得に失敗した場合、アプリケー ションには即座にエラーが返ります。あるいはPostgreSQLでロック待ちタイム アウトのパッチを適用している場合、ロックの取得に時間がかかった場合に エラーが返ります。(ロック待ちタイムアウトのパッチ適用については、管理 者ガイド「PostgreSQLソースへのパッチ適用・コンパイル」を参照してください)
これらの対処を行わない場合、PostgresForestのlocal_config設定の値によって 異なりますが、アプリケーションが停止する、JDBCオブジェクトがリークして いく、縮退が頻繁に発生する、等々運用が困難な問題が発生します。
PostgresForestでアプリケーションにエラー(SQLException)が返却される ケースは、
などがあります。
これらのエラーが発生した場合には、必ずアプリケーション側からトランザク ションを明示的にロールバックしてください。特にアプリケーションSQLの エラーやノード間デッドロックが発生した場合に、明示的にロールバックを せずにそのまま処理を継続する・明示的にロールバックをせずにコネクション を使いまわす、などを行うと、各DBノード間で不整合が発生する可能性があり ます。
特定のレコードへのUPDATEが集中すると、更新処理に必要なロックをすべての ノードで取得できない場合が発生し、NOWAITでのエラー、あるいはノード間 デッドロックエラーが多発する原因となります。 PostgresForestでは複数ノードにおいて同時にロックを獲得しなければならな いため、同一レコードへのロックの操作が集中すると、ノード間のデッドロッ クあるいはロック獲得の失敗となります。そのため、同一レコードへのUPDATE 処理が集中する状況を可能な限り避ける必要があります。
アプリケーション設計(テーブル設計)時には、特定レコードへのUPDATEが多発 するような設計を可能な限りを回避することに加え、処理のリトライなどにより エラーが起きても問題ないアプリケーションの作りとすることが重要です。
PostgresForestでは、複数のDBノードにSQL文を発行することで高可用性およ び分散処理を実現します。
current_timestampやnowなどの関数は、アプリケーションから使用されると、 それぞれのDBサーバ上で自動的に値が生成されます。これらの値は、各DBサーバ 上で必ずしも同じ値になるとは限らず、結果として一貫性の取れない状況が発生 する可能性があります。
PostgresForestでは、複数のサーバ上で自動生成されるこれらの関数の整合性を 保つ方法を提供していませんので、これらの関数を使う場合には、アプリケー ション側で何らかの対処を行う必要があります。
SERIAL型、OID型などのデータ型は、それぞれのDBサーバ上で自動的に値が生成 されます。これらの値は、各DBサーバ上で必ずしも同じ値になるとは限らず、 結果として一貫性のとれない状況が発生する可能性があります。
PostgresForestでは、複数のサーバ上のこれらのデータ型の関数の整合性を保つ 方法を提供していませんので、これらの関数を使う場合には、アプリケーション 側で何らかの対処を行う必要があります。
PostgresForestにおいては上記のように、SERIAL型がDBサーバごとに異なる値 となってしまう場合があるため、一意な値を取得する目的には使用できません。 一意な値を取得するには、採番用のテーブルを作成する方法が挙げられます。
採番テーブル作成方法:
CREATE TABLE seq ( seqname VARCHAR(16) PRIMARY KEY, seqval INTEGER ); INSERT INTO seq VALUES ( 'seq0', 0 );
採番テーブルに対して、autocommit = off の状態で次のようなSQLを1つの トランザクションブロックで実行することで、シリアル型に相当する値を取得 できます。
シーケンス更新用SQL(ロック待ちタイムアウトパッチを適用している場合):
SELECT seqval FROM seq WHERE seqname = 'seq0' FOR UPDATE; UPDATE seq SET seqval = seqval + 1 WHERE seqname = 'seq0';
シーケンス更新用SQL(ロック待ちタイムアウトパッチを適用していない場合):
SELECT seqval FROM seq WHERE seqname = 'seq0' FR UPDATE NOWAIT; UPDATE seq SET seqval = seqval + 1 WHERE seqname = 'seq0';
エラーが発生した場合には、アプリケーションからリトライを行う必要があり ます。
JDBCドライバの仕様では、1つのコネクションオブジェクトから生成された オブジェクトを、複数のスレッドから使用することができるか否かは既定され ていません。
PostgresForestも、1つのコネクションオブジェクトから生成されたオブジェ クトを、(クライアントサイドからの適切な同期化処理無しに)複数スレッド からアクセスされることを想定しておりません。
唯一の例外として、 Statement#cancel() のみは複数スレッドからの使用を 想定して実装されております。
PostgreSQLの独自SQLとして「SET」句が存在しますが、SET句のようにコネク ションの状態に影響を与えるSQLを使用しているアプリケーションの場合、 オンラインリカバリツールを使用した際に問題が生じる可能性があります。
オンラインリカバリツールは、リカバリ処理の中でアクセス停止してしている PostgreSQLに対してコネクションを再生成します。SET句等でコネクションの 状態を変更している場合、新規に生成したコネクションと以前から存在している コネクションとで状態が異なってしまう場合があります。そのため、SET句の ようなコネクションの状態に影響を与えるSQLを使用しているアプリケーション に対しては、オンラインリカバリツールを使うことはできません。
オンラインリカバリツールは、アプリケーションからのデータベーストラン ザクションの新規生成を待機させる形で抑制し、既存のトランザクションが 終了するのを待ちます。ある一定時間内に全てのトランザクションが終了する と、そこでデータベース間の差分を是正します。 ある一定時間内に既存トランザクションが全て終了しなかった場合、リカバリ に失敗することになります。
このため、1つのトランザクションが非常に長いアプリケーションや、 トランザクションをプーリングしてしまうようなタイプのアプリケーションで は、トランザクションがなくなる状態にならないために、リカバリに常に失敗 してしまう可能性があります。