レベル: 中級 丸山 孝之, 情報マネジメント技術/ソフトウェア開発研究所 , IBM
2006年 11月 24日 CM接続プールはコネクション・プールとも呼ばれており、広く使われています。接続の再利用方法などはサンプルでは提供されていないため、この方式について詳しく解説します。
接続プールの種類
接続プーリングとして、アプリケーション起動時にあらかじめ接続したセッションをプールしておき、セッションの取得・返却を再利用する機能があります。「セッションをどこに保存するか」と「どのプログラムがその情報を管理するか」によっていくつかの種類がありますが、接続プーリングすることのできるオブジェクトを接続プールといいます。
接続プールを使用する利点には次の項目が挙げられます。
- 既存の接続を再利用することで接続時のオーバーヘッドを削減する
- 接続数の管理が容易になる
接続処理にはリソースやパフォーマンスの観点で大きなコストがかかります。そのため事前に接続処理を行っておくことでサーバーを強化することなく有効なリソース配分が可能になります。
IBM DB2 Content Manager(以下Content Managerと略す)では3種類の接続プールを利用することができます。
リスト1.接続プールの種類
| 種類 | CM接続プール(DKDatastorePool) | WAS接続プール | CM Beans接続プール(CMBConnectionPool) |
|---|
|
対象
| DKDatastoreXXXオブジェクト | JDBCデータ・ソース | CMBConnectionオブジェクト | |
特徴
|
- コードで実装
- XXXは特定のコンテンツ・サーバー名(ICM,OD,...)
- APIで制御できる
|
- WASとcmbpool.iniで設定
- CM接続プールとBeans接続プールと併用可能
|
- コードで実装
- 多数のユーザーが共通のユーザーを使用して文書を参照するようなWebアプリに最適。
- CM接続プールで実装されている。
|
今回は、これらの接続プールのうちContent Manager独自の実装によるDKDatastorePoolについて、その使用方法などを紹介します。その他の接続プールに関しては参考文献のマニュアルに使用方法が記載されていますので参照してください。
CM接続プールとはDB2 Information Integrator for Content (以下II4Cと略) で提供されているDKDatastorePool クラスによる接続プールです。C++またはJavaのクラス・ライブラリーとして提供され、データストア・オブジェクトを管理します。1つの接続プールは、1つのデータストア・タイプを扱います。Content Manager V8の場合は DKDatastoreICMというクラス名になります。
Content Managerのデータストア・オブジェクトについての詳しい説明は別の機会にするとして、簡単に復習してみます。 DKDatastoreICMのオブジェクトはコンテンツ・サーバーとの接続を表し、接続の管理、トランザクションの提供、およびサーバー・コマンドの実行を行います。Content ManagerのアプリケーションがCMのデータにアクセスするためにはDKDatastoreICMを使用してデータベースへの接続を開始する必要があります。 このDKDatastoreICMをプールするクラスがDKDatastorePoolです。
リスト2. DKDatastorePoolの概要
CM接続プールのサンプル
CM接続プールにはセッションID付きとセッションID無しの2種類があります。 それぞれ専用のメソッドが用意されていて、引数と返りの型が異なります。どのような違いがあるか通常の接続と比較してみます。 完全なサンプル・コードとその実行環境については参考文献をご覧ください。
リスト3. 通常の接続例 (Java版)
DKDatastoreICM dsICM = new DKDatastoreICM(); // データストア・オブジェクトの作成
dsICM.connect("icmnlsdb",userId,password,""); // データベースへの接続
dsICM.disconnect(); // データベースからの切断
dsICM.destroy(); // データストア・オブジェクトの破棄
|
接続先の接続名(主にDB名)と接続するユーザー名、パスワードを使用して接続します。 これに対してCM接続プールでは事前に接続プールの初期化作業を行います。
リスト4. CM接続プールの初期化例 (Java版)
DKDatastorePool dsrICM = new DKDatastorePool(“com.ibm.mm.sdk.server.DKDatastoreICM”)
// プールするデータストア・オブジェクトのクラスを使用して初期化
dsrICM.setDatastoreName("icmnlsdb"); // 接続名
dsrICM.setMaxPoolSize(10); // 最大接続プール数
dsrICM.setMinPoolSize(10); // 破棄されない最小接続プール数
dsrICM.setConnectString(“”); // 接続オプション文字列
dsrICM.setTimeOut(0); // 未使用のプールを保持する時間(分)0は制限無し
dsrICM.initConnections(userId, password, 2); // 接続ユーザー名、パスワード、初期接続数
|
リスト5. CM接続プールの初期化例 (C++版)
DKDatastorePool dsrICM = new DKDatastorePool(“ICM”, “libcmbicmfac816”);
// プールするデータストア・オブジェクトのクラスを使用して初期化
dsrICM->setDatastoreName(“icmnlsdb”); // 接続名
dsrICM->setMaxPoolSize(10); // 最大接続プール数
dsrICM->setMinPoolSize(10); // 破棄されない最小接続プール数
dsrICM->setConnectString(""); // 接続オプション文字列
dsrICM->setTimeOut(0); // 未使用のプールを保持する時間(分)0は制限無し
dsrICM->initConnections(userId, password, 2); // 接続ユーザー名、パスワード、初期接続数
|
プールするデータストア・オブジェクトのDLL名を使用して初期化します。initConnections()で使用するユーザー名はセッションID無しとしてあらかじめ初期化するプール数を指定します。 例では最大接続数のプールサイズを10に設定していますが、接続数には注意してください。実際のデータベースへの接続上限数を上回ることはできません。 C++版では初期化で使用するプールするクラス名が使用するプラットフォームで異なります。例で使用しているのはAIXの例ですが、Windowsの場合、” cmbicmfac817.dll” または” cmbicmfac8171.dll”になります。 接続プール作成後は初期化で使用した値を変更することができませんので、変更するためには接続プール自体を再作成することになります。
初期化後にデータストアの取得(getConnection)と返却(returnConnection)を行います。次のコードは、「セッションID付き」の場合と「セッションID無し」場合での接続例です。
リスト6. 接続プールを使用した接続と切断例(セッションID無し)(Java版)
DKDatastoreICM dsICM = (DKDatastoreICM)dsrICM.getConnection(userId, password); // ログイン
dsrICM.returnConnection(dsICM); // 取得したデータストア・オブジェクトをプールに返却
|
リスト7. 接続プールを使用した接続と切断例(セッションID付き)(Java版)
DKDatastoreSession dssICM = dsrICM.getConnection(userId, password, sessionId); // ログイン
DKDatastoreICM dsICM = (DKDatastoreICM)dssICM.getDatastore(); // データストア・オブジェクトの取り出し
dsrICM.returnConnection(dssICM); // 取得したセッション・オブジェクトをプールに返却
|
セッションIDを使用しない場合はセッションの管理をContent Managerが行います。前回使用したデータストア・オブジェクトと同一のオブジェクトが使用できることは保証されていません。 セッションIDを指定した場合はセッション・オブジェクトを介してデータストア・オブジェクトを取得する必要があります。その代わり、前回取得したセッションを明示的に指定するので同一セッションを使用することができます。
接続数が多くなりリソースの再利用度が高くなっても、なるべくセッションIDに関連したオブジェクトを返すように工夫されているので、最小限のリソースで接続プールを管理することができます。同じデータストア・オブジェクトを再利用できるメリットは、データベースに接続したセッションが毎回同じであるため、データベースでのリソースの再利用が期待できる点にあります。例えば同じSQL(Content Managerの場合はXQuery)を反復利用するような場合にメリットを享受できる点が挙げられます。 CM接続プールでのセッションの接続はそのままデータベースのセッションに結び付けられているため、デバッグやデータベースのチューニングが容易になるメリットもあります。
これらのCM接続プールの違いについては以下のようになります。
リスト8. セッションID付きと無い場合の比較
|
| セッションID無し | セッションID付き |
|---|
| メリット | Content Managerがセッション管理を行うのでアプリケーションは管理しなくて良い。 | 毎回同じセッションが保証される。 |
|---|
| デメリット | 前回と同じセッションが保証されない。 | アプリケーションにてセッションIDの管理が必要である。 |
|---|
セッションIDの管理はアプリケーションで工夫する必要がありますが、新規に接続する場合はセッションIDに何を指定するのでしょうか?答えとしては、新規に接続するにはセッションIDに“0”、または今まで使用したことのないIDを指定します。 どちらの場合でも機能的には新規にオブジェクトが作成されてセッションIDも作られるのですが、内部的には動作が異なります。 0を指定するとMaxPoolSizeまでは新規にセッションを作成します。しかし0以外の場合はすでに内部で保持されている未使用のセッションを割り当てるのです。これらの動きはメソッドの説明で理解することができます。この違いは一見同じに見えますが、すでに使用されたセッションの再利用を行う方が有利な場合が多いようです。添付のサンプル・コードでは新規にオブジェクトを作成する時に再利用することを利用しています。
メソッドの説明
セッションID付きとセッションID無しでは使用するメソッドが異なりますが、内部的にはどのように管理されているのか見てみます。 CM接続プールクラスの内部にはそれぞれ使用中のプールと未使用のプールがあります。それぞれgetConnection()/returnConnection()でデータストア・オブジェクトの状態が変更されます。以下にgetConnectionしたときのCM接続プールのシーケンスを追ってみます。どのように内部で保持しているセッションIDを検索するのかはマニュアルに記述されています。
リスト9. DKDatastorePoolの状態遷移概略
接続プールのセッションはID付きで作成されたものと、ID無しで作成されたものでは動作が異なります。 セッションID付きで作成されたものは、returnConnection()で未使用状態になっても、使用時についていたIDが保存されています。(A-a) このセッションは0より大きな値のIDを指定してgetConnection()を実行した場合に、同じセッションが再使用されます。それに対して、セッションID無しで作成され未使用になった接続(B-a)は、セッションIDを指定されなかった場合だけではなく、条件によっては指定された場合も再使用されることがあります。 なお、セッションの最大数はセッションID付きのものとセッションID無しのものを合わせた数になります。
セッションIDに 0を指定した場合
- 新規にセッションを作成して接続します。
- MaxPoolSizeを超える場合は、新規にセッションを作成しないでセッションID無しで未使用(B-a)から取得してセッションIDを割り当てます。
リスト10. DKDatastorePoolの状態遷移概略 (SessionID = 0の時)
セッションIDに 0より大きな値を指定した場合
- セッションID付きで未使用 (A-a) から同じセッションIDがないかどうかを検索します。同じIDが見つかれば、そのデータストア・オブジェクトを返します。
- セッションID無しで未使用 (B-a) から取得してセッションIDを割り当てます。
- どちらの未使用にも空きがないときは新規にセッションIDを作成して接続します。
- 新規に作成するときにMaxPoolSizeを超えてしまう場合は、新規には作成しません。その代わり、セッションID付きで未使用(A-a) の中からセッションIDが異なるものを返します。この場合は、使用されていない中で、最も古いデータストア・オブジェクトを返します。
リスト11. DKDatastorePoolの状態遷移概略 (SessionID > 0の時)
以上のようになるべく有効にリソースを使用していることがわかります。
サンプルの説明
サンプルを説明する前にContent Managerの特性として接続直後の検索が遅いという問題点を説明しておきます。これは接続後の最初の検索時にContent Managerで使用しているアイテム・タイプの属性名や属性グループなどをローカルのメモリーにキャッシュするために起きる問題です。これが場合によっては数秒要する場合があります。 新規に接続を作成する場合は毎回接続後に発生するため避けることができませんが、接続プールを使用する場合は、CM接続プールの作成時にキャッシュしておくことができますので、その方法も併せて紹介します。リスト12は実際の検索は行わずに、キャッシュ処理だけを事前に実行するための擬似コードです。
リスト12. データオブジェクトにキャッシュさせる方法(Java版)
DKDatastoreDefICM dsDef = (DKDatastoreDefICM)dsICM.datastoreDef();
dsDef.listAttrs(); // ユーザー定義属性の取得
dsDef.listAttrGroup(); // 属性グループ定義の取得
dsDef.listXdoClassifications(); // XDO区分定義の取得
dsDef.listComponentTypes(); // ユーザー定義構成要素タイプの取得
dsDef.getDescription((short)13, 1, "ENU"); // ディスクリプションの取得
|
例えばアプリケーションが起動して接続プールを初期化する時に、これらをキャッシュに読み込んでおくことで、接続直後の最初の検索でユーザーを待たせることがなくなります。これはデータストア・オブジェクトごとに実行する必要があります。
今までの使用例をサンプル・コードとして添付します。ここでサンプルに使用している新規接続時のセッションIDについて説明します。 事前にデータオブジェクトのキャッシュを使用して未使用プールを先に初期化していますが、この時はセッションID無しで初期化しています。実際に使用する時はセッションIDを指定するのですがJava版ではInteger.MAX_VALUEをC++版ではLONG_MAXを指定しています。新規接続なので以前に使用されていないIDということで、これらの閾値を使用しています。それぞれ int型 (またはlong型) の最大値になります。通常の使用では問題ないと思います。
付属のC++用MakefileはCM V8.3用です。サンプル・コード自体はCM8.2でも動作しますが、リンクするライブラリーが異なるので注意してください。詳しくはII4C付属のREADME_SAMPLES_CPP_ICM.txtをご覧ください。 もし動作しない場合は、II4C付属のサンプルがコンパイル/実行ができることを確認してください。
終わりに
本稿では、DKDatastorePoolクラスの使い方について説明してきました。付属のドキュメントにも説明が少なく、そのために敬遠している人もいるのではないでしょうか。使い方はそれほど難しいものではないことをご理解いただけたのではないでしょうか。 接続プールを使用することでリソースを有効活用できるようになり、パフォーマンスが向上するので使い勝手が一段と良くなるはずです。サンプルのコードとしてC++版とjava版を添付しました。クラス・ライブラリーとしてはC++版もJava版もほとんど同じですので同様に扱うことができます。ぜひ、あなたの作成するアプリケーションに活用してください。 最後に、使用する時は必ず最新のレベルのPTF適応を行った状態でご利用ください。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| DKDatastorePoolTest.zip | DKDatastorePoolTest.zip | 6 KB | HTTP |
|---|
参考文献
著者について  | |  | 丸山 孝之はソフトウェア開発研究所のエンジニアで、Content Managementの分野におけるプロジェクトでの開発・支援業務を担当しています。 |
記事の評価
|