12 C++アプリケーションのパフォーマンスの最適化
この章では、OCCIカスタム・アプリケーションのパフォーマンスの向上にお薦めする方法を説明します。
この章には次のトピックが含まれます:
12.1 透過的アプリケーション・フェイルオーバーの理解
OCCIの透過的アプリケーション・フェイルオーバー(TAF)により、分散アプリケーションの実行時のデータベース・インスタンス障害の処理におけるOCCIの堅牢性が向上します。サーバー・ノードが使用できなくなると、アプリケーションは他の使用可能なノードに自動的に再接続します。
TAFは、ラウンドトリップ操作中にクライアント・アプリケーションがデータベース・インスタンスが停止中であることを検知した場合に発生します。TAFのために構成されたバックアップ用データベースへの接続を確立します。このバックアップは、Oracle RAC構成の別のノード、ホット・スタンバイ・データベース、または同じデータベース・インスタンス自体であっても構いません。
失敗した接続上のラウンドトリップに責任を持つOCCI/OCI APIは、通常、次のいずれかのエラーを返します。
-
ORA-25401: フェッチを続行できません。
-
ORA-25402: トランザクションをロールバックしてください。
-
ORA-25408: 安全にコールを再実行することはできません。
後続のアプリケーション要求、および再起動が必要な進行中の作業には、新規接続を使用できます。アプリケーションのアイドル接続は影響を受けません。
アプリケーションで透過的アプリケーション・フェイルオーバーを使用する場合、いくつかの設計オプションを検討する必要があります。
-
フェイルオーバー処理に内在する遅延ため、フェイルオーバーが進行中で通常の操作がまもなく再開されることをユーザーに通知するようにアプリケーションを設計できます。
-
フェイルオーバーの開始前に、最初のインスタンスのセッションが
ALTER SESSION
コマンドを受信した場合、このコマンドは2番目のインスタンスでは自動再実行されません。したがって、開発者としては、2番目のインスタンスで
ALTER SESSION
コマンドの再実行を要求する場合もあります。
SESSION
パラメータの変更の追跡は、ユーザーの責任で行ってください。
これらの問題に対応するため、このアプリケーションではフェイルオーバー・コールバック関数を登録できます。フェイルオーバーが発生した後、コールバック関数が、ユーザー・セッションの再確立中に数回呼び出されます。
-
コールバック関数が最初にコールされるのは、Oracleがインスタンスの接続中断を最初に検出したときです。このコールバックは、アプリケーションがユーザーに対して遅延の発生を通知することを目的にしています。
-
フェイルオーバーが正常終了すると、コールバック関数の2番目のコールは、接続が再確立して使用可能になったときに発生します。このとき、クライアントで
ALTER SESSION
コマンドを再実行し、ユーザーにフェイルオーバーが発生したことを通知できます。SESSION
パラメータへの変更を追跡し、フェイルオーバー完了後に再実行する必要があることに留意してください。フェイルオーバーが異常終了した場合、コールバック関数がコールされ、フェイルオーバーを実行できなかったことをアプリケーションに通知します。
-
フェイルオーバーの最初の試行が正常に終了しないことがあります。フェイルオーバー・コールバックは、フェイルオーバーの再試行が必要なことを示すため、
FO_RETRY
を戻します。関連項目:
-
「OCCI Application Program Interface」の表13-11の
FailOverType
およびFailOverEventType
の定義 -
アプリケーション・フェイルオーバーの詳細は、『Oracle Database Net Services管理者ガイド』を参照してください。
-
12.1.1 透過的アプリケーション・フェイルオーバーの使用方法
TAFを有効にするため、フェイルオーバーのための接続文字列を構成し、Connection
(Environment
、ConnectionPool
およびStatelessConnectionPool
から作成)に登録する必要があります。コールバック関数を登録するには、ConnectionクラスのインタフェースsetTAFNotify()を使用します。
void Connection::setTAFNotify( int (*notifyFn)( Environment *env, Connection *conn, void *ctx, FailOverType foType, FailOverEventType foEvent), void *ctxTAF);
TAFでのConnectionPool
のサポートにはBACKUP
およびPRECONNECT
句は含まれないため、接続文字列ではこれらを使用することはできません。
12.1.2 オブジェクトと透過的アプリケーション・フェイルオーバーについて
透過的アプリケーション・フェイルオーバーは、OCCIのナビゲーション・モデルと連想アクセス・モデル、およびオブジェクト・キャッシュと連携しています。非Oracle RAC設定では、プライマリ・インスタンスとバックアップ・インスタンスで、オブジェクト型定義とオブジェクトOIDが同一であることを確認する必要があります。
フェイルオーバーの後にアプリケーションが「ORA-25402: トランザクションをロールバックしてください。」
のエラーを受信すると、アプリケーションはロールバックを開始し、そのクライアントのオブジェクト・キャッシュを正しくリセットします。フェイルオーバー前にトランザクションが開始されていない場合、アプリケーションはフェイルオーバー後にロールバックを開始し、そのクライアントのオブジェクト・キャッシュのオブジェクトを新しいインスタンスでリフレッシュします。
12.1.3 接続プーリングと透過的アプリケーション・フェイルオーバーの使用
透過的アプリケーション・フェイルオーバー機能がアクティブ化すると、接続プールに生成された接続もフェイルオーバーします。アプリケーション・フェイルオーバー・コールバックは、接続プールから取得した接続それぞれに対して指定する必要があります。それらの接続は、プライマリ・インスタンスが失敗した後に使用されるとフェイルオーバーします。
カスタム接続プール内の接続は、明示的に消去と修復を行う必要があることにご注意ください。1つのプールに500個の接続を持つアプリケーションを考えてみましょう。接続のうち10個はビジー(ラウンドトリップ中)であり、490個はフリーかアイドルです。データベース・インスタンスが失敗した場合、TAFは10個のアクティブな接続に対して機能し、これらの接続に対するクライアントの要求は再起動される必要があります。他の490個の接続がアプリケーションによってそれぞれ取得された場合、TAFが実行されて、OCCIはORA-25401
、ORA-24502
、ORA-25408
のうち1つのエラーコードを返し、ユーザー・リクエストを強制的に再起動します。それまでに接続プール内の10個の接続でTAFがアクティブ化されたという知識を使用することにより、アプリケーションは、490個のアイドル接続を修復するか削除してこれらの接続エラーを回避できます。
OCCIで接続を修復するには、軽量でデータに依存しないデータベース・コールであるConnectionクラスのインタフェースgetServerVersion()を使用して、失敗したインスタンスへの接続上でTAFを開始します。
string getServerVersion() const;
OCCI TAFコールバックで、アプリケーションはカスタム・プール内のアイドル接続に対してgetServerVersion()を呼び出して、これらの接続のフェイルオーバーを開始および完了できます。
例12-1に、TAFコールバックおよび不良接続の修復にOCCIを使用する方法を示します。例では、カスタム・プールのデータ構造やmutexおよび同時実行性制御は示していません。
TAFの動作は、スタンドアロン接続とカスタム接続プールの接続とで同じです。
例12-1 接続プーリングに対してTAFを有効にする方法
#include <occi.h> #include <iostream> #include <time.h> using namespace std; using namespace oracle::occi; //Application custom pool of 3 connections Environment *env; Connection *conn1,*conn2,*conn3; bool conn1free,conn2free,conn3free; bool repairing = false; int taf_callback(Environment *env, Connection *conn, void *ctx, Connection::FailOverType foType, Connection::FailOverEventType foEvent) { cout << "TAF callback for connection " << conn << endl; if(foEvent == Connection::FO_ERROR) { cout << "Retrying" << endl; return FO_RETRY; } if (foEvent == Connection::FO_END) { cout << "TAF complete for connnection " << conn << endl; if (repairing == false) { repairing = true; cout << "repairing other idle connections" << endl; //ignore errors during TAF try { if (conn1free) conn1->getServerVersion(); } catch (...) { } try { if (conn2free) conn2->getServerVersion(); } catch (...) { } try { if (conn3free) conn3->getServerVersion(); } catch (...) { } repairing = false; }//if }//if return 0; //continue failover } main() { try { env = Environment::createEnvironment(Environment::THREADED_MUTEXED); //open 3 connections; conn1 = env->createConnection("hr","password","inst1_failback"); conn2 = env->createConnection("hr","password","inst1_failback"); conn3 = env->createConnection("hr","password","inst1_failback"); //all connections are 'free' conn1free = conn2free = conn3free = true; //set TAF callbacks on all connection conn1->setTAFNotify(taf_callback,NULL); conn2->setTAFNotify(taf_callback,NULL); conn3->setTAFNotify(taf_callback,NULL); //use 1 connection conn1free=false; cout << "Using conn1" << endl; Statement *stmt = conn1->createStatement ("select * from employees"); ResultSet *rs = stmt->executeQuery(); while (rs->next()) { cout << (rs->getString(2)) << endl; } stmt->closeResultSet(rs); conn1->terminateStatement(stmt); cout << "Shutdown and restart the database" << endl; string buf; cin >> buf; Statement *stmt2; try { cout << "Trying a update on EMP table" << endl; stmt2 = conn1->createStatement("delete from employees"); stmt2->executeUpdate(); } catch (SQLException &ex) { cout << "Update EMPLOYEES returned error : " << ex.getMessage() << endl; cin >> buf; } cout << "Done" << endl; env->terminateConnection(conn1); env->terminateConnection(conn2); env->terminateConnection(conn3); Environment::terminateEnvironment(env); } catch(SQLException &ex) { cout << ex.getMessage() << endl; } }
12.2 接続の共有について
この項の内容は次のとおりです。
12.2.1 スレッド・セーフティの概要
スレッドは大きなプロセス内に存在する軽量のプロセスです。複数のスレッドそれぞれで同一コードおよび同一データ・セグメントを共有しますが、プログラム・カウンタ、システム・レジスタおよびスタックはスレッドごとにあります。グローバル変数と静的変数はすべてのスレッドに共通であり、通常、アプリケーション内の複数のスレッドからこれらの変数へのアクセスを管理するには、相互排他メカニズムが必要となることがあります。
スレッドは、1度作成されると他のスレッドとは非同期的に動作します。このため、順序を気にせずに共通のデータ要素にアクセスし、OCCIコールを実行します。このようにデータ要素に共有アクセスを行うため、複数のスレッドによってアクセスされるデータの整合性を維持するメカニズムが必要です。データ・アクセスを管理するメカニズムでは、mutex (相互排他ロック)の形式を使用し、アプリケーション内の共有リソースにアクセスする複数のスレッド間で、競合が発生しないようにします。OCCIでは、mutexはOCCI環境単位で付与されます。
Oracleデータベース・サーバーおよびOCCIライブラリのスレッド・セーフティ機能により、開発者はOCCIをマルチスレッド・アプリケーションで使用でき、次の利点を得ることもできます。
-
複数のスレッドでOCCIコールを実行した場合も、単一のスレッドで連続するコールを実行した場合と同じ結果になります。
-
複数のスレッドでOCCIコールを実行した場合、スレッド間で副作用がありません。
-
マルチスレッド・プログラムを作成しない場合にスレッド・セーフのOCCIコールを実行しても、パフォーマンスは低下しません。
-
複数のスレッドを使用すると、プログラムのパフォーマンスが向上します。パフォーマンスが向上するのは、別々のプロセッサでスレッドを同時実行するマルチプロセッサ・システム、および低速操作と高速操作の間でオーバーラップが発生するシングル・プロセッサ・システムの場合です。
クライアント/サーバー・アプリケーションでは、クライアントをマルチスレッド・プログラムにできますが、それに加え、スレッド・セーフティは3層アーキテクチャまたはクライアント/エージェント/サーバー・アーキテクチャで典型的に使用されます。このアーキテクチャでは、クライアントが関係するのは表示サービスのみです。エージェント(つまり、アプリケーション・サーバー)は、クライアント・アプリケーションのアプリケーション・ロジックを処理します。一般的には、この関係は多対1の関係で、複数のクライアントが同一のアプリケーション・サーバーを共有します。
この3層アーキテクチャのサーバー層は、Oracleデータベース・サーバーです。アプリケーション・サーバー(エージェント)はマルチスレッドをサポートし、各スレッドが別のクライアント・アプリケーションの要求を処理します。Oracle環境では、この中間層アプリケーション・サーバーはOCCIプログラムまたはプリコンパイラ・プログラムです。
12.2.2 スレッド・セーフティの実装
OCCIでスレッド・セーフティを使用するには、最初にアプリケーションをスレッド・セーフなオペレーティング・システム上で実行する必要があります。次に、createEnvironment()
メソッドのモード・パラメータにTHREADED_MUTEXED
またはTHREADED_UNMUTEXED
を指定することで、アプリケーションがマルチスレッド・モードで稼働していることをOCCIに通知する必要があります。たとえば、相互排他ロックをオンにするには、次の文を発行します。
Environment *env = Environment::createEnvironment( Environment::THREADED_MUTEXED);
createEnvironment
にTHREADED_MUTEXED
またはTHREADED_UNMUTEXED
を指定してコールする場合は、createEnvironment
メソッドの後続のコールもすべてTHREADED_MUTEXED
またはTHREADED_UNMUTEXED
のモードを指定して実行する必要があります。
マルチスレッド・アプリケーションがスレッド・セーフなオペレーティング・システム上で稼働している場合、OCCIライブラリでは、アプリケーションのmutexがOCCI環境単位で管理されます。ただし、この機能を上書きし、アプリケーションで独自のmutexスキームを維持することもできます。これを行うには、createEnvironment()
メソッドにTHREADED_UNMUTEXED
のモード値を指定します。
スレッド・セーフでないプラットフォームで稼働するアプリケーションの場合は、createEnvironment()
メソッドにTHREADED_MUTEXED
またはTHREADED_UNMUTEXED
の値を渡さないでください。
シングル・スレッド・アプリケーションの場合は、プラットフォームがスレッド・セーフであるかどうかにかかわらず、createEnvironment
メソッドにEnvironment::DEFAULT
の値を渡す必要があります。これは、モード・パラメータのデフォルト値でもあります。シングル・スレッド・アプリケーションをTHREADED_MUTEXED
モードで稼働すると、パフォーマンスが低下する可能性があります。
OCCIでは非ブロック化モードがサポートされません。
12.2.3 シリアライズについて
アプリケーション・プログラマは、マルチスレッド・アプリケーションでの同時実行性に関して、次の2つの基本的なオプションから選択できます。
-
自動シリアライズ。OTISの透過的メカニズムを使用します
-
アプリケーション対応のシリアライズ。複数スレッドのメンテナンスに付随して発生する問題をプログラマが管理します
12.2.3.1 自動シリアライズ
複数スレッドが、OCCI環境から導出されたオブジェクト(接続および接続プール)を操作している場合は、OCCIでこれらのオブジェクトへのアクセスをシリアライズするように選択できます。最初のステップとして、createEnvironment
メソッドにTHREADED_MUTEXED
の値を渡します。この時点で、OCCIライブラリは、環境内のスレッド・セーフ・オブジェクトに関するmutexを自動的に取得します。
OCCI環境がTHREADED_MUTEXED
モードで作成されている場合は、Environment
、Map
、ConnectionPool
、StatelessConnectionPool
およびConnection
オブジェクトのみがスレッド・セーフになります。たとえば、2つのスレッドがこれらのオブジェクトの1つに対して同時コールを行うと、OCCIによってそれらのコールが内部的にシリアライズされます。ただし、Statement
、ResultSet
、SQLException
、Stream
など、その他のOCCIオブジェクトはスレッド・セーフではないため、アプリケーションでこれらのオブジェクトを複数スレッドから同時に操作することはできません。
OCCIコールの処理のほとんどはサーバー上で行われます。そのため、OCCIコールを使用する2つのスレッドの接続先が同じ場合、サーバーで一方のスレッドの処理が終了するまでは、もう一方がブロックされる場合があるので注意してください。
12.2.3.2 アプリケーション対応のシリアライズ
複数スレッドが、OCCI環境から導出されたオブジェクトを操作している場合は、シリアライズを管理するように選択できます。最初のステップとして、createEnvironment
モードにTHREADED_UNMUTEXED
の値を渡します。この場合、アプリケーションでは、同一のOCCI環境から導出されたオブジェクトに対して相互排他ロックのOCCIコールを行う必要があります。この方法には、アプリケーションの設計に基づいてmutexスキームを最適化し、同時実行性を向上できるという利点があります。
OCCI環境をこのモードで作成した場合、OCCIでは、アプリケーションはマルチスレッド・アプリケーションで稼働しているが、その内部mutexの取得は不要であると認識されます。つまり、OCCIは、OCCI環境から導出されたオブジェクトのメソッドのコールはすべて、アプリケーションによってシリアライズされるとみなします。次の2つの方法を実行できます。
-
各スレッドには、それぞれ専用の環境があります。つまり、専用の環境とその環境から導出されたすべてのオブジェクト(接続、接続プール、文、結果セットなど)は、スレッド間で共有されません。この場合、アプリケーションでmutexを適用する必要はありません。
-
OCCI環境またはその環境から導出されたオブジェクトをスレッド間で共有する場合は、1つのスレッドのみが任意のオブジェクトに対してOCCIメソッドをコールするように、オブジェクトへのアクセスをシリアライズする必要があります。
いずれの場合も、OCCIによってmutexは取得されません。THREADED_UNMUTEXED
を使用する場合は、OCCI環境から導出された任意のオブジェクトに対するプロセスに、1つのOCCIコールのみが存在していることを常に確認する必要があります。
OCCIは、オブジェクトを最大限に再利用できるよう最適化されています。各環境にそれぞれ専用のヒープがあるため、複数環境の場合は、メモリー使用量の増加につながります。複数環境の存在は、接続、接続プール、文および結果セット・オブジェクトに関する作業が重複していることを意味します。これにより、メモリー使用量がさらに増加します。
サーバーに対して複数の接続を行うと、サーバーとネットワーク両方のリソース使用量が増加します。複数環境の場合は、さらに多くの接続数を伴うことになります。
12.2.4 オペレーティング・システムに関する考慮事項
一部のオペレーティング・システムには、親プロセスによって作成された状態を子プロセスが再利用できるプロセス生成の機能があります。
親プロセスが子プロセスを生成した後、子プロセスは親が作成したデータベース接続を使用できません。SQL*Netでは1つのユーザー・プロセスのみがデータベースへの特定の接続を使用することが期待されるため、子プロセスが親と同じデータベース接続を使用しようとすると、意図しない接続干渉が発生し、断続的なORA-03137
エラーが発生することがあります。
アプリケーションで複数の同時接続が必要な場合は、スレッドをサポートするプラットフォームでスレッドを使用することをお薦めします。Oracleでは、シングルスレッド・アプリケーションとマルチスレッド・アプリケーションの両方で同時接続をサポートします。
スレッドの詳細は、「スレッド・セーフティの概要」および「スレッド・セーフティの実装」を参照してください。
同時に開かれた多くの接続でパフォーマンスを向上させるには、「接続プーリング」を参照してください。
12.3 アプリケーションによるデータ・バッファの管理について
パラメータ化された文でset
xxxメソッドを使用してバインド・パラメータにデータを提供すると、その値は内部データ・バッファにコピーされた後、データベース・サーバーに渡されて挿入されます。ユーザー・バッファ内のstring
型データをコピーするオーバーヘッドを削減するには、ResultSetクラスのsetDataBuffer()およびnext()メソッド、およびStatementクラスのexecute()メソッドを使用します。
12.3.1 setDataBuffer() Methodの使用方法
高パフォーマンスのアプリケーションの場合は、setDataBuffer
メソッドによって、データ・バッファをアプリケーションで管理できます。次に、setDataBuffer()メソッドの例を示します。
void setDataBuffer(int paramIndex, void *buffer, Type type, sb4 size, ub2 *length, sb2 *ind = NULL, ub2 *rc = NULL);
前述のメソッド例では、次のパラメータが使用されています。
-
paramIndex
: パラメータ番号 -
buffer
: データを格納するデータ・バッファ。 -
type
: データ・バッファ内のデータの型。 -
size
: データ・バッファのサイズ。 -
length
: データ・バッファ内のデータの現在の長さ。 -
ind
: インジケータ情報。この情報は、データがNULL
かどうかを示します。パラメータ化された文の場合、値-1
はNULL
値が挿入されることを意味します。コール可能文から戻されたデータの場合、値-1
はNULL
データが取り出されたことを意味します。 -
rc
: リターン・コード。この変数は、Statement
メソッドに提供されたデータには適用できません。ただし、コール可能文から戻されたデータの場合、このリターン・コードにパラメータ固有のエラー番号が指定されます。
setDataBuffer()
メソッドによって、すべてのデータ型を提供したり取得できるわけではありません。たとえば、C++標準ライブラリの文字列はsetDataBuffer()
インタフェースでは提供されません。
set
xxx()
メソッドで提供されたデータとsetDataBuffer()
メソッドで提供されたデータには、重要な違いがあります。set
xxx()
メソッドでデータをコピーした場合は、データのコピー後に元のデータを変更できます。たとえば、setString(str1)
メソッドを使用すると、実行前に値str1
を変更できます。使用される値str1
は、setString(str1)
のコール時の値です。これに対し、setDataBuffer()
メソッドで提供されたデータの場合は、実行が完了するまで、バッファは有効のままです。
反復実行またはexecuteArrayUpdate()
メソッドを使用する場合、複数行のデータおよび反復のデータは単一のバッファに格納できます。この場合、i番目の反復に対するデータはbuffer + (i-1) * sizeのアドレス
にあり、長さ、インジケータおよびリターン・コードはそれぞれ、*(length + i)
、*(ind + i)
、*(rc + i)
にあります。
またこのインタフェースは、配列実行や配列、またはOUT
バインド・パラメータを持つコール可能文にも使用できます。
このメソッドをResultSet
クラスで使用すると、各フェッチに対してバッファを再割当てせずにデータを取り出すことができます。
12.3.2 executeArrayUpdate()メソッドの使用方法
すべてのデータをsetDataBuffer()
メソッドまたは出力ストリームで提供する場合(つまり、setDataBuffer()
またはgetStream()
以外のset
xxx()をコールしない場合)、反復実行を行う簡潔な方法があります。
この場合、setMaxIterations()
およびsetMaxParamSize()
はコールしないでください。かわりに、setDataBuffer()
またはgetStream()
メソッドを、各反復にデータを提供する適切なサイズの配列をそれぞれのパラメータに指定してコールし、続いてexecuteArrayUpdate(int
arrayLength)
メソッドをコールします。arrayLength
パラメータには、各バッファに格納する要素数を指定します。これは、実質的にarrayLength
に反復回数を設定して文を実行するのと同じです。
ストリーム・パラメータは1度のみ指定されるため、配列実行でも使用できます。ただし、set
xxx()
メソッドを使用する場合は、addIteration()
メソッドをコールして複数行にデータを提供します。この2つの方法を比較するために、例12-2で、2人の従業員をemployees
表に挿入する例を示しています。
ただし、setDataBuffer()
インタフェースを使用して最初のパラメータも指定できる場合は、例12-3で示すように、addIteration()
メソッドのかわりにexecuteArrayUpdate()
メソッドを使用できます。
例12-2 addIteration()メソッドを使用したレコードの挿入方法
Statement *stmt = conn->createStatement( "insert into departments (department_id, department_name) values(:1, :2)"); char dnames[][100] = {"Community Outreach", "University Recruiting"}; ub2 dnameLen[2]; for (int i = 0; i < 2; i++) dnameLen[i] = strlen(dnames[i] + 1); stmt->setMaxIterations(2); // set maximum number of iterations stmt->setInt(1, 7369); // specify data for the first row stmt->setDataBuffer(2, dnames, OCCI_SQLT_STR, sizeof(dnames[0]), dnameLen); stmt->addIteration(); stmt->setInt(1, 7654); // specify data for the second row // a setDatBuffer is unnecessary for the second // bind parameter as data provided through // setDataBuffer is specified only once. stmt->executeUpdate();
例12-3 executeArrayUpdate()メソッドを使用したレコードの挿入方法
Statement *stmt = conn->createStatement( "insert into departments (department_id, department_name) values(:1, :2)"); char dnames[][100] = {"Community Outreach", "University Recruiting"}; ub2 dnameLen[2]; for (int i = 0; i < 2; i++) dnameLen[i] = strlen(dnames[i] + 1); int ids[2] = {7369, 7654}; ub2 idLen[2] = {sizeof(ids[0]), sizeof(ids[1])}; stmt->setDataBuffer(1, ids, OCCIINT, sizeof(ids[0]), idLen); stmt->setDataBuffer(2, dnames, OCCI_SQLT_STR, sizeof(dnames[0]), dnameLen); stmt->executeArrayUpdate(2); // data for two rows is inserted.
12.4 next()メソッドを使用した配列フェッチの使用方法
アプリケーションでsetDataBuffer()
インタフェースまたはストリーム・インタフェースのみを使用してデータをフェッチする場合は、配列フェッチを実行できます。配列フェッチは、ResultSet
クラスのnext()
メソッドを通して実装します。再度コールする場合は、その前にnext()で取得した結果を処理する必要があります。
これにより、各列についてnumRows
の数のデータがフェッチされます。setDataBuffer()
インタフェースで指定するバッファ・サイズは、データのnumRows
を保持するのに十分なサイズ以上である必要があります。
例12-4 ResultSetによる配列フェッチの使用方法
ResultSet *resultSet = stmt->executeQuery(...);
resultSet->setDataBuffer(...);
while (resultSet->next(numRows) == DATA_AVAILABLE)
process(resultSet->getNumArrayRows() );
12.5 行変更の反復
バッチ・エラーを処理するには、setBatchErrorMode()メソッドを使用して、Statement
オブジェクトをbatchMode
で実行することを指定する必要があります。batchMode
を設定し、バッチ更新が実行されると、発生するすべてのエラーがBatchSQLExceptionクラスによりレポートされます。
BatchSQLException
クラスにより、バッチ・エラーを処理するメソッドが提供されます。例12-5は、OCCIアプリケーションでバッチ処理を実装する方法を示しています。
例12-5 行変更の反復とエラー処理の方法
-
Statementオブジェクトを作成し、バッチ・エラー・モードを
TRUE
に設定します。Statement *stmt = conn->createStatement ("..."); stmt->setBatchErrorMode (true);
-
アプリケーションに必要なプログラム上の変更を行います。
-
文を更新します。
try { updateCount = stmt->executeUpdate (); }
-
バッチ挿入または更新で生成されたエラーを捕捉し、処理します。
catch (BatchSQLException &batchEx) { cout<<"Batch Exception: "<<batchEx.what()<<endl; int errCount = batchEx.getFailedRowCount(); cout << "Number of rows failed " << errCount <endl; for (int i = 0; i < errCount; i++ ) { SQLException err = batchEx.getException(i); unsigned int rowIndex = batchEx.getRowNum(i); cout<<"Row " << rowIndex << " failed because of " << err.getErrorCode() << endl; } // take recovery action on the failed rows }
-
文の更新で生成されたその他のエラーを捕捉し、処理します。文レベルのエラーは、引き続き
SQLException
のインスタンスとして発生します。catch( SQLException &ex) // to catch other SQLExceptions. { cout << "SQLException: " << e.what() << endl; }
12.6 Traffic DirectorモードのOracle Connection Managerの使用について
Traffic DirectorモードのOracle Connection Managerは、サポートされるデータベース・クライアントとデータベース・インスタンスの間に配置されるプロキシです。
操作モード
Traffic DirectorモードのOracle Connection Managerでは、次の操作モードがサポートされます。
-
プール接続モードでは、Traffic DirectorモードのOracle Connection Managerは次のデータベース・クライアント・リリースを使用する任意のアプリケーションをサポートします。
-
OCI、OCCIおよびオープン・ソース・ドライバ(Oracle Database 11gリリース2 (11.2.0.4)以降)
-
JDBC (Oracle Database 12cリリース1 (12.1)以降)
-
ODP.NET (Oracle Database 12cリリース2 (12.2)以降)
さらに、アプリケーションはDRCPを使用する必要があります。つまり、アプリケーションは接続文字列(または
tnsnames.ora
の別名)でDRCPを有効にする必要があります。 -
-
非プール接続(または専用)モードでは、Traffic DirectorモードのOracle Connection Managerはデータベース・クライアント・リリースOracle Database 11gリリース2 (11.2.0.4)以降を使用する任意のアプリケーションをサポートします。このモードでは、一部の機能(接続多重化など)は使用できません。
主な機能
-
透過的なパフォーマンス強化と接続多重化。次の機能が含まれます。
-
文キャッシュ、行プリフェッチおよび結果セット・キャッシュは、すべての操作モードで自動的に有効になります。
-
プロキシ常駐接続プール(PRCP)を使用したデータベース・セッションの多重化(プール・モードのみ)。PRCPは、データベース常駐接続プーリング(DRCP)のプロキシ・モードです。アプリケーションは、Traffic DirectorモードのOracle Connection Managerとデータベースの間で透過的な接続時ロード・バランシングおよびランタイム・ロード・バランシングを実現できます。
-
Traffic DirectorモードのOracle Connection Managerインスタンスが複数ある場合、アプリケーションはクライアント側の接続時ロード・バランシングまたはロード・バランサ(BIG-IPやNGINXなど)を使用して高度なスケーラビリティを実現できます。
-
-
アプリケーションの停止時間ゼロ
-
計画されたデータベース・メンテナンスまたはプラガブル・データベース(PDB)の再配置
-
プール・モード
Traffic DirectorモードのOracle Connection Managerは、計画停止のOracle Notification Service(ONS)イベントに応答し、作業をリダイレクトします。リクエストが完了すると、Traffic DirectorモードのOracle Connection Managerのプールから接続が排出されます。サービスの再配置は、Oracle Database 11gリリース2 (11.2.0.4)以降でサポートされます。
PDBの再配置の場合、Traffic DirectorモードのOracle Connection Managerは、ONSが構成されていない場合でも、PDBが再配置されたときのインバンド通知に応答します(Oracle Database 18c (バージョン18.1)以降のサーバーのみ)。
-
非プールまたは専用モード
クライアントからリクエスト境界情報が指定されていない場合、Traffic DirectorモードのOracle Connection Managerは多くのアプリケーションで計画停止をサポートします(単純なセッション状態とカーソル状態のみをリクエスト/トランザクションの境界にまたがって維持する必要がある場合)。次の機能がサポートされます。-
トランザクション境界でサービス/PDBを停止するか、Oracle Databaseリリース18cの継続的なアプリケーション可用性を利用してリクエスト境界でサービスを停止します。
-
Traffic DirectorモードのOracle Connection Managerは、透過的アプリケーション・フェイルオーバー(TAF)フェイルオーバーのリストアを利用して再接続し、単純な状態をリストアします。
-
-
-
読取りが主なワークロードに対する計画外のデータベース停止
-
-
Traffic DirectorモードのOracle Connection Managerの高可用性によるシングル・ポイント障害の回避。これは次の機能によってサポートされます。
-
接続文字列でロード・バランサまたはクライアント側のロード・バランシング/フェイルオーバー機能を使用する複数のTraffic DirectorモードのOracle Connection Managerインスタンス
-
Traffic DirectorモードのOracle Connection Managerインスタンスのローリング・アップグレード
-
計画停止で行われるクライアントからTraffic DirectorモードのOracle Connection Managerへの既存の接続の正常終了
-
Oracle Databaseリリース18c以降のクライアントに対するインバンド通知
-
古いクライアントの場合は、現在のリクエストのレスポンスとともに通知が送信されます。
-
-
セキュリティと独立性を確保するため、Traffic DirectorモードのOracle Connection Managerは次の機能を提供します。
-
Transmission Control Protocol/Transmission Control Protocol Secure (TCP/TCPS)およびプロトコル変換をサポートするデータベース・プロキシ
-
IPアドレス、サービス名およびSecure Socket Layer/Transport Layer Security (SSL/TLS)ウォレットに基づくファイアウォール
-
マルチテナント環境でのテナント分離
-
DoS攻撃およびFuzzing攻撃からの保護
-
Oracle DatabaseオンプレミスとOracle Cloud間のデータベース・トラフィックのセキュア・トンネル接続
-
関連項目:
-
Oracle Database Net Services管理者ガイド(
cman.ora
構成ファイルを構成してTraffic DirectorモードのOracle Connection Managerを設定する方法の詳細) -
Oracle Database Net Services管理者ガイド(Traffic DirectorモードのOracle Connection Managerでプロキシ認証を使用できるようにデータベースを構成する方法の詳細)
-
Oracle Database Net Services管理者ガイド(計画外停止イベントに応答するようにTraffic DirectorモードのOracle Connection Managerを構成する方法の詳細)
-
Oracle Database Net Services管理者ガイド(計画停止イベントに応答するようにTraffic DirectorモードのOracle Connection Managerを構成する方法の詳細)
-
Oracle Database Net Services管理者ガイド(Traffic DirectorモードのOracle Connection Managerで使用するプロキシ常駐接続プールを構成する方法の詳細)
-
Oracle Database Net Services管理者ガイド(Traffic DirectorモードのOracle Connection Managerのすべてのドライバでサポートされていない機能の詳細)
-
Oracle Database Net Servicesリファレンス(Oracle CMAN構成ファイルの概要)
12.7 ステートレスな接続プールのランタイム・ロード・バランシングについて
ステートレスな接続プールのランタイム・ロード・バランシングでは、データベースの負荷が最も少ないインスタンスへ接続要求が動的にルーティングされます。これには、Oracle RACロード・バランシング・アドバイザによって配布されるサービス・メトリックが使用されます。
この機能では、ステートレスな接続プールが次の方法で変更されます。
-
プールは、インスタンスのロードに関して定期的な通知を受け取ります。
-
接続要求を受け取ると、プールはインスタンスのロードに基づいて要求の型に最適な接続を選択します。
-
ステートレスな接続プールは、インスタンスのロードに対応する接続トポロジを保持しながら、オーバーロードされたインスタンスの接続を定期的に終了します。
-
オーバーロードされたインスタンスへの接続は終了となる場合があるため、プールは同時実行性の要件を保持するため新規接続を作成します。これらの新規接続は、Oracle RACリスナーの接続時ロード・バランシングを使用して作成されます。
OCCI環境がTHREADED_MUTEXED
モードおよびEVENTS
モードで作成され、サーバーがイベント通知を発行するよう構成されている場合、ランタイム・ロード・バランシングはデフォルトでオンになります。
この項には次のトピックが含まれます: 「APIのサポート」。
12.8 障害診断について
障害診断では、問題インシデントが発生した場合、OCCIクライアントでダンプ・ファイルまたはコア・ダンプ・ファイルなどの診断データが取得されます。障害診断機能により、各インシデントについて、この診断データを格納するための自動診断リポジトリ(ADR)サブディレクトリが作成されます。たとえば、LinuxまたはUNIXアプリケーションのいずれかでNULLのポインタ参照により障害が発生した場合、コア・ファイルが、オペレーティング・システム・ディレクトリではなくADRホーム・ディレクトリ(存在する場合)に格納されます。この項では、ADRサブディレクトリ構造とその出力を管理するユーティリティ、ADRコマンド・インタプリタ(ADRCI)について説明します。
ADRホームは、OCCIなどの製品のインスタンスに対するすべての診断データ、および特定のオペレーティング・システム・ユーザーのルート・ディレクトリです。ADRホームはすべて、同じルート・ディレクトリ、ADRベースの下に表示されます。
12.8.2 ADRCIの使用
ADRCIは、ADR内の診断データを表示し、インシデントおよび問題の情報をOracleサポートと共有できるZIPファイルにパッケージングできるコマンドライン・ツールです。ADRCIは、対話式にもスクリプトによっても使用できます。
問題とは、OCIまたはクライアント内のクリティカル・エラーです。各問題に問題キーが割り当てられます。インシデントとは、問題の1回の発生を指し、数値を使用した一意のインシデントIDで識別されます。各インシデントには、属性セット(ORA
エラー番号、エラー・パラメータ値および類似情報)を含む問題キーが割り当てられます。2つのインシデントの問題キーが一致する場合、これらのインシデントの根本的原因は同じです。
次の各例では、Linuxオペレーティング・システムでのADRCIの使用方法を示しています。ARDCIコマンドでは大/小文字が区別されない点に注意してください。ユーザー入力はすべて太字体です。
関連項目:
-
ADRCIの概要については、『Oracle Databaseユーティリティ』を参照してください。
例12-6 OCCIアプリケーション・インシデントに関するADRCIの使用方法
LinuxシステムでADRCIを起動するには、adrci
コマンドを使用します。ADRCIが起動したら、help
でshow base
コマンドの詳細を検索します。その後、-product client
オプション(OCCIアプリケーションに必要)を使用して特定のクライアントのベースを決定します。ADRCIベースを設定するには、set baseコマンドを使用します。ADRCI
が起動すると、デフォルトのADRベースはrdbms
サーバー用となります。$ORACLE_HOME
は/ade/chuck_l3/oracle
に設定されます。インシデントを確認するには、show incidents
コマンドを使用します。ADRCIを終了するには、quit
コマンドを使用します。
% adrci ADRCI: Release 11.2. - on Wed November 25 16:16:55 2008 Copyright (c) 1982, 2008, Oracle. All rights reserved. adrci> help show base Usage: SHOW BASE [-product <product_name>] Purpose: Show the current ADR base setting. Options: [-product <product_name>]: This option allows users to show the given product's ADR Base location. The current registered products are "CLIENT" and "ADRCI". Examples: show base -product client show base adrci> show base -product client ADR base is "/ade/chuck_l3/oracle/log" adrci> help set base Usage: SET BASE <base_str> Purpose: Set the ADR base to use in the current ADRCI session. If there are valid ADR homes under the base, all homes are added to the current ADRCI session. Arguments: <base_str>: It is the ADR base directory, which is a system-dependent directory path string. Notes: On platforms that use "." to signify current working directory, it can be used as base_str. Example: set base /net/sttttd1/scratch/someone/view_storage/someone_v1/log set base . adrci> set base /ade/chuck_l3/oracle/log adrci> show incidents ... adrci> quit
例12-7 Instant ClientにおけるADRCIの使用方法
Instant Clientでは$ORACLE_HOME
を使用しないため、デフォルトのADRベースはユーザーのホーム・ディレクトリとなります。
adrci> show base -product client ADR base is "/home/chuck/oradiag_chuck" adrci> set base /home/chuck/oradiag_chuck adrci> show incidents ADR Home = /home/chuck/oradiag_chuck/diag/clients/user_chuck/host_4144260688_11: ************************************************************************* INCIDENT_ID PROBLEM_KEY CREATE_TIME ------------------------------------------------------------------------- 1 oci 24550 [6] 2007-05-01 17:20:02.803697 -07:00 1 rows fetched adrci> quit
12.9 クライアント結果キャッシュの使用
クライアントの結果キャッシュは、繰り返し実行される問合せのレスポンス時間を向上させます。この機能では、クライアント・メモリーを使用して、データベースから実行およびフェッチされるSQL問合せの結果をキャッシュします。同じ問合せの後続の実行で、クライアント・キャッシュから結果をフェッチし、サーバーCPUの使用量を削減します。データベースのラウンドトリップが排除されるため、アプリケーションのレスポンス時間が向上します。
OCCIアプリケーションは、OCCI文キャッシュを有効にすることでクライアントの結果キャッシュ機能を透過的に使用します。キャッシュする必要のあるSELECT
問合せは、/*+ result_cache */
ヒントで注釈が付けられます。例12-8に、このようなSELECT
問合せを使用するOCCI文オブジェクトの作成方法を示します。
使用方法のガイドライン、キャッシュの一貫性および制限は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。
例12-8 クライアントの結果キャッシュの有効化および使用の方法
Connection *conn; Statement *stmt; ResultSet *rs; ... //enable OCCI Statement Caching conn->setStmtCacheSize(20); //Specify the hint in the SELECT query stmt = conn->createStatement("select /*+ result_cache */ * from products \ where product_id = :1"); //the following execute fetches rows from the client cache if //the query results are cached. If this is the first execute //of the query, the results fetched from the server are //cached on the client side. rs = stmt->executeQuery();
12.10 クライアント側のデプロイ・パラメータと自動チューニングについて
Oracle Database Release 12cリリース1 (12.1)以降、選択したプロパティを構成するために使用できるクライアント側の構成ファイルoraaccess.xml
が提供されています。これにより、ソース・コードを変更しないで、デプロイ中にアプリケーションの動作を変更できるようになります。
注意:
OCCIアプリケーションがすでにsetPrefetchRowCount()
メソッドまたはsetPrefetchMemorySize()
メソッドを使用している場合、プリフェッチ・デプロイ・パラメータを使用しないでください。
関連項目:
クライアント側のデプロイ・パラメータと自動チューニングの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。