ヘッダーをスキップ
Oracle® C++ Call Interfaceプログラマーズ・ガイド,
11g リリース2 (11.2)
B72452-01
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

12 OCCIアプリケーションのパフォーマンスの最適化

この章では、OCCIカスタム・アプリケーションのパフォーマンスの向上にお薦めする方法を説明します。

ここでは、次の項目について説明します。

透過的アプリケーション・フェイルオーバー

OCCIの透過的アプリケーション・フェイルオーバー(TAF)により、分散アプリケーションの実行時のデータベース・インスタンス障害の処理におけるOCCIの堅牢性が向上します。サーバー・ノードが使用できなくなると、アプリケーションは他の使用可能なノードに自動的に再接続します。

TAFは、ラウンドトリップ操作中にデータベース・インスタンスがダウンしていることをクライアント・アプリケーションが検出した場合に発生します。これにより、TAFに対して構成されたバックアップ・データベースへの接続が確立されます。このバックアップは、Oracle RAC構成の別のノード、ホスト・スタンバイ・データベースまたは同じデータベース・インスタンス自体にすることができます。

失敗した接続上のラウンドトリップに責任を持つOCCI/OCI APIは、通常、次のいずれかのエラーを返します。

後続のアプリケーション要求、および再起動が必要な進行中の作業には、新規接続を使用できます。アプリケーションのアイドル接続は影響を受けません。

アプリケーションで透過的アプリケーション・フェイルオーバーを使用する場合、いくつかの設計オプションを検討する必要があります。

SESSIONパラメータの変更の追跡は、ユーザーの責任で行ってください。

これらの問題に対応するため、このアプリケーションではフェイルオーバー・コールバック関数を登録できます。フェイルオーバーが発生した後、コールバック関数が、ユーザー・セッションの再確立中に数回呼び出されます。

透過的アプリケーション・フェイルオーバーの使用方法

TAFを有効にするため、フェイルオーバーのための接続文字列を構成し、ConnectionEnvironmentConnectionPoolおよびStatelessConnectionPoolから作成)に登録する必要があります。コールバック関数を登録するには、ConnectionクラスのインタフェースsetTAFNotify()を使用します。

void Connection::setTAFNotify(
   int (*notifyFn)(
      Environment *env,
      Connection *conn,
      void *ctx,
      FailOverType foType,
      FailOverEventType foEvent),
   void *ctxTAF);

TAFでのConnectionPoolのサポートにはBACKUPおよびPRECONNECT句は含まれないため、接続文字列ではこれらを使用することはできません。

オブジェクトと透過的アプリケーション・フェイルオーバー

透過的アプリケーション・フェイルオーバーは、OCCIのナビゲーション・モデルと連想アクセス・モデル、およびオブジェクト・キャッシュと連携しています。非Oracle RAC設定では、プライマリ・インスタンスとバックアップ・インスタンスで、オブジェクト型定義とオブジェクトOIDが同一であることを確認する必要があります。

フェイルオーバーの後にアプリケーションが「ORA-25402: トランザクションをロールバックしてください。」のエラーを受信すると、アプリケーションはロールバックを開始し、そのクライアントのオブジェクト・キャッシュを正しくリセットします。フェイルオーバー前にトランザクションが開始されていない場合、アプリケーションはフェイルオーバー後にロールバックを開始し、そのクライアントのオブジェクト・キャッシュのオブジェクトを新しいインスタンスでリフレッシュします。

接続プーリングと透過的アプリケーション・フェイルオーバー

透過的アプリケーション・フェイルオーバー機能が有効である場合、接続プールで作成された接続もフェイルオーバーされます。接続プールから取得された各接続に対し、アプリケーション・フェイルオーバー・コールバックを指定する必要があります。これらの接続は、プライマリ・インスタンスの障害後に使用されるとフェイルオーバーされます。

カスタム接続プール内の接続は、明示的にクリーンアップおよび修復する必要があります。プールに500個の接続があるアプリケーションについて考えます。接続のうち10個はビジー(ラウンドトリップを実行中)で、490個は空きまたはアイドルです。データベース・インスタンスが失敗した場合、TAFは10個のアクティブ接続に対して動作し、これらの接続上のクライアント要求を再開する必要があります。他の490個の接続がそれぞれアプリケーションによって選択されると、TAFが実行され、OCCIはエラー・コードORA-25401ORA-24502ORA-25408のいずれかを返して、ユーザー要求を強制的に再開します。アプリケーションは、TAFが接続プールの10個の接続に対して以前にアクティブになったという知識を使用して、これらの接続を修復またはパージすることにより、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;
}
}

接続の共有

この項は、次の項目で構成されています。

スレッド・セーフティの概要

スレッドは、大規模なプロセス内に存在する軽量プロセスです。複数のスレッドで同一コードおよび同一データ・セグメントを共有しますが、プログラム・カウンタ、システム・レジスタおよびスタックは、スレッドごとにあります。グローバル変数および静的変数は、すべてのスレッドに共通です。そのため、1つのアプリケーション内で、複数のスレッドによるこれらの変数へのアクセスを管理する相互排他メカニズムが必要です。

スレッドは、1度作成されると他のスレッドとは非同期的に動作します。このため、順序を気にせずに共通のデータ要素にアクセスし、OCCIコールを実行します。このようにデータ要素に共有アクセスを行うため、複数のスレッドによってアクセスされるデータの整合性を維持するメカニズムが必要です。データ・アクセスを管理するメカニズムでは、mutex(相互排他ロック)の形式を使用し、アプリケーション内の共有リソースにアクセスする複数のスレッド間で、競合が発生しないようにします。OCCIでは、mutexはOCCI環境単位で付与されます。

Oracleデータベース・サーバーおよびOCCIライブラリのスレッド・セーフティ機能により、開発者はOCCIをマルチスレッド・アプリケーションで使用でき、次の利点を得ることもできます。

  • 複数のスレッドでOCCIコールを実行した場合も、単一のスレッドで連続するコールを実行した場合と同じ結果になります。

  • 複数のスレッドでOCCIコールを実行した場合、スレッド間で副作用がありません。

  • マルチスレッド・プログラムを作成しない場合にスレッド・セーフのOCCIコールを実行しても、パフォーマンスは低下しません。

  • 複数のスレッドを使用すると、プログラムのパフォーマンスが向上します。パフォーマンスが向上するのは、別々のプロセッサでスレッドを同時実行するマルチプロセッサ・システム、および低速操作と高速操作の間でオーバーラップが発生するシングル・プロセッサ・システムの場合です。

クライアント/サーバー・アプリケーションでは、クライアントをマルチスレッド・プログラムにできますが、それに加え、スレッド・セーフティは3層アーキテクチャまたはクライアント/エージェント/サーバー・アーキテクチャで典型的に使用されます。このアーキテクチャでは、クライアントが関係するのはプレゼンテーション層のサービスのみです。エージェント(つまり、アプリケーション・サーバー)は、クライアント・アプリケーションのアプリケーション・ロジックを処理します。一般的には、この関係は多対1の関係で、複数のクライアントが同一のアプリケーション・サーバーを共有します。

この3層アーキテクチャのサーバー層は、Oracleデータベース・サーバーです。アプリケーション・サーバー(エージェント)はマルチスレッドをサポートし、各スレッドが別のクライアント・アプリケーションの要求を処理します。Oracle環境では、この中間層アプリケーション・サーバーはOCCIプログラムまたはプリコンパイラ・プログラムです。

スレッド・セーフティの実装

OCCIでスレッド・セーフティを使用するには、最初にアプリケーションをスレッド・セーフなオペレーティング・システム上で実行する必要があります。次に、createEnvironment()メソッドのモード・パラメータにTHREADED_MUTEXEDまたはTHREADED_UNMUTEXEDを指定することで、アプリケーションがマルチスレッド・モードで稼働していることをOCCIに通知する必要があります。たとえば、相互排他ロックをオンにするには、次の文を発行します。

Environment *env = Environment::createEnvironment( 
          Environment::THREADED_MUTEXED);

createEnvironmentTHREADED_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では非ブロック化モードがサポートされません。

シリアライズ

アプリケーション・プログラマは、マルチスレッド・アプリケーションでの同時実行性に関して、次の2つの基本的なオプションから選択できます。

  • 自動シリアライズ。OTISの透過的メカニズムを使用します。

  • アプリケーション対応のシリアライズ。複数スレッドのメンテナンスに付随して発生する問題をプログラマが管理します。

自動シリアライズ

複数スレッドが、OCCI環境から導出されたオブジェクト(接続および接続プール)を操作している場合は、OCCIでこれらのオブジェクトへのアクセスをシリアライズするように選択できます。最初のステップとして、createEnvironmentメソッドにTHREADED_MUTEXEDの値を渡します。この時点で、OCCIライブラリは、環境内のスレッド・セーフ・オブジェクトに関するmutexを自動的に取得します。

OCCI環境がTHREADED_MUTEXEDモードで作成されている場合は、EnvironmentMapConnectionPoolStatelessConnectionPoolおよびConnectionオブジェクトのみがスレッド・セーフになります。たとえば、2つのスレッドがこれらのオブジェクトの1つに対して同時コールを行うと、OCCIによってそれらのコールが内部的にシリアライズされます。ただし、StatementResultSetSQLExceptionStreamなど、その他のOCCIオブジェクトはスレッド・セーフではないため、アプリケーションでこれらのオブジェクトを複数スレッドから同時に操作することはできません。

OCCIコールの処理のほとんどはサーバー上で行われます。そのため、OCCIコールを使用する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は、オブジェクトを最大限に再利用できるよう最適化されています。各環境にそれぞれ専用のヒープがあるため、複数環境の場合は、メモリー使用量の増加につながります。複数環境の存在は、接続、接続プール、文および結果セット・オブジェクトに関する作業が重複していることを意味します。これにより、メモリー使用量がさらに増加します。

サーバーに対して複数の接続を行うと、サーバーとネットワーク両方のリソース使用量が増加します。複数環境の場合は、さらに多くの接続数を伴うことになります。

オペレーティング・システムの考慮事項

一部のオペレーティング・システムには、親プロセスによって作成された状態を子プロセスが再利用できるプロセス生成の機能があります。

親プロセスが子プロセスを生成した後、子プロセスは親が作成したデータベース接続を使用できません。SQL*Netでは1つのユーザー・プロセスのみがデータベースへの特定の接続を使用することが期待されるため、子プロセスが親と同じデータベース接続を使用しようとすると、意図しない接続干渉が発生し、断続的なORA-03137エラーが発生することがあります。

アプリケーションで複数の同時接続が必要な場合は、スレッドをサポートするプラットフォームでスレッドを使用することをお薦めします。Oracleでは、シングルスレッド・アプリケーションとマルチスレッド・アプリケーションの両方で同時接続をサポートします。

スレッドの詳細は、「スレッド・セーフティの概要」および「スレッド・セーフティの実装」を参照してください。

同時に開かれた多くの接続でパフォーマンスを向上させるには、「接続プーリング」を参照してください。

アプリケーションによるデータ・バッファの管理

パラメータ化された文でsetxxxメソッドを使用してバインド・パラメータにデータを提供すると、その値は内部データ・バッファにコピーされた後、データベース・サーバーに渡されて挿入されます。ユーザー・バッファ内のstring型データをコピーするオーバーヘッドを削減するには、ResultSetクラスsetDataBuffer()およびnext()メソッド、およびStatementクラスexecute()メソッドを使用します。

setDataBuffer()メソッド

高パフォーマンスのアプリケーションの場合は、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かどうかを示します。パラメータ化された文の場合、値-1NULL値が挿入されることを意味します。コール可能文から戻されたデータの場合、値-1NULLデータが取り出されたことを意味します。

  • rc: リターン・コード。この変数は、Statementメソッドに提供されたデータには適用できません。ただし、コール可能文から戻されたデータの場合、このリターン・コードにパラメータ固有のエラー番号が指定されます。

setDataBuffer()メソッドによって、すべてのデータ型を提供したり取得できるわけではありません。たとえば、C++標準ライブラリの文字列はsetDataBuffer()インタフェースでは提供されません。


関連項目

具体的な内容は、第5章「データ型」表5-2「外部データ型と対応するC++型およびOCCI型」を参照してください。

setxxx()メソッドで提供されたデータとsetDataBuffer()メソッドで提供されたデータには、重要な違いがあります。setxxx()メソッドでデータをコピーした場合は、データのコピー後に元のデータを変更できます。たとえば、setString(str1)メソッドを使用すると、実行前に値str1を変更できます。使用される値str1は、setString(str1)のコール時の値です。これに対し、setDataBuffer()メソッドで提供されたデータの場合は、実行が完了するまで、バッファは有効のままです。

反復実行またはexecuteArrayUpdate()メソッドを使用する場合、複数行のデータおよび反復のデータは単一のバッファに格納できます。この場合、i番目の反復に対するデータはbuffer + (i-1) * sizeのアドレスにあり、長さ、インジケータおよびリターン・コードはそれぞれ、*(length + i)*(ind + i)*(rc + i)にあります。

またこのインタフェースは、配列実行や配列、またはOUTバインド・パラメータを持つコール可能文にも使用できます。

このメソッドをResultSetクラスで使用すると、各フェッチに対してバッファを再割当てせずにデータを取り出すことができます。

executeArrayUpdate()メソッド

すべてのデータをsetDataBuffer()メソッドまたは出力ストリームで提供する場合(つまり、setDataBuffer()またはgetStream()以外のsetxxx()をコールしない場合)、反復実行を行う簡潔な方法があります。

この場合、setMaxIterations()およびsetMaxParamSize()はコールしないでください。かわりに、setDataBuffer()またはgetStream()メソッドを、各反復にデータを提供する適切なサイズの配列をそれぞれのパラメータに指定してコールし、続いてexecuteArrayUpdate(int arrayLength)メソッドをコールします。arrayLengthパラメータには、各バッファに格納する要素数を指定します。これは、実質的にarrayLengthに反復回数を設定して文を実行するのと同じです。

ストリーム・パラメータは1度のみ指定されるため、配列実行でも使用できます。ただし、setxxx()メソッドを使用する場合は、addIteration()メソッドをコールして複数行にデータを提供します。この2つの方法を比較するために、例12-2で、2人の従業員をemployees表に挿入する例を示しています。

例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(); 

ただし、setDataBuffer()インタフェースを使用して最初のパラメータも指定できる場合は、例12-3で示すように、addIteration()メソッドのかわりにexecuteArrayUpdate()メソッドを使用できます。

例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. 

next()メソッドを使用した配列フェッチ

アプリケーションでsetDataBuffer()インタフェースまたはストリーム・インタフェースのみを使用してデータをフェッチする場合は、配列フェッチを実行できます。配列フェッチは、ResultSetクラスのnext()メソッドを通して実装します。再度コールする場合は、その前にnext()で取得した結果を処理する必要があります。

例12-4 ResultSetによる配列フェッチの使用方法

ResultSet *resultSet = stmt->executeQuery(...);
resultSet->setDataBuffer(...);
while (resultSet->next(numRows) == DATA_AVAILABLE)
   process(resultSet->getNumArrayRows() );

これにより、各列についてnumRowsの数のデータがフェッチされます。setDataBuffer()インタフェースで指定するバッファ・サイズは、少なくともデータのnumRowsを保持するのに十分なサイズである必要があります。

行変更の反復

バッチ・エラーを処理するには、setBatchErrorMode()メソッドを使用して、StatementオブジェクトをbatchModeで実行することを指定する必要があります。batchModeを設定し、バッチ更新が実行されると、発生するすべてのエラーがBatchSQLExceptionクラスによりレポートされます。

BatchSQLExceptionクラスにより、バッチ・エラーを処理するメソッドが提供されます。例12-5は、OCCIアプリケーションでバッチ処理を実装する方法を示しています。

例12-5 行変更の反復とエラー処理の方法

  1. Statementオブジェクトを作成し、バッチ・エラー・モードをTRUEに設定します。

    Statement *stmt = conn->createStatement ("...");
    stmt->setBatchErrorMode (true);
    
  2. アプリケーションに必要なプログラム上の変更を行います。

  3. 文を更新します。

    try {
      updateCount = stmt->executeUpdate ();
    }
    
  4. バッチ挿入または更新で生成されたエラーを捕捉し、処理します。

    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
    }
    
  5. 文の更新で生成されたその他のエラーを捕捉し、処理します。文レベルのエラーは、引き続きSQLExceptionのインスタンスとして発生します。

    catch( SQLException &ex) // to catch other SQLExceptions.
    {
       cout << "SQLException: " << e.what() << endl;
    }
    

ステートレスな接続プールのランタイム・ロード・バランシング

ステートレスな接続プールのランタイム・ロード・バランシングでは、データベースの負荷が最も少ないインスタンスへ接続要求が動的にルーティングされます。これには、Oracle RACロード・バランシング・アドバイザによって配布されるサービス・メトリックが使用されます。

この機能では、ステートレスな接続プールが次の方法で変更されます。

OCCI環境がTHREADED_MUTEXEDモードおよびEVENTSモードで作成され、サーバーがイベント通知を発行するよう構成されている場合、ランタイム・ロード・バランシングはデフォルトでオンになります。


関連項目

『Oracle Call Interfaceプログラマーズ・ガイド』

APIのサポート

StatelessConnectionPoolクラスPoolType属性の新規オプションNO_RLBでは、ランタイム・ロード・バランシングが無効化されます。

障害診断

障害診断では、問題インシデントが発生した場合、OCCIクライアントでダンプ・ファイルまたはコア・ダンプ・ファイルなどの診断データが取得されます。障害診断機能により、各インシデントについて、この診断データを格納するための自動診断リポジトリ(ADR)サブディレクトリが作成されます。たとえば、LinuxまたはUNIXアプリケーションのいずれかでNULLのポインタ参照により障害が発生した場合、コア・ファイルが、オペレーティング・システム・ディレクトリではなくADRホーム・ディレクトリ(存在する場合)に格納されます。この項では、ADRサブディレクトリ構造とその出力を管理するユーティリティ、ADRコマンド・インタプリタ(ADRCI)について説明します。

ADRホームは、OCCIなどの製品のインスタンスに対するすべての診断データ、および特定のオペレーティング・システム・ユーザーのルート・ディレクトリです。ADRホームはすべて、同じルート・ディレクトリ、ADRベースの下に表示されます。


関連項目

『Oracle Database管理者ガイド』

ADRベースの位置

ADRベースの位置は、次の順序で決定されます。

  1. sqlnet.oraファイル(Windowsでは%TNS_ADMIN%ディレクトリ内、LinuxまたはUNIXでは$TNS_ADMINディレクトリ内)。

    TNS_ADMINディレクトリがない場合、sqlnet.oraは現行ディレクトリに格納されています。

    ADRベースがsqlnet.oraファイルにリストされている場合、次の型の文となります。

    ADR_BASE=/directory/adr
    

    意味は次のとおりです。

    • adr引数は存在している必要があるディレクトリであり、OCCIアプリケーションを実行し、同じADRベースを共有する必要があるすべてのオペレーティング・システム・ユーザーが書込み可能である必要があります。

    • directory引数はパス名です。

    ADR_BASEが設定されており、すべてのユーザーが1つのsqlnet.oraファイルを共有する場合、adrディレクトリが存在しない、または書込みができないと、OCCIは検索を停止します。ADR_BASEが設定されていない場合、OCCIは検索を続行し、他の特定のディレクトリの有無をテストします。

    たとえば、sqlnet.oraにエントリADR_BASE=/home/chuck/testが含まれる場合、次のようになります。

    • ADRベース:

      /home/chuck/test/oradiag_chuck
      
    • ADRホーム:

      /home/chuck/test/oradiag_chuck/diag/clients/user_chuck/host_4144260688_11
      
  2. Oracleベースが存在する場合(Windows: %ORACLE_BASE%LinuxおよびUNIX: $ORACLE_BASE)、クライアント・サブディレクトリがデータベースのインストール時にOracle Universal Installerによって作成されるため、クライアント・サブディレクトリも存在します。

    たとえば、$ORACLE_BASE/home/chuck/obaseである場合、次のようになります。

    • ADRベース:

      /home/chuck/obase
      
    • ADRホーム:

      /home/chuck/obase/diag/clients/user_chuck/host_4144260688_11
      
  3. Oracleホームが存在する場合(Windows: %ORACLE_HOME%LinuxおよびUNIX: $ORACLE_HOME)、クライアント・サブディレクトリがデータベースのインストール時にOracle Universal Installerによって作成されるため、クライアント・サブディレクトリも存在します。

    たとえば、$ORACLE_HOME/ade/chuck_l1/oracleである場合、次のようになります。

    • ADRベース:

      /ade/chuck_l1/oracle/log
      
    • ADRホーム:

      /ade/chuck_l1/oracle/log/diag/clients/user_chuck/host_4144260688_11
      
  4. オペレーティング・システムのホーム・ディレクトリ上。

    • Windowsでは、オペレーティング・システムのホーム・ディレクトリは%USERPROFILE%です。

      Oracleフォルダの位置は次のとおりです。

      C:\Documents and Settings\chuck
      

      アプリケーションがサービスとして実行される場合、ホーム・ディレクトリ・オプションはスキップされます。

    • LinuxおよびUNIXでは、オペレーティング・システムのホーム・ディレクトリは$HOMEです。

      位置は次のとおりです。

      /home/chuck/oradiag_chuck
      

    たとえば、Instant Clientでは、$HOME/home/chuckである場合、次のようになります。

    • ADRベース:

      /home/chuck/oradiag_chuck
      
    • ADRホーム:

      /home/chuck/oradiag_chuck/diag/clients/user_chuck/host_4144260688_11
      

    関連項目

    「Instant Client」

  5. 一時ディレクトリ内。

    • Windowsでは、一時ディレクトリは次の順序で検索されます。

      • %TMP%

      • %TEMP%

      • %USERPROFILE%

      • Windowsシステム・ディレクトリ

    • LinuxおよびUNIXでは、一時ディレクトリは/var/tmpにあります。

    たとえば、Instant Clientでは、$HOMEが書込み不可である場合、次のようになります。

    • ADRベース:

      /var/tmp/oradiag_chuck
      
    • ADRホーム:

      /var/tmp/oradiag_chuck/diag/clients/user_chuck/host_4144260688_11
      

これらのディレクトリ選択肢のどれも使用不可で書込み不可の場合、ADRは作成されず、診断は格納されません。


関連項目

『Oracle Database Net Servicesリファレンス』

ADRCIの使用

ADRCIは、ADR内の診断データを表示し、インシデントおよび問題の情報をOracleサポートと共有できるZIPファイルにパッケージングできるコマンドライン・ツールです。ADRCIは、対話式にもスクリプトによっても使用できます。

問題とは、OCIまたはクライアント内のクリティカル・エラーです。各問題に問題キーが割り当てられます。インシデントとは、問題の1回の発生を指し、数値を使用した一意のインシデントIDで識別されます。各インシデントには、属性セット(ORAエラー番号、エラー・パラメータ値および類似情報)を含む問題キーが割り当てられます。2つのインシデントの問題キーが一致する場合、これらのインシデントの根本的原因は同じです。

次の各例では、Linuxオペレーティング・システムでのADRCIの使用方法を示しています。ARDCIコマンドでは大/小文字が区別されない点に注意してください。ユーザー入力はすべて太字体です。

例12-6 OCCIアプリケーション・インシデントに関するADRCIの使用方法

LinuxシステムでADRCIを起動するには、adrciコマンドを使用します。ADRCIの起動後、helpshow 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

関連項目

  • ADRCIの概要については、『Oracle Databaseユーティリティ』を参照してください。

  • 「Instant Client」


ADR作成の制御および障害診断の無効化

障害診断機能を無効化するには、診断の取得をオフにする必要があります。sqlnet.oraファイルの編集により、DIAG_ADR_ENABLEDパラメータおよびDIAG_DDE_ENABLEDパラメータの値をFALSEまたはOFFに変更します。デフォルト値はTRUEまたはONです。

OCCIシグナル・ハンドラをオフにし、標準オペレーティング・システム障害処理を再度有効化するには、sqlnet.oraファイルの編集により、対応するパラメータDIAG_SIGHANDLER_ENABLED=FALSEを追加します。


関連項目

『Oracle Database Net Servicesリファレンス』

クライアントの結果キャッシュ

クライアントの結果キャッシュは、繰り返し実行される問合せのレスポンス時間を向上させます。この機能では、クライアント・メモリーを使用して、データベースから実行およびフェッチされるSQL問合せの結果をキャッシュします。同じ問合せの後続の実行で、クライアント・キャッシュから結果をフェッチし、サーバーCPUの使用量を削減します。データベースのラウンドトリップが排除されるため、アプリケーションのレスポンス時間が向上します。

OCCIアプリケーションは、OCCI文キャッシュを有効にすることでクライアントの結果キャッシュ機能を透過的に使用します。キャッシュする必要のあるSELECT問合せは、/*+ result_cache */ヒントで注釈が付けられます。例12-8に、このようなSELECT問合せを使用するOCCI文オブジェクトの作成方法を示します。

例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();

使用方法のガイドライン、キャッシュの一貫性および制限は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。