22 OCIパイプライン化
パイプライン化によって、アプリケーションの全体的なスループットと応答性が向上します。Oracle Databaseリリース23c以降では、アプリケーションがインターリーブを効果的に利用し、アプリケーション・リクエストをオーバーラップすることでサーバーをビジー状態にして、サーバーによって返されるレスポンスを維持できるようにパイプライン化機能が導入されました。
関連項目:
OCIパイプライン関数22.1 ブロック化および非ブロック化の概念
この項では、ブロック化と非ブロック化の概念について説明します。
ブロック化機能
ネットワーク・ラウンドトリップを行うすべてのOCI関数は、リクエストとレスポンスが本質的に行われます。アプリケーションがリクエストを送信し、レスポンスを待機してから別のリクエストを送信します。
たとえば、OCIStmtExecute()
関数は、SQLを実行するリクエストを送信し、サーバーからのレスポンスを待機します。単一のOCI関数が一度に実行されます。前のリクエストを完了せずに別のOCI関数を同時に起動すると、操作はエラーになります。この実行モデルでは、現在のレスポンスと次のリクエストの間にサーバーがアイドル状態のままになります。また、クライアントは現在のリクエストとサーバーからのレスポンスの間にアイドル状態のままになります。
非ブロック化機能
非ブロック化機能は、アイドル時間中にインターリーブを使用して他の操作を実行することでメリットが得られるため、アプリケーションで役立ちます。このモデルでは、サーバーからのレスポンスをブロックするのではなく、アプリケーションが何をするかを選択できるようにすることで、アプリケーションの応答性が向上します。つまり、クライアントのアイドル時間です。
22.2 OCIパイプライン化の概要
この章では、パイプライン化機能について説明します。
パイプライン化の基本的な考え方は、サーバーをビジー状態に保ち、アプリケーションでインターリーブ・リクエストとレスポンスを適切に使用できるようにすることです。
アプリケーションは複数のリクエストを送信し続け、サーバーがキューを構築して1つずつ実行します。サーバーは、リクエストを受信した順序でクライアントにレスポンスを返します。
ノート:
パイプラインはレスポンスを読み取らないことが多いため、以前のSQLレスポンスからのデータを後続のリクエストへのバインド・データとして使用すると、依存関係が作成され、パイプラインが中断されます。次の図は、パイプライン内のサンプル・シナリオのエンドツーエンドの説明を示しています。
- OCIパイプライン・アプリケーションは、前の図に示すように7つの操作を同時に処理しています。
- サーバーは最初のリクエストを処理し、このリクエストに対するレスポンスがクライアントに到達します。レスポンスを読み取る準備ができました。
- 2番目のリクエストは、サーバー側で完了処理中です。
- 3から6のリクエストはサーバー側でキューに入れられ、サーバーによってまだ処理されません。
- クライアントは、7番目のリクエストをサーバーに非同期に送信しています。
OCIPipelineBegin()
: 操作のパイプライン・ブロックの開始を示します。OCIPipelineProcess()
: 操作を処理します。OCIPipelineEnd()
: 操作のパイプライン・ブロックの終了を示します。
- 上半分(リクエストを送信)
- 下半分(レスポンスを受信)
操作の上半分は、処理のためにサーバーに送信されるリクエストです。下半分は、サーバーから受信した応答です。レスポンスは、OCIPipelineProcess()
またはOCIPipelineEnd()
関数を使用して取得されます。
このアプローチでは、パイプライン操作ブロック内のパイプラインをサポートするすべてのOCI関数(たとえば、OCIStmtExecute()
、OCIStmtFetch2()
)が暗黙的にパイプライン化されます。
ノート:
パイプライン・モードでは、2つの連続した操作への依存性が解決または回避されることを保証するのは、アプリケーションの責任です。パイプライン操作の2つの隣接ブロックがサポートされています。次に、サンプル・コード・スニペットを示します。
OCIPipelineBegin();
OCIPipelineEnd();
;;;
OCIPipelineBegin();
OCIPipelineEnd();
OCIPipelineBegin();
OCIPipelineBegin();
OCIPipelineEnd();
OCIPipelineEnd();
ノート:
Oracle Database 23c以降、パイプライン化機能を提供するために、クライアントとサーバーの両方に拡張が行われています。22.2.1 OCIパイプライン化の有効化
この項では、OCI APIのパイプライン化を有効にする方法について説明します。
デフォルトでは、OCI APIはアプリケーションでパイプライン化に使用できません。パイプライン化を有効にするために、新しい属性OCI_PIPELINE_ENABLE
が導入されました。パイプライン機能は、アプリケーションが環境に属性を設定する場合にのみ使用できます。このモードが設定されていないOCIパイプライン関数をコールすると、エラーが返されます。
構文
boolean pipelineEnable = TRUE;
status = OCIAttrSet((dvoid *) envhp,
OCI_HTYPE_ENV,
(dvoid *) &sts,
(ub4) sizeof(sts),
OCI_ATTR_PIPELINE_ENABLE,
(OCIError *) errhp));
ほとんどのアプリケーションでは、ブロック化モードまたは非ブロック化モードでOCI関数が使用されます。OCI_ATTR_PIPELINE_ENABLE
属性は、ブロック化シナリオおよび非ブロック化シナリオでのパフォーマンスの低下を回避し、OCIアプリケーションでパイプライン化を無効にするスイッチのように機能します。
22.3 パイプライン操作のモード
この項では、パイプライン操作の様々なモードについて説明します。
OCIPipelineBegin()
関数は、操作のパイプライン・ブロックの開始をマークします。
パイプライン操作のモードは、パイプライン操作の実行方法を定義します。パイプライン・モードには2つのレベルがあります。各レベルは、2つの実行モードのいずれかを設定できます。
- 操作レベル・モード(
OCI_ATTR_PIPELINE_OP_MODE
):OCIPipelineEnd()
関数が実行されるまで、パイプライン操作のいずれかがエラーで失敗した場合でも、サーバーは操作の実行を続行します。アプリケーションでは、
OCIAttrSet
属性を使用して、操作のハンドル((svchp)
にOCI_ATTR_PIPELINE_OP_MODE
属性を設定する必要があります。例:
OCIStmtExecute
の場合、次の属性が設定されます。rc = OCIAttrSet((dvoid *) svchp, (ub4) OCI_HTYPE_SVC, (dvoid *) OCI_PIPELINE_CONT_ON_ERROR, (ub4) 0, (ub4) OCI_ATTR_PIPELINE_OP_MODE, (OCIError *) errhp); rc = OCIStmtExecute(svchp, stmthp, … , OCI_DEFAULT);
- ブロックレベル・モード(
OCI_PIPELINE_ABORT_ON_ERROR
): パイプライン操作ブロックの処理中にエラーが発生した場合、パイプライン操作は中断されます。パイプライン操作ブロックのいずれかの操作でエラーが返された場合、その操作に続くすべてのパイプライン操作が中断されます。
OCIPipelineEnd()
関数までのすべての操作は、クライアントにエラーを返します。操作が失敗する前に正常に実行された操作のレスポンスがアプリケーションに返されます。エラーが発生した操作も有効な操作です。エラー・ハンドルには、返されたエラーの詳細があります。
次の例は、OCI_PIPELINE_ABORT_ON_ERROR
モードの使用方法を示しています。
ブロック・レベル・モードがrc = OCIPipelineBegin(svchp, cbk, cbkCtx, errhp, OCI_PIPELINE_ABORT_ON_ERROR);
OCI_PIPELINE_ABORT_ON_ERROR
の場合、パイプライン操作の処理中にエラーが発生すると、パイプラインは異常終了します。パイプライン操作が終了するまで、サーバーは操作ごとにエラー(ORA-43610)を返します。
22.4 OCIPipelineOperation
OCIPipelineOperationは、パイプライン・ブロック内のパイプライン操作を表す不透明なハンドルです。OCIPipelineOperationには、実行されたOCI操作の情報が含まれます。操作インスタンスには、パイプライン操作の上(送信リクエスト)および下(受信レスポンス)の部分を完了するためのパラメータと状態が格納されます。
22.5 OCIパイプライン・ハンドルのライフ・サイクル
この項では、OCIパイプライン・ハンドルのライフ・サイクルについて説明します。
OCI_PIPELINE_OP_SENT
になります。レスポンスを受信すると、OCI_PIPELINE_OP_READY
状態に変わります。操作が暗黙的または明示的に完了すると、登録されたコールバックが処理されます。
ノート:
アプリケーションには、操作の下半分(レスポンスを受信)が実行されるまでOCIコール・パラメータを有効にしておく責任があります。OCIStmtExecute()
関数の完了後にINバインドの値を変更できます。
ノート:
同じアウトバインドまたは定義パラメータは、操作の下半分(レスポンスを受信)の後にオーバーライドされます。22.6 OCIパイプライン属性
この項では、OCIパイプライン属性をリストし、説明します。
OCIパイプライン属性は次のとおりです。
OCI_ATTR_PIPELINE_PROCESS_FIRST
例22-1 例:
OCIPipelineOperationID first;
status = OCIAttrGet (svchp, OCI_HTYPE_SVC, &first, NULL,
OCI_ATTR_PIPELINE_PROCESS_LAST, errhp);
OCI_ATTR_PIPELINE_PROCESS_LAST
例22-2 例:
OCIPipelineOperationID last;
status = OCIAttrGet (svchp, OCI_HTYPE_SVC, &last, NULL,
OCI_ATTR_PIPELINE_PROCESS_LAST, errhp);
OCI_ATTR_PIPELINE_DEPTH
例22-3 例:
ub4 depth = 300;
status = OCIAttrSet (svchp, OCI_HTYPE_SVC, &depth, size of(depth), OCI_ATTR_PIPELINE_DEPTH, errhp);
OCI_ATTR_PIPELINE_HANDLE
例22-4 例:
OCIStmt *updateHandle;
status = OCIAttrGet (operation, OCI_HTYPE_OPERATION, &updateHandle, NULL, OCI_ATTR_PIPELINE_HANDLE, errhp);
OCI_ATTR_PIPELINE_HANDLE_TYPE
例22-5 例:
ub4 handleType = 0;status = OCIAttrGet (operation, OCI_HTYPE_OPERATION, &handleType, sizeof(handleType),
OCI_ATTR_PIPELINE_HANDLE_TYPE, errhp);
22.7 パイプライン化をサポートするOCI関数
ノート:
OCIStmtExecute()
、OCIStmtFetch()
およびOCIStmtFetch2()
リクエストにOCIオブジェクトが含まれている場合は、パイプライン化できません。OCITransCommit()
およびOCITransRollback()
トランザクション・リクエスト・パイプライン化関数は、ローカル・トランザクションのみをサポートします。
OCIStmtExecute()
OCIStmtFetch()
OCIStmtFetch2()
OCITransCommit
OCITransRollback
OCILobAppend
OCILobArrayWrite
OCILobArrayRead
OCILobClose
OCILobCopy2
OCILobCreateTemporary
OCILobErase2
OCILobFileClose
OCILobFileCLoseAll
OCILobFileExists
OCILobFileIsOpen
OCILobFileOpen
OCILobFreeTemporary
OCILobGetChunkSize
OCILobGetLength2
OCILobOpen
OCILobIsOpen
OCILobLoadFromFile2
OCILobOpen
OCILobRead
OCILobRead2
OCILobTrim2
OCILobWrite
OCILobWrite2
OCILobWriteAppend2
22.8 パイプライン化機能を使用する場合
このセクションでは、パイプライン化機能を使用するタイミングについて説明します。
パイプライン化機能は、多数の小さな操作が連続して実行されている場合に便利です。
- OCIStmtExecute
- DDL、DML、問合せ、PL/SQL、配列挿入、戻り句を含むDML
- 完全フェッチ、複数のフェッチ
- 2つの異なる文ハンドルからのフェッチをパイプライン化
パイプライン化は、サーバーが遠い場合により高い効果があります。つまり、ネットワーク待機時間が長い場合です。
操作に、前の操作の結果に対する暗黙的または明示的な依存関係がある場合、パイプライン化の効果は低下します。このような場合、クライアントは同期ポイントを導入し、クライアントまたはサーバーの完全往復を待って必要な結果を取得する必要があります。
- 暗黙的な依存関係: 文の実行後に、同じ文ハンドルに対するフェッチを実行
- 明示的な依存関係: 前の操作の結果は、次の操作の実行をバインドするために使用