プライマリ・コンテンツに移動
Oracle® Database SecureFilesおよびラージ・オブジェクト開発者ガイド
12cリリース1 (12.1)
B71284-05
目次へ移動
目次
索引へ移動
索引

前
次

12 高度な設計時の考慮事項

より高度なアプリケーション開発時の問題について設計上の考慮事項があります。

内容は次のとおりです。

LOBバッファリング・サブシステム

データベースは、高度なOCIベースのアプリケーションに対してLOBバッファリング・サブシステム(LBS)を提供する必要があります。このようなアプリケーションには、データ・カートリッジ、Webサーバー、および1つ以上のLOBの内容をクライアント・アドレス領域にバッファリングする必要がある他のクライアント・ベースのアプリケーションなどがあります。

バッファリング・サブシステムに対するクライアント側の最大使用時のメモリー要件は512KBです。これはまた、バッファリングされたアクセス用として有効であるLOBの単一の読取りまたは書込み操作に対して指定できる最大量でもあります。

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

LOBバッファリングのメリット

バッファリングは、LOBの特定の部分に一連の小規模な読取りおよび書込みを実行する(繰り返し行われる場合が多い)クライアント・アプリケーションに対して特に、次の点で有効です。

  • バッファリングによってサーバーへの遅延書込みが可能になります。クライアントのアドレス空間のLOBバッファに数回分の書込みをバッファリングし、最後にサーバーへフラッシュできます。これによって、クライアント・アプリケーションとサーバー間のネットワーク・ラウンドトリップ数が減り、LOB更新の全体的なパフォーマンスが向上します。

  • バッファリングによって、サーバー上のLOBの総更新回数が減り、LOBのバージョン数とロギング量が減ります。これによって、全体的なLOBパフォーマンスおよびディスク使用率が向上します。

LOBバッファリングの使用上のガイドライン

バッファリングされたLOB操作では、次のことに注意する必要があります。

  • LOBバッファの内容を明示的にフラッシュします。

    LOBバッファリング・サブシステムは、キャッシュではありません。LOBバッファの内容がサーバー上のLOB値とは異なる場合があります。LOBバッファの内容を明示的にフラッシュしなければ、サーバーの実際のLOBに反映された、バッファリングを利用した書込み結果を参照できません。

  • バッファリングされたLOB操作のエラー・リカバリはユーザーの責任です。

    実際のLOBの更新には遅れがあるため、バッファリングされた特定の読取りまたは書込み操作のエラー・レポートは、次回のサーバー・ベースのLOBへのアクセスまで行われません。

  • LOBバッファリングは、シングル・ユーザー、シングル・スレッド形式です。

    LOBのバッファリング操作を含むトランザクションは、ユーザー・セッション間では移行できません。LBSは、シングル・ユーザー、シングル・スレッドのシステムであるためです。

  • ロールバックするための論理セーブポイントを保持します。

    Oracleでは、バッファリングされたLOB操作に対するトランザクション・サポートは保証されていません。バッファリングされたLOB更新に対してトランザクション・セマンティクスを保証するには、アプリケーションで論理セーブポイントを保持して、エラーの際には、LOBに対するすべての変更をロールバックできるようにする必要があります。論理セーブポイント内のバッファリングされたLOBの更新を常にラップする必要があります(「OCI: LOBバッファリングの例」を参照)。

  • バッファリング・サブシステムを使用しない同一トランザクションの他の操作によって、LOBが更新されないことを確認します。

    どのトランザクションにおいても、バッファリングされた書込みを使用してLOBの更新を開始した後は、バッファリング・サブシステムを使用しない同一のトランザクション内で、他の操作によって同じLOBが更新されないようにする必要があります。

    この状況は、サーバー・ベースのLOBを更新するSQL文を使用することで発生する可能性があります。Oracleではこのような操作を区別できないため、防止することができません。これは、アプリケーションの正確さと整合性に影響します。

  • バッファリングを使用可能にしたLOBロケータを更新します。

    LOBに対するバッファリング操作は、通常の場合と同様にロケータを介して実行されます。バッファリングを使用可能にしたロケータは、そのロケータを使用したLOBへの書込み操作が実行されるまでは、読取り一貫性のあるLOBを提供します。読取り一貫性のあるロケータも参照してください。

    バッファリングされた書込み用として使用されることによってロケータが更新済ロケータになると、このロケータは、バッファリングされたサブシステムを介して確認できるように最新バージョンのLOBへのアクセスを常に提供します。また、バッファリングにより、この更新済ロケータの重要性が増します。つまり、LOBに対してバッファリングされた書込みはすべてこの更新済ロケータを介してのみ実行可能になります。バッファリングを使用可能にした他のロケータを介してLOBに書き込もうとすると、エラーが戻されます。更新済ロケータを介したLOB更新の例も参照してください。

  • バッファリングを使用可能にしたLOBロケータにIN OUTまたはOUTパラメータを渡します。

    バッファリングを使用可能にした更新済ロケータは、PL/SQLプロシージャへのINパラメータとして渡せます。ただし、IN OUTまたはOUTパラメータを渡したり、更新済ロケータを戻そうとすると、エラーが発生します。

  • バッファリングを使用可能にした更新済ロケータを別のロケータに割り当てることはできません。

    ロケータの割当ては、OCILobAssign()の使用、PL/SQL変数の割当て、オブジェクトがLOB属性を含む場合のOCIObjectCopy()の使用など、様々な場合に発生します。バッファリングを使用可能にしている読取り一貫性のあるロケータを、バッファリングを使用可能にしていないロケータに割り当てると、対象となるロケータに対してバッファリング機能がオンになります。同様に、バッファリングを使用可能にしていないロケータを、バッファリングを使用可能にしているロケータに割り当てると、対象となるロケータに対してバッファリング機能がオフになります。

    また、もともとバッファリングを使用可能にしているロケータに対してSELECTを発行した場合、ロケータは新しいロケータ値で上書きされ、バッファリング機能はオフになります。

  • 複数のロケータが同じLOBを指す場合は、1つのみのロケータでバッファリングを使用可能にします。

    複数の異なるロケータが同じLOBを指す場合は、1つのロケータでのみバッファリングが使用可能になるようにする必要があります。それ以外の場合、LOBの内容は保証されません。

  • バッファリングを使用可能にしたLOBでは、バイト値0 (ゼロ)の充填文字または空白の作成が必要になるような追加はサポートされていません。

    バッファリングされた書込みを使用したLOB値への追加は、これらの書込みを開始するオフセットが、BLOB(またはCLOB/NCLOB)の終わりから1バイト(または1文字)の場合にのみ可能です。バッファリング・サブシステムでは、サーバー・ベースのLOB内に対してバイト値0(ゼロ)の充填文字または空白の作成が必要になるような追加はサポートされていません。

  • CLOBの場合、ロケータ・バインド変数に対するクライアント側のキャラクタ・セット・フォームがサーバー内のLOBのものと同じである必要があります。

    ほとんどのOCI LOBプログラムでは、この条件が必要です。ロケータがリモート・データベースから選択された場合は例外で、この場合は、OCIプログラムが現在アクセスしているデータベースからのキャラクタ・セット・フォームと異なっている場合があります。この場合は、エラーが戻されます。キャラクタ・セット・フォームがユーザーによって入力されていない場合、OracleではSQLCS_IMPLICITが使用されます。

LOBバッファリング・サブシステムの使用方法

LOBバッファリング・サブシステムには、多くの詳細があります。

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

LOBバッファの物理構造

各ユーザー・セッションの構造は、次のとおりです。

  • 各ユーザー・セッションには、16ページの固定ページ・プールがあります。このページは、そのセッションからバッファリング・モードでアクセスされるすべてのLOBによって共有されます。

  • 各ページ・サイズは、最大32KB(文字単位ではない)の固定サイズです。ページ・サイズ = n x CHUNK = 約32KBとなります。

LOBバッファは、これらのページの1つ以上で構成されています(セッションごとに最大16)。バッファリングされた特定の読取りまたは書込み操作に指定する必要がある最大量は512KBですが、状況が異なる場合、読取り/書込み可能な最大量は小さくなる可能性があります。

LOBバッファリング・サブシステムの使用例

LOBは、固定サイズの論理リージョンに分割されます。各ページはこれらの固定サイズ・リージョンの1つにマップされ、基本的には、インメモリー・コピーにもマップされます。入力オフセット、および読取りまたは書込み操作用に指定した量に従って、ページ・プールから1ページ以上の空きページがLOBのバッファに割り当てられます。空きページは、バッファリングされた読取りまたは書込み操作によって読取りまたは書込みが行われていないページです。

たとえば、ページ・サイズを32KBとします。

  • 入力オフセットが1000で、指定された読取り/書込みの量が30000の場合、Oracleでは、LOBの最初の32KBのリージョンがLOBバッファ内のページに読み取られます。

  • 入力オフセットが33000で、読取り/書込みの量が30000の場合、LOBの次の32KBのリージョンがページに読み取られます。

  • 入力オフセットが1000で、読取り/書込みの量が35000の場合、LOBのバッファは2ページになります。最初の部分はLOBの1から32KBまでのリージョンに、次の部分は32KB + 1から64KBまでのリージョンにマップされます。

ページとLOBリージョン間のこのマッピングは、ページに別のリージョンがマップされるまでの一時的なものです。LOBバッファ内で完全には使用可能ではないLOBのリージョンにアクセスしようとすると、ページ・プール内で使用可能な任意の空きページがLOBバッファに割り当てられます。ページ・プール内で使用可能な空きページがない場合、ページは次のように再配置されます。LOBバッファ内で未修正のページ間で最近最も使用されていないページが削除され、現在の操作に再配置されます。

LOBバッファ内にこのようなページがない場合、同じセッション内でバッファリングされている他のLOBの未修正のページ間で最近最も使用されていないページが削除されます。それでもこのようなページがない場合は、ページ・プール内のすべてのページが変更済であることを意味し、現在アクセスしているLOBまたは他のLOBの1つをフラッシュする必要があります。この状況はエラーとしてユーザーに通知されます。変更済ページが暗黙的にフラッシュされて再配置されることはありません。これらは明示的にフラッシュするか、LOBでのバッファリングを無効化することによって破棄できます。

前述の内容をわかりやすく説明するために、バッファリング・モードでL1およびL2の2つのLOBにアクセス中で、各LOBには8ページのバッファがある場合を考えてみます。L1のバッファの8ページのうち6ページが使用済で、残りの2ページには、サーバーから読み取られた未修正データが入っているとします。L2のバッファの状態も同様であるとします。ここで、L1の次のバッファリング操作のために、L1のバッファの未修正の2ページからLRUページが再配置されます。L1のバッファの8ページすべてがLOB書込みのために使用されると、LRUの方針に基づいて、L2のバッファから未修正の2ページを再配置することによって、L1に対してさらに2つの操作が可能になります。ただし、L1またはL2でさらにバッファリング操作を実行しようとすると、エラーが戻されます。

バッファがすべて使用済の場合に、バッファリングされたLOBに別の読取りまたは書込みを行うと、次のエラーが発生します。

Error 22280: no more buffers available for operation

これには、2つの原因が考えられます。

  1. バッファ・プールにあるすべてのバッファが、以前の操作で使用された。

    この場合、LOBの更新に使用されているロケータを介してLOBをフラッシュします。

  2. 以前バッファリングされた更新操作がないのにLOBをフラッシュしようとしている。

    この場合、バッファをフラッシュする前に、バッファリングが可能なロケータを介してLOBに書き込みます。

LOBバッファのフラッシュについて

フラッシュとは、一連のプロセスを指します。ロケータを介してバッファ内のLOBにデータを書き込むと、そのロケータは、更新済ロケータになります。更新済ロケータを介してバッファのLOBデータを更新すると、フラッシュ・コールは、次のことを行います。

  • LOBのバッファの使用済ページをサーバー・ベースのLOBに書き込み、それによってLOB値を更新します。

  • 更新済ロケータをリセットして、読取り一貫性のあるロケータにします。

  • フラッシュ済バッファを解放するか、バッファのページの状態を使用済から変更されていない状態に戻します。

フラッシュ後、ロケータは、読取り一貫性のあるロケータとなり、別のロケータに割り当てることができます(L2 := L1)。

たとえば、L1およびL2の2つのロケータがあるとします。これらは両方とも読取り一貫性のあるロケータであり、サーバー内のLOBデータの状態との一貫性があるとします。バッファに書込みをしてLOBを更新すると、L1は更新済ロケータになります。L1およびL2は、その時点で、別のバージョンのLOB値を参照しています。サーバーのLOBを更新する場合は、L1を使用して、L2で得た読取り一貫性のある状態を維持する必要があります。フラッシュ操作によって、フラッシュに使用したロケータに新しいスナップショット環境が書き込まれます。重要な点は、LOBバッファをフラッシュするとき、更新済ロケータ(L1)を使用する必要があることです。読取り一貫性のあるロケータをフラッシュしようとすると、エラーが発生します。

LOBバッファのフラッシュ

LOBバッファのフラッシュに使用する方法によって、バッファ内のキャッシュがクリアされるかどうかが決定されます。また、パフォーマンスに次のように影響します。

  • デフォルト・モードの場合、データは、フラッシュ操作が行われたときに変更されたページ内に維持されます。その際、同じバイト範囲に対して読取りまたは書込みを行う場合、サーバーに対するラウンドトリップは必要ありません。このコンテキストでバッファをフラッシュしてもバッファ内のデータはクリアされません。また、フラッシュされたバッファによって占有されているメモリーがクライアントのアドレス領域に戻されることもありません。

    注意:

    変更されていないページは、必要に応じてページ・アウトされます。

  • 2つ目のケースでは、OCILobFlushBuffer()のフラグ・パラメータをOCI_LOB_BUFFER_FREEに設定して、バッファ・ページを解放すると、メモリーは、クライアントのアドレス領域に戻されます。この方法を使用してバッファをフラッシュすると、サーバー上でLOB値が更新され、読取り一貫性のあるロケータが戻され、バッファ・ページが解放されます。

更新済LOBのフラッシュについて

LOBバッファリング・システムを使用して更新したLOBは、状況によってはフラッシュする必要があります。

  • トランザクションをコミットする前

  • 現行のトランザクションから別のトランザクションに移行する前

  • LOBのバッファリング操作を使用禁止にする前

  • 外部コールアウトの実行からPL/SQLのファンクション、プロシージャまたはメソッドのコールに戻る前

    外部コールアウトが、PL/SQLブロックからコールされ、ロケータがパラメータとして渡される場合、バッファリングを使用可能にするコールを含めてすべてのバッファリング操作をコールアウトの内部で行う必要があることに注意してください。

    「更新済LOBのフラッシュ」で説明されている手順に従ってください

データベースでLOBバッファが暗黙的にフラッシュすることはないことに注意してください。

更新済LOBのフラッシュ

外部コールアウトがPL/SQLブロックからコールされ、ロケータがパラメータとして渡される状況である場合、バッファリングを使用可能にするコールを含めてすべてのバッファリング操作を次の順序でコールアウトの内部で行う必要があります。「更新済LOBのフラッシュについて」を参照してください。

  1. 外部コールアウトを呼び出します。
  2. ロケータをバッファリング可能にします。
  3. ロケータを使用して読取り/書込みを行います。
  4. LOBをフラッシュします。
  5. ロケータをバッファリング使用禁止にします。
  6. PL/SQLのコール側のファンクション、プロシージャまたはメソッドに戻ります。

バッファリング対応のロケータの使用

バッファリング対応のロケータを使用できる場合と使用できない場合があることに注意してください。

  • バッファリング対応ロケータを使用できる場合

    • OCI - バッファリングが使用可能になっているロケータは、次のOCI APIとのみ使用できます。

      OCILobRead2()、OCILobWrite2()、OCILobAssign()、OCILobIsEqual()、OCILobLocatorIsInit()、OCILobCharSetId()、OCILobCharSetForm()

  • バッファリング対応ロケータを使用できない場合

    次のOCI APIを、バッファリングが使用可能になっているロケータとともに使用すると、エラーが戻されます。

    • OCI:

      OCILobCopy2()、OCILobAppend()、OCILobErase2()、OCILobGetLength2()、OCILobTrim2()、OCILobWriteAppend2()

      これらのAPIは、バッファリングが使用可能になっていないロケータとともに使用したときに、そのロケータが示すLOBがその他のロケータを介してバッファリング・モードでアクセスされている場合にも、エラーを戻します。

    • PL/SQL (DBMS_LOB):

      入力LOBロケータでバッファリングが使用可能になっている場合、DBMS_LOB APIからエラーが戻されます。

    その他のすべてのロケータの場合と同様に、LOBバッファリングを使用可能にしたロケータは、トランザクションにまたがることはできません。

再選択を避けるためのロケータ状態の保存

さらにLOBバッファに書き込む前に、LOBの現在の状態を保存できます。LOBバッファリングの使用中に更新を実行すると、既存のバッファへの書込みで、サーバーへのラウンドトリップは不要なため、ロケータのスナップショット環境はリフレッシュされません。

これは、LOBバッファリングを使用しないで直接LOBを更新する場合にはあてはまりません。その場合、更新するたびにサーバーへのラウンドトリップが必要となるため、ロケータのスナップショットはリフレッシュされます。

LOBバッファを使用して書き込まれたLOBの状態を保存するには、次の手順に従います。

  1. LOBをフラッシュして、LOBおよびロケータ(L1)のスナップショット環境を更新します。この時点では、ロケータ(L1)の状態とLOBは同じです。
  2. フラッシュおよび更新に使用されたロケータ(L1)を別のロケータ(L2)に割り当てます。この時点では、2つのロケータの状態(L1およびL2)とLOBはすべて同じです。

これで、L2は読取り一貫性のあるロケータになり、フラッシュが行われるまではL1を介して行われた変更にアクセスできますが、フラッシュ後はアクセスできません。この割当てによって、ロケータをL2に選択しなおすために、サーバーにアクセスする必要がなくなります。

OCI: LOBバッファリングの例

次に示すOCIの擬似コード例は、Oracle Databaseサンプル・スキーマに含まれるPMスキーマに基づいています。

この例では、LOBの例の表: PMスキーマのprint_media表で説明されているprint_media表を使用します

OCI_BLOB_buffering_program()
{
   int            amount;
   int            offset;
   OCILobLocator  lbs_loc1, lbs_loc2, lbs_loc3;
   void          *buffer;
   int            bufl;

   -- Standard OCI initialization operations - logging on to
   -- server, creating and initializing bind variables...

   init_OCI();

   -- Establish a savepoint before start of LOB buffering subsystem
   -- operations
   exec_statement("savepoint lbs_savepoint");

   -- Initialize bind variable to BLOB columns from buffered
   -- access:
   exec_statement("select ad_composite into lbs_loc1 from Print_media
       where ad_id = 12001");
   exec_statement("select ad_composite into lbs_loc2 from Print_media
       where ad_id = 12001 for update");
   exec_statement("select ad_composite into lbs_loc2 from Print_media
       where ad_id = 12001 for update");

   -- Enable locators for buffered mode access to LOB:
   OCILobEnableBuffering(..., lbs_loc1);
   OCILobEnableBuffering(..., lbs_loc2);
   OCILobEnableBuffering(..., lbs_loc3);

   -- Read 4K bytes through lbs_loc1 starting from offset 1:
   amount = 4096; offset = 1; bufl = 4096;
   OCILobRead2(.., lbs_loc1, &amount, 0, offset, buffer, bufl, ...);
      if (exception)
          goto exception_handler;
          -- This reads the first 32K bytes of the LOB from
          -- the server into a page (call it page_A) in the LOB
          -- client-side buffer.
          -- lbs_loc1 is a read-consistent locator.

          -- Write 4K of the LOB throgh lbs_loc2 starting from
          -- offset 1:
          amount = 4096; offset = 1; bufl = 4096;
          buffer = populate_buffer(4096);
          OCILobWrite2(.., lbs_loc2, &amount, 0, offset, buffer, bufl, ..);

      if (exception)
          goto exception_handler;
          -- This reads the first 32K bytes of the LOB from
          -- the server into a new page (call it page_B) in the
          -- LOB buffer, and modify the contents of this page
          -- with input buffer contents.
          -- lbs_loc2 is an updated locator.

          -- Read 20K bytes through lbs_loc1 starting from
          -- offset 10K
          amount = 20480; offset = 10240;
          OCILobRead2(.., lbs_loc1, &amount, 0, offset, buffer, bufl, ..);

      if (exception)
        goto exception_handler;
          -- Read directly from page_A into the user buffer.
          -- There is no round-trip to the server because the
          -- data is in the client-side buffer.

          -- Write 20K bytes through lbs_loc2 starting from offset
          -- 10K
          amount = 20480; offset = 10240; bufl = 20480;
          buffer = populate_buffer(20480);
          OCILobWrite2(.., lbs_loc2, &amount, 0, offset, buffer, bufl, ..);

      if (exception)
          goto exception_handler;
          -- The contents of the user buffer are now written
          -- into page_B without involving a round-trip to the
          -- server.  This avoids making a new LOB version on the
          -- server and writing redo to the log.

          -- The following write through lbs_loc3 also
          -- results in an error:
          amount = 20000; offset = 1000; bufl = 20000;
          buffer = populate_buffer(20000);
          OCILobWrite2(.., lbs_loc3, amount, 0, offset,buffer, bufl, ..);

      if (exception)
          goto exception_handler;
          -- No two locators can be used to update a buffered LOB
          -- through the buffering subsystem

      -- The following update through lbs_loc3 also
      -- results in an error
      OCILobFileCopy(.., lbs_loc3, lbs_loc2, ..);

      if (exception)
          goto exception_handler;
          -- Locators enabled for buffering cannot be used with
          -- operations like Append, Copy, Trim and so on
          -- When done, flush the LOB buffer to the server:
      OCILobFlushBuffer(.., lbs_loc2, OCI_LOB_BUFFER_NOFREE);

      if (exception)
         goto exception_handler;
         -- This flushes all the modified pages in the LOB buffer,
         -- and resets lbs_loc2 from updated to read-consistent
         -- locator. The modified pages remain in the buffer
         -- without freeing memory.  These pages can be aged
         -- out if necessary.

      -- Disable locators for buffered mode access to LOB */
      OCILobDisableBuffering(..., lbs_loc1);
      OCILobDisableBuffering(..., lbs_loc2);
      OCILobDisableBuffering(..., lbs_loc3);

      if (exception)
         goto exception_handler;
         -- This disables the three locators for buffered access,
         -- and frees up the LOB buffer resources.
        exception_handler:
      handle_exception_reporting();
      exec_statement("rollback to savepoint lbs_savepoint");
}

OPENおよびCLOSEインタフェースを使用した永続LOBのオープン

OPENおよびCLOSEインタフェースを使用すると、永続LOBインスタンスを明示的にオープンできます。

OPENインタフェースを使用してLOBインスタンスをオープンすると、そのインスタンスはCLOSEインタフェースを使用してLOBを明示的にクローズするまでオープンされたままになります。ISOPENインタフェースを使用すると、永続LOBがオープンしているかどうかを確認できます。

LOBのオープン状態はLOBロケータではなくLOBインスタンスに関連付けられていることに注意してください。ロケータには、それが指すLOBインスタンスがオープンしているかどうかを示す情報は格納されません。

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

LOBの明示的なオープンによる索引のパフォーマンス上のメリット

LOBインスタンスを明示的にオープンすると、索引付き列の永続LOBのパフォーマンスを改善できます。

LOBインスタンスを明示的にオープンしない場合、LOBに対する変更が行われるたびにLOBインスタンスが暗黙的にオープンおよびクローズされます。ドメイン索引に対するトリガーは、LOBがクローズされるたびに起動されます。この場合、LOBインスタンスに対する変更が行われるとすぐにLOB上のドメイン索引が更新されます。ドメイン索引は常に有効であり、いつでも使用できます。

LOBインスタンスを明示的にオープンすると、LOBを明示的にクローズするまで索引トリガーは起動されません。この方法を使用すると、LOBを明示的にクローズするまで不要な索引付けイベントが発生しなくなり、索引列のパフォーマンスを改善できます。LOB列のすべての索引は、そのLOBを明示的にクローズするまで無効であることに注意してください。

明示的にオープンしたLOBインスタンスのクローズ

LOBインスタンスを明示的にオープンした場合は、トランザクションをコミットする前にLOBをクローズする必要があります。

オープン状態にあるLOBインスタンスに対するトランザクションをコミットすると、エラーが発生します。このエラーが発生すると、LOBインスタンスが暗黙的にクローズされてLOBインスタンスに対する変更が保存され、トランザクションがコミットされますが、LOB列の索引は更新されません。この場合は、LOB列に対する索引を再作成する必要があります。

その後トランザクションをロールバックすると、LOBインスタンスは前の状態にロールバックされますが、LOBインスタンスは明示的にオープンされません。

次の状況で明示的にオープンしたLOBインスタンスは、すべてクローズする必要があります。

  • トランザクションを起動するDML文の間(SELECT ... FOR UPDATEおよびCOMMITを含む)

  • 自律型トランザクション・ブロックの中

  • セッションの終了前(関係するトランザクションがないとき)

    LOBインスタンスを明示的にクローズしない場合、セッションの終了時に暗黙的にクローズされ、索引トリガーは起動されません。

明示的にオープンしたLOBがオープン状態かクローズ状態かを追跡してください。次の場合にはエラーが発生します。

  • すでに明示的にオープンしているLOBインスタンスを明示的にオープンする場合

  • すでに明示的にクローズしているLOBインスタンスを明示的にクローズする場合

このエラーは、LOBインスタンスへのアクセスに同じロケータを使用しているかどうかに関係なく発生します。

読取り一貫性のあるロケータ

Oracle DatabaseのLOBに対する読取り一貫性メカニズムは、その他すべてのスカラー量に対するデータベースの読取りおよび更新のための読取り一貫性メカニズムと同じです。読取り一貫性に関する一般的な説明は、『Oracle Database概要』を参照してください。

LOBロケータの場合、読取り一貫性にはいくつかの特別な用途があるため、正しく理解しておく必要があります。次の各項では、読取り一貫性について説明し、互いに関連付けながら参照する必要がある例を示します。

また、これらの例では、LOBの例の表: PMスキーマのprint_media表で説明されているprint_media表を参照します。

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

読取り一貫性のあるロケータになる、SELECTされたロケータ

FOR UPDATE句の有無にかかわらず、SELECTされたロケータは読取り一貫性のあるロケータとなり、LOB値がそのロケータによって更新されるまでは読取り一貫性のあるロケータとして存在します。

読取り一貫性のあるロケータには、SELECT操作の実行時点のスナップショット環境が含まれます。

これには複雑な意味があります。たとえば、SELECT操作によって読取り一貫性のあるロケータ(L1)を作成したとします。L1によって永続LOB値を読み取るときは、次のことに注意してください。

  • SELECT文にFOR UPDATEが含まれていても、SELECT文実行時点のLOBが読み取られます。

  • 同じトランザクションの別のロケータ(L2)によってLOB値が更新されても、L1ではL2の更新が認識されません。

  • L1では、別のトランザクションによってLOBにコミットされた更新も認識されません。

  • 読取り一貫性のあるロケータL1が別のロケータL2にコピーされた場合(たとえば、2つのロケータ変数のPL/SQL割当てL2:= L1によって)、L2L1と同様に読取り一貫性のあるロケータとなり、読み取られるデータはL1に対するSELECT実行時点のデータとなります。

複数のロケータが存在していることを利用して、変化するLOB値のそれぞれの値にアクセスできます。ただし、その場合は、どのロケータでどの値にアクセスしているかを常に理解しておく必要があります。

LOBの更新および読取り一貫性の例

読取り一貫性のあるロケータでは、SELECT実行時期に関係なく同じLOB値が提供されます。

次に、読取り一貫性と更新の関係を簡単な例で示します。LOBの例の表: PMスキーマのprint_media表で説明されているprint_media表とPL/SQLを使用して、clob_selectedclob_updateおよびclob_copiedの3つのCLOBインスタンスをロケータとして作成します。

次のコード例では、t1からt6が次のように処理されます。

  • 最初のSELECT INTOの実行時(t1)に、ad_sourcetext内の値がロケータclob_selectedに対応付けられます。

  • 次の操作(t2)で、ad_sourcetext内の値がロケータclob_updatedに対応付けられます。t1t2の間ではad_sourcetextの値が変わらないため、clob_selectedおよびclob_updatedの両方のロケータは、異なる時点でのスナップショットを反映していても、同じ値を持つ読取り一貫性のあるロケータとなります。

  • 3つ目の操作(t3)では、clob_selectedの値がclob_copiedにコピーされます。この時点で、3つのロケータが同じ値になります。例では、一連のDBMS_LOB.READ()コールがこれを示しています。

  • t4で、プログラムはDBMS_LOB.WRITE()を使用してclob_updated内の値を変更します。DBMS_LOB.READ()が新しい値を示します。

  • ただし、clob_selectedを介した値のDBMS_LOB.READ()は(t5)、これが読取り一貫性のあるロケータで、SELECTの実行時と同じ値が引き続き参照されることを示します。

  • 同様に、clob_copiedを介した値のDBMS_LOB.READ()は(t6)、これが読取り一貫性のあるロケータで、clob_selectedと同じ値が引き続き参照されることを示します。

例12-1

INSERT INTO PRINT_MEDIA VALUES (2056, 20020, EMPTY_BLOB(), 
    'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL);

COMMIT;

DECLARE
  num_var           INTEGER;
  clob_selected     CLOB;
  clob_updated      CLOB;
  clob_copied       CLOB;
  read_amount       INTEGER;
  read_offset       INTEGER;
  write_amount      INTEGER;
  write_offset      INTEGER;
  buffer            VARCHAR2(20);

BEGIN
  -- At time t1:
  SELECT ad_sourcetext INTO clob_selected
     FROM Print_media
     WHERE ad_id = 20020;

  -- At time t2:
  SELECT ad_sourcetext INTO clob_updated
     FROM Print_media
     WHERE ad_id = 20020
     FOR UPDATE;

  -- At time t3:
  clob_copied := clob_selected;
  -- After the assignment, both the clob_copied and the
  -- clob_selected have the same snapshot as of the point in time
  -- of the SELECT into clob_selected

  -- Reading from the clob_selected and the clob_copied does
  -- return the same LOB value. clob_updated also sees the same
  -- LOB value as of its select:
  read_amount := 10;
  read_offset := 1;
  dbms_lob.read(clob_selected, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_selected value: ' || buffer);
  -- Produces the output 'abcd'

  read_amount := 10;
  dbms_lob.read(clob_copied, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_copied value: ' || buffer);
  -- Produces the output 'abcd'

  read_amount := 10;
  dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_updated value: ' || buffer);
  -- Produces the output 'abcd'

  -- At time t4:
  write_amount := 3;
  write_offset := 5;
  buffer := 'efg';
  dbms_lob.write(clob_updated, write_amount, write_offset, buffer);

  read_amount := 10;
  dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_updated value: ' || buffer);
  -- Produces the output 'abcdefg'

  -- At time t5:
  read_amount := 10;
  dbms_lob.read(clob_selected, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_selected value: ' || buffer);
  -- Produces the output 'abcd'

  -- At time t6:
  read_amount := 10;
  dbms_lob.read(clob_copied, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_copied value: ' || buffer);
  -- Produces the output 'abcd'
END;
/

更新済ロケータを介したLOB更新の例

LOBロケータ(L1)を介して永続LOBの値を更新する場合、現在のスナップショット環境が含まれるようL1が更新されます。

このスナップショットは、ロケータL1を介してLOB値に対する操作が完了した時点のものです。このため、L1は更新済ロケータと呼ばれます。この操作によって、LOB値に対して行った変更を、同じロケータL1による次の読取り時に参照できます。

注意:

LOB値を読み取ることのみのためにロケータが使用される場合、ロケータのスナップショット環境は更新されません。更新されるのは、PL/SQL DBMS_LOBパッケージまたはOCI LOB APIを使用して、ロケータを介してLOB値を変更した場合のみです。

別のトランザクションによってコミットされた更新は、そのトランザクションがコミット読取りトランザクションであり、他のトランザクションのコミット後にL1を使用してLOB値を更新する場合のみに、L1によって認識されます。

注意:

永続LOBの値を更新すると、最新のLOB値が常に変更されます。

OCI LOB APIやPL/SQL DBMS_LOBパッケージなどの使用可能なメソッドによって永続LOBの値を更新することは、LOB値を更新して、新しいLOB値を参照するロケータを再選択することになります。

注意:

どのような手段でもLOBロケータを選択すればロケータから読み取れますが、書き込めません。

SQLを介したLOB値の更新は、単なるUPDATE文です。UPDATE文によって行われた変更をロケータが認識できるように、LOBロケータを再選択するか、UPDATE文でRETURNING句を使用するかは、ユーザーが決定します。LOBロケータを再選択するかRETURNING句を使用しないかぎり、最新の値を読み取っていない場合でも最新の値を読み取っていると判断する場合があります。このような理由により、SQL DMLをOCIおよびDBMS_LOBピース単位操作と混同しないようにする必要があります。

関連項目:

『Oracle Database PL/SQL言語リファレンス』

SQL DMLおよびDBMS_LOBを使用したLOBの更新例

次の例では、Print_media表を使用し、CLOBロケータがclob_selectedとして作成されます。この例では、t1からt3が次のように処理されます。

  • 最初のSELECT INTOの実行時(t1)に、ad_sourcetext内の値がロケータclob_selectedに対応付けられます。

  • 次の操作(t2)では、ロケータclob_selectedに影響を与えずに、SQL UPDATE文を介してad_sourcetext内の値が変更されます。ロケータは元のSELECT時点でのLOB値を参照します。つまり、ロケータはSQL UPDATE文によって実行された更新を認識しません。例では、後続のDBMS_LOB.READ()コールがこれを示しています。

  • 3つ目の操作(t3)では、LOB値が再選択されロケータclob_selectedに挿入されます。これによって、ロケータは最新のスナップショット環境に更新され、先のSQL UPDATE文によって行われた変更を認識できるようになります。このため、次のDBMS_LOB.READ()では、LOB値が空である(データがない)ためエラーが戻されます。

INSERT INTO Print_media VALUES (3247, 20010, EMPTY_BLOB(), 
    'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL);

COMMIT;

DECLARE
  num_var           INTEGER;
  clob_selected     CLOB;
  read_amount       INTEGER;
  read_offset       INTEGER;
  buffer            VARCHAR2(20);

BEGIN

  -- At time t1:
  SELECT ad_sourcetext INTO clob_selected
  FROM Print_media
  WHERE ad_id = 20010;

  read_amount := 10;
  read_offset := 1;
  dbms_lob.read(clob_selected, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_selected value: ' || buffer);
  -- Produces the output 'abcd'

  -- At time t2:
  UPDATE Print_media SET ad_sourcetext = empty_clob()
      WHERE ad_id = 20010;
  -- although the most current LOB value is now empty,
  -- clob_selected still sees the LOB value as of the point
  -- in time of the SELECT

  read_amount := 10;
  dbms_lob.read(clob_selected, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_selected value: ' || buffer);
  -- Produces the output 'abcd'

  -- At time t3:
  SELECT ad_sourcetext INTO clob_selected FROM Print_media WHERE
       ad_id = 20010;
  -- the SELECT allows clob_selected to see the most current
  -- LOB value

  read_amount := 10;
  dbms_lob.read(clob_selected, read_amount, read_offset, buffer);
  -- ERROR: ORA-01403: no data found
END;
/

同じLOB値を更新するために1つのロケータを使用する例

注意:

異なるロケータを使用して同じLOBを更新しないでください。特定のLOB値の更新にはロケータを1つのみ使用することで、多くの問題を回避できます。

次の例では、Print_media表を使用し、clob_updatedおよびclob_copiedの2つのCLOBをロケータとして作成します。

この例では、t1からt5が次のように処理されます。

  • 最初のSELECT INTOの実行時(t1)に、ad_sourcetext内の値がロケータclob_updatedに対応付けられます。

  • 次の操作(t2)では、clob_updatedの値がclob_copiedにコピーされます。この時点では、両方のロケータが同じ値を参照します。例では、一連のDBMS_LOB.READ()コールがこれを示しています。

  • t3で、プログラムはDBMS_LOB.WRITE()を使用してclob_updated内の値を変更します。DBMS_LOB.READ()が新しい値を示します。

  • ただし、clob_copiedを介した値のDBMS_LOB.READ()は(t4)、clob_updatedから割り当てられた時点(t2)のLOBの値を参照しています。

  • clob_updatedclob_copiedに割り当てられた時点(t5)で初めて、clob_copiedclob_updatedによる更新があったことを認識します。

INSERT INTO PRINT_MEDIA VALUES (2049, 20030, EMPTY_BLOB(), 
    'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL);

COMMIT;

DECLARE
  num_var          INTEGER;
  clob_updated     CLOB;
  clob_copied      CLOB;
  read_amount      INTEGER;
  read_offset      INTEGER;
  write_amount     INTEGER;
  write_offset     INTEGER;
  buffer           VARCHAR2(20);
BEGIN

-- At time t1:
  SELECT ad_sourcetext INTO clob_updated FROM PRINT_MEDIA
      WHERE ad_id = 20030
      FOR UPDATE;

  -- At time t2:
  clob_copied := clob_updated;
  -- after the assign, clob_copied and clob_updated see the same
  -- LOB value

  read_amount := 10;
  read_offset := 1;
  dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_updated value: ' || buffer);
  -- Produces the output 'abcd'

  read_amount := 10;
  dbms_lob.read(clob_copied, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_copied value: ' || buffer);
  -- Produces the output 'abcd'

  -- At time t3:
  write_amount := 3;
  write_offset := 5;
  buffer := 'efg';
  dbms_lob.write(clob_updated, write_amount, write_offset,
        buffer);

  read_amount := 10;
  dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_updated value: ' || buffer);
  -- Produces the output 'abcdefg'


  -- At time t4:
  read_amount := 10;
  dbms_lob.read(clob_copied, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_copied value: ' || buffer);
  -- Produces the output 'abcd'

  -- At time t5:
  clob_copied := clob_updated;

  read_amount := 10;
  dbms_lob.read(clob_copied, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_copied value: ' || buffer);
  -- Produces the output 'abcdefg'
END;
/

PL/SQL(DBMS_LOB)バインド変数を使用したLOBの更新の例

別の永続LOBを更新するためのソースとしてLOBロケータを使用する場合(SQLのINSERT文またはUPDATE文、DBMS_LOB.COPYルーチンなど)、ソースLOBロケータ内のスナップショット環境によって、ソースとして使用されるLOB値が決まります。

ソース・ロケータ(たとえばL1)が読取り一貫性のあるロケータの場合、L1SELECT実行時点のLOB値が使用されます。ソース・ロケータ(たとえばL2)が更新済ロケータの場合、更新時におけるL2のスナップショット環境に対応付けられたLOB値が使用されます。

次の例では、clob_selected、clob_updatedおよびclob_copiedの3つのCLOBをロケータとして作成します。

この例では、t1からt5が次のように処理されます。

  • 最初のSELECT INTOの実行時(t1)に、ad_sourcetext内の値がロケータclob_updatedに対応付けられます。

  • 次の操作(t2)では、clob_updatedの値がclob_copiedにコピーされます。この時点では、両方のロケータが同じ値を参照します。

  • この時点で(t3)、プログラムはDBMS_LOB.WRITE()を使用してclob_updated内の値を変更します。DBMS_LOB.READ()が新しい値を示します。

  • ただし、clob_copiedを介した値のDBMS_LOB.READ()は(t4)、clob_copiedclob_updatedによる変更を参照しないことを示します。

  • このため(t5の時点で)、clob_copiedINSERT文の値のソースとして使用する場合、clob_copiedと対応付けられた値が挿入されます(clob_updatedによる新規の変更は反映されません)。これは、その後の、挿入された直後の値に対するDBMS_LOB.READ()によってわかります。

INSERT INTO PRINT_MEDIA VALUES (2056, 20020, EMPTY_BLOB(), 
    'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL);

COMMIT;

DECLARE
  num_var           INTEGER;
  clob_selected     CLOB;
  clob_updated      CLOB;
  clob_copied       CLOB;
  read_amount       INTEGER;
  read_offset       INTEGER;
  write_amount      INTEGER;
  write_offset      INTEGER;
  buffer            VARCHAR2(20);
BEGIN

  -- At time t1:
  SELECT ad_sourcetext INTO clob_updated FROM PRINT_MEDIA
      WHERE ad_id = 20020
      FOR UPDATE;

  read_amount := 10;
  read_offset := 1;
  dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_updated value: ' || buffer);
  -- Produces the output 'abcd'

  -- At time t2:
  clob_copied := clob_updated;


  -- At time t3:
  write_amount := 3;
  write_offset := 5;
  buffer := 'efg';
  dbms_lob.write(clob_updated, write_amount, write_offset, buffer);

  read_amount := 10;
  dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_updated value: ' || buffer);
  -- Produces the output 'abcdefg'
  -- note that clob_copied does not see the write made before
  -- clob_updated


  -- At time t4:
  read_amount := 10;
  dbms_lob.read(clob_copied, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_copied value: ' || buffer);
  -- Produces the output 'abcd'

  -- At time t5:
  -- the insert uses clob_copied view of the LOB value which does
  -- not include clob_updated changes
  INSERT INTO PRINT_MEDIA VALUES (2056, 20022, EMPTY_BLOB(), 
    clob_copied, EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL)
    RETURNING ad_sourcetext INTO clob_selected;

  read_amount := 10;
  dbms_lob.read(clob_selected, read_amount, read_offset, buffer);
  dbms_output.put_line('clob_selected value: ' || buffer);
  -- Produces the output 'abcd'
END;
/

LOBロケータとトランザクション境界

LOBロケータは、トランザクションおよびトランザクションIDで使用できます。LOBロケータおよびBFILEロケータの基本的な説明は、LOBロケータとBFILEロケータを参照してください。

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

LOBロケータとトランザクション境界について

LOBロケータとトランザクションについては、次のことに注意してください。

  • ロケータがトランザクションIDを含む場合

    トランザクションを開始してからロケータを選択する場合: トランザクションを開始した後にロケータを選択すると、ロケータにトランザクションIDが含まれます。トランザクションを明示的に開始しなくてもトランザクションに暗黙的に入ることができます。たとえば、SELECT... FOR UPDATEによってトランザクションは暗黙的に開始されます。このような場合、ロケータにはトランザクションIDが含まれます。

  • ロケータがトランザクションIDを含まない場合

    • トランザクション外でロケータを選択する場合: 対照的に、トランザクションの外部でロケータを選択する場合、ロケータはトランザクションIDを含みません。

    • DML文の実行の前に選択した場合: トランザクションIDは、最初のDML文が実行されるまでは割り当てられません。このため、このようなDML文の前に選択されたロケータはIDを含みません。

ロケータを使用したLOBに対する読取りおよび書込み操作

ロケータがトランザクションIDを含んでいるかどうかにかかわらず、常にロケータを使用してLOBデータを読み取ることができます。

  • ロケータを使用して書込みができない場合:

    ロケータがトランザクションIDを含む場合、その特定のトランザクション外でLOBに書き込むことはできません。

  • ロケータを使用して書込みができる場合:

    ロケータがトランザクションIDを含まない場合、トランザクションを明示的または暗黙的に開始した後、LOBに書き込むことができます。

  • シリアライズ可能なトランザクションでロケータを使用して読取りまたは書込みができない場合:

    ロケータが古いトランザクションのトランザクションIDを含み、現在のトランザクションがシリアライズ可能である場合、このロケータを使用して読取りまたは書込みを行うことはできません。

  • シリアライズ不能なトランザクションでロケータを使用して読取りはできるが書込みができない場合:

    トランザクションがシリアライズ不能である場合、トランザクション外で読み取ることはできますが、書き込むことはできません。

「トランザクション境界外でのロケータの選択」「トランザクション境界内でのロケータの選択」「複数のトランザクションにまたがることはできないLOBロケータ」および「トランザクションにまたがらないロケータの例」の各例は、ロケータとシリアライズ可能でないトランザクション間の関係を示しています

トランザクション境界外でのロケータの選択

2つの使用例により、トランザクション外でロケータが選択された場合に、シリアライズ可能でないトランザクションでロケータを使用する方法について説明します。

使用例1:

  1. 現行トランザクションを持たないロケータを選択します。この時点ではロケータはトランザクションIDを含みません。

  2. トランザクションを開始します。

  3. ロケータを使用してLOBからデータを読み取ります。

  4. トランザクションをコミットまたはロールバックします。

  5. ロケータを使用してLOBからデータを読み取ります。

  6. トランザクションを開始します。ロケータはトランザクションIDを含みません。

  7. ロケータを使用してデータをLOBに書き込みます。この操作は、ロケータが書込みの前にトランザクションIDを含まないため有効です。このコールの後、ロケータはトランザクションIDを含むようになります。

使用例2:

  1. 現行トランザクションを持たないロケータを選択します。この時点ではロケータはトランザクションIDを含みません。
  2. トランザクションを開始します。ロケータはトランザクションIDを含みません。
  3. ロケータを使用してLOBからデータを読み取ります。ロケータはトランザクションIDを含みません。
  4. ロケータを使用してデータをLOBに書き込みます。この操作は、ロケータが書込みの前にトランザクションIDを含まないため有効です。このコールの後、ロケータはトランザクションIDを含むようになります。続けてLOBの読取りまたは書込みを行うことができます。
  5. トランザクションをコミットまたはロールバックします。ロケータは引き続きトランザクションIDを含みます。
  6. ロケータを使用してLOBからデータを読み取ります。この操作は有効です。
  7. トランザクションを開始します。ロケータは前のトランザクションのIDを含んでいます。
  8. ロケータを使用してデータをLOBに書き込みます。ロケータが現行トランザクションに一致するトランザクションIDを含まないため、この書込み操作は失敗します。

トランザクション境界内でのロケータの選択

2つの使用例により、トランザクション内でロケータが選択された場合に、シリアライズ可能でないトランザクションでロケータを使用する方法について説明します。

使用例1:

  1. トランザクションの中でロケータを選択します。この時点では、ロケータはトランザクションIDを含んでいます。

  2. トランザクションを開始します。ロケータは前のトランザクションのIDを含んでいます。

  3. ロケータを使用してLOBからデータを読み取ります。ロケータの中のトランザクションIDは現在のトランザクションに一致していませんが、この操作は有効です。

    関連項目:

    ロケータを使用したLOBデータの読取りの詳細は、読取り一貫性のあるロケータを参照してください。

  4. ロケータを使用してデータをLOBに書き込みます。ロケータの中のトランザクションIDが現在のトランザクションに一致していないため、この操作は失敗します。

使用例2:

  1. トランザクションを開始します。
  2. ロケータを選択します。ロケータがトランザクションの中で選択されたため、トランザクションIDが含まれています。
  3. ロケータを使用してLOBの読取りまたは書込みを行います。これらの操作は有効です。
  4. トランザクションをコミットまたはロールバックします。ロケータは引き続きトランザクションIDを含みます。
  5. ロケータを使用してLOBからデータを読み取ります。ロケータの中にトランザクションIDがあり、そのトランザクションはすでにコミットまたはロールバックされていますが、この操作は有効です。
  6. ロケータを使用してデータをLOBに書き込みます。ロケータの中のトランザクションIDはすでにコミットまたはロールバックされたトランザクション用であるため、この操作は失敗します。

複数のトランザクションにまたがることのできないLOBロケータ

DBMS_LOB、OCI、SQLのINSERT文またはUPDATE文を使用し、LOBロケータによって永続LOBの値を更新すると、読取り一貫性のあるロケータが更新済ロケータに変更されます。

INSERT文やUPDATE文によって、トランザクションが自動的に開始され、行がロックされます。一度これが発生すると、ロケータを現行トランザクション以外で使用して、LOB値を変更できなくなる可能性があります。データの書込みに使用するLOBロケータを複数のトランザクションにまたがって使用することはできません。ただし、シリアライズ可能なトランザクション内でない場合は、ロケータを使用してLOB値を読み取ることができます。

関連項目:

LOBとトランザクション境界の関係については、LOBロケータとトランザクション境界を参照してください。

「トランザクションにまたがらないロケータの例」では、CLOBロケータとしてclob_updatedが作成されています

  • 最初のSELECT INTOの実行時(t1)に、ad_sourcetext内の値がロケータclob_updatedに対応付けられます。

  • 次の操作で(t2)、DBMS_LOB.WRITEファンクションを使用してclob_updated内の値を変更します。DBMS_LOB.READが新しい値を示します。

  • COMMIT文(t3)によって現行のトランザクションが終了します。

  • トランザクションを終了すると(t4)、clob_updatedロケータは別のトランザクション(コミット済)を参照することになるため、その後のDBMS_LOB.WRITE操作は失敗します。これは、戻されるエラーによって通知されます。このLOBロケータをさらにDBMS_LOB(およびOCI)の変更操作で使用するには、再選択する必要があります。

トランザクションにまたがらないロケータの例

この例では、LOBの例の表: PMスキーマのprint_media表で説明されているprint_media表を使用します

INSERT INTO PRINT_MEDIA VALUES (2056, 20010, EMPTY_BLOB(), 
    'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL);

COMMIT;

DECLARE
  num_var          INTEGER;
  clob_updated     CLOB;
  read_amount      INTEGER;
  read_offset      INTEGER;
  write_amount     INTEGER;
  write_offset     INTEGER;
  buffer           VARCHAR2(20);

BEGIN
          -- At time t1:
     SELECT      ad_sourcetext
     INTO        clob_updated
     FROM        PRINT_MEDIA
     WHERE       ad_id = 20010
     FOR UPDATE;
     read_amount := 10;
     read_offset := 1;
     dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
     dbms_output.put_line('clob_updated value: ' || buffer);
     -- This produces the output 'abcd'

     -- At time t2:
     write_amount := 3;
     write_offset := 5;
     buffer := 'efg';
     dbms_lob.write(clob_updated, write_amount, write_offset, buffer);
     read_amount := 10;
     dbms_lob.read(clob_updated, read_amount, read_offset, buffer);
     dbms_output.put_line('clob_updated value: ' || buffer);
     -- This produces the output 'abcdefg'

    -- At time t3:
    COMMIT;

     -- At time t4:
    dbms_lob.write(clob_updated , write_amount, write_offset, buffer);
    -- ERROR: ORA-22990: LOB locators cannot span transactions
END;
/

オブジェクト・キャッシュ内のLOB

LOBロケータ属性を使用してオブジェクト・キャッシュ内でオブジェクトを別のオブジェクトにコピーする場合、LOBロケータのみがコピーされます。

つまり、これら2つの異なるオブジェクト内のLOB属性には、唯一かつ同一のLOBを参照する厳密に同一のロケータが含まれます。ターゲット・オブジェクトがフラッシュされた場合のみ、異なるLOB値の物理コピーが作成されます。これは、ソースLOB値とは異なります。

関連項目:

ロケータの1つを使用して書込みが実行された場合、各オブジェクトでどのバージョンのLOB値が参照されるかについては、LOBの更新および読取り一貫性の例を参照してください。

したがって、コピーのターゲットであったLOBを変更する場合、ターゲット・オブジェクトをフラッシュし、ターゲット・オブジェクトをリフレッシュしてから、ロケータ属性を介してLOBに書き込む必要があります。

内部LOB属性および外部LOB属性には、次のようなオブジェクト・キャッシュの問題があります。

  • 永続LOB属性: オブジェクト・キャッシュ内にオブジェクトを作成すると、LOB属性は空に設定されます。

    オブジェクト・キャッシュ内に、永続LOB属性を持つオブジェクトを作成すると、そのLOB属性は暗黙的に空に設定されます。この空のLOBロケータを使用して、データをLOBに書き込むことはできません。最初にオブジェクトをフラッシュし、その後、表に行を挿入し、空のLOB(長さ0(ゼロ)のLOB)を作成する必要があります。オブジェクトがオブジェクト・キャッシュ内でリフレッシュされ(OCI_PIN_LATESTを使用)、実際のLOBロケータがLOB属性に読み取られると、OCI LOB APIをコールしてLOBにデータを書き込むことができます。

  • 外部LOB(BFILE)属性: オブジェクト・キャッシュ内にオブジェクトを作成すると、BFILE属性はNULLに設定されます。

    外部LOB (BFILE)属性を使用してオブジェクトを作成する場合、BFILENULLに設定されます。これは、BFILEから読み取られる前に有効なディレクトリ・オブジェクト名およびファイル名を使用して更新する必要があります。

サイズがTBのLOBのサポート

TサイズがTBのLOBは、データベース・ブロック・サイズに応じて最大サイズが8TBから128TBになるLOBです。

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

サイズがTBのLOBのサポートについて

サイズがTBのLOBは、次のAPIによってサポートされます。

  • JDBC(Java Database Connectivity)を使用したJava

  • DBMS_LOBパッケージを使用したPL/SQL

  • OCI(Oracle Call Interface)を使用したC

4GBを超えるサイズのLOBインスタンス(サイズがTBのLOB)は、次のプログラム環境では作成も使用もできません。

  • Pro*COBOLプリコンパイラを使用したCOBOL

  • Pro*C/C++プリコンパイラを使用したCまたはC++

注意:

Oracle Databaseでは、どのプログラム環境であっても、2^64-1バイト(OCIではUB8MAXVAL)を超えるBFILEはサポートされません。BFILEには、オペレーティング・システムに基づくその他のファイル・サイズ制限も適用されます。

サイズがTBのLOBの最大記憶域の制限

サポートされている環境では、データベース構成の最大記憶域サイズの上限までLOBを作成して操作できます。

Oracle Databaseを使用すると、データベースのブロック・サイズとは異なるブロック・サイズの表領域を作成できます。LOBの最大サイズは、表領域ブロックのサイズによって決まります。CHUNKは、LOB記憶域のパラメータで、この値は、LOBが格納されている表領域のブロック・サイズによって制御されます。

注意:

CHUNKパラメータは、SecureFilesには適用されません。これは、BasicFiles LOBに対してのみ使用されます。

LOB列を作成する場合、CHUNKの値を指定できます。これは、LOBの操作用に割り当てるバイト数です。この値は、表領域ブロック・サイズの倍数である必要があります。そうでない場合、Oracle Databaseによって次の倍数に切り上げられます。(表ブロック・サイズがデータベース・ブロック・サイズと同じ場合、CHUNKは、データベース・ブロック・サイズの倍数でもあります。)

使用構成で許可される記憶域の最大制限は、表領域ブロック・サイズによって決まり、(4GB - 1)にDBMS_LOB.GETCHUNKSIZEまたはOCILobGetChunkSize()から取得した値を乗算した値として計算されます。この値(BLOBのバイト数またはCLOBの文字数)は、内部記憶域のオーバーヘッドにより、実際にはCHUNKパラメータのサイズより小さくなります。表領域ブロック・サイズで許可される現在の範囲は2Kから32Kであるため、記憶域の制限範囲は8TBから128TBです。

たとえば、データベース・ブロック・サイズが32KBの場合に、8KBの非標準のブロック・サイズの表領域を作成するとします。また、LOB列のある表を作成し、CHUNKサイズを16KB(表領域ブロック・サイズ8KBの倍数)に指定するとします。その場合、この列のLOBの最大サイズは、(4GB-1)×16KBです。

関連項目:

  • データベースのインストールにあわせた初期化パラメータ設定の詳細は、『Oracle Database管理者ガイド』を参照してください

  • CHUNK

この記憶域制限は、サイズがTBのLOBをサポートしている環境では、すべてのLOB型に適用されます。ただし、CLOB型とNCLOB型のサイズは文字単位で、BLOB型のサイズはバイト単位であることに注意してください。

JDBCでのサイズがTBのLOBの使用

Oracle JDBCクラスに含まれるLOB APIを使用して、サイズがTBのLOBにアクセスできます。

DBMS_LOBパッケージでのサイズがTBのLOBの使用

DBMS_LOB PL/SQLパッケージに含まれるどのAPIでも、サイズがTBのLOBにアクセスできます。

LOBの読取りおよび書込みに使用される値を取得するには、DBMS_LOB.GETCHUNKSIZEを使用します。チャンクに格納されているバイト数は、実際には、内部記憶域のオーバーヘッドのため、CHUNKパラメータのサイズよりも小さくなります。DBMS_LOB.GET_STORAGE_LIMITファンクションは、データベース構成の記憶域制限を戻します。これは、LOBの最大許容記憶域の値です。BLOBのサイズはバイト単位で、CLOBおよびNCLOBのサイズは文字単位であることに注意してください。

注意:

データベースのインストールにあわせた初期化パラメータ設定の詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

OCIでのサイズがTBのLOBの使用

Oracle Call Interface APIには、すべてのサイズのLOBを操作するための関数セットが用意されています。

OCILobGetChunkSize()は、BLOBの場合はバイト単位で、CLOBの場合は文字単位で値を戻します。可変幅キャラクタ・セットの場合、値は幅に応じたUnicode文字数となります。チャンクに格納されているバイト数は、実際には、内部記憶域のオーバーヘッドのため、CHUNKパラメータのサイズよりも小さくなります。OCILobGetStorageLimit()ファンクションは、現在のデータベース・インストレーションにおける内部LOBの最大許容サイズをバイト単位で戻します。LOB全体が読み取られるストリーム・モードが使用される場合は、チャンク・サイズを取得する必要はありません。

注意:

LOBをサポートするOCI関数の詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』の「LOBおよびBFILEの操作」、サイズが4GBを超えるLOBの使用に関する項を参照してください。

サイズがGBのLOB作成時のガイドライン

サポートされている環境でサイズがGBのLOBを作成するには、次のガイドラインに従って、LOB記憶域用の表領域で使用可能な領域をすべて使用します。

  • 単一のデータ・ファイル・サイズの制限:

    オペレーティング・システムごとに単一のデータ・ファイルのサイズに関する制限があります。たとえば、Solaris 2.5では、オペレーティング・システム・ファイルは2GB以下に制限されています。このため、Oracle Databaseが実行されているオペレーティング・システムのファイルの最大許容ファイル・サイズよりもLOBが大きくなった場合、表領域にデータファイルを追加する必要があります。

  • PCT INCREASEパラメータをゼロに設定:

    LOB記憶域句のPCTINCREASEパラメータは、新規エクステント・サイズの増加率を指定します。LOBが表領域内にピース単位で格納されていくとき、そのプロセスで多数の新しいエクステントが作成されます。エクステント・サイズが毎回デフォルト値である50%ずつ増加し続けると、エクステントが管理不可能になり、最終的に表領域内の領域が無駄になります。そのため、PCTINCREASEパラメータを0(ゼロ)または小さい値に設定する必要があります。

  • MAXEXTENTSを適切な値またはUNLIMITEDに設定:

    MAXEXTENTSパラメータは、LOB列で許可されるエクステント数を制限します。LOBのサイズが大きくなると、多数のエクステントが段階的に作成されます。そのため、このパラメータは、その列のすべてのLOBを保持できる十分大きい値に設定する必要があります。または、UNLIMITEDに設定できます。

  • 大きいエクステント・サイズを使用:

    新しいエクステントが作成されるたびに、そのエクステントに対してヘッダーとその他のメタデータのUNDO情報が生成されます。エクステントの数が多い場合、ロールバック・セグメントが容量オーバーになることがあります。これを避けるには、大きなエクステント・サイズ(100MBなど)を選択してエクステント作成の頻度を削減するか、またはより頻繁にトランザクションをコミットして、ロールバック・セグメントの領域を再利用します。

サイズがGBのLOBを格納する表領域および表の作成

次の例に、サイズがGBのLOBを格納する表領域と表の作成方法を示します。

CREATE TABLESPACE lobtbs1 DATAFILE '/your/own/data/directory/lobtbs_1.dat'
SIZE 2000M REUSE ONLINE NOLOGGING DEFAULT STORAGE (MAXEXTENTS UNLIMITED);
ALTER TABLESPACE lobtbs1 ADD DATAFILE
'/your/own/data/directory/lobtbs_2.dat' SIZE 2000M REUSE;

CREATE TABLE print_media_backup
  (product_id NUMBER(6), 
   ad_id NUMBER(6), 
   ad_composite BLOB, 
   ad_sourcetext CLOB, 
   ad_finaltext CLOB, 
   ad_fltextn NCLOB, 
   ad_textdocs_ntab textdoc_tab, 
   ad_photo BLOB, 
   ad_graphic BLOB, 
   ad_header adheader_typ)
   NESTED TABLE ad_textdocs_ntab STORE AS textdocs_nestedtab5 
   LOB(ad_sourcetext) STORE AS (TABLESPACE lobtbs1 CHUNK 32768 PCTVERSION 0 
                                NOCACHE NOLOGGING
                                STORAGE(INITIAL 100M NEXT 100M MAXEXTENTS 
                                UNLIMITED PCTINCREASE 0));

この例では、次のことに注意してください。

  • この例では、CREATE TABLESPACE文にSTORAGE句が指定されています。

  • STORAGE句は、CREATE TABLE文に指定することも可能です。

  • CREATE TEMPORARY TABLESPACE文にはSTORAGE句を指定できません。

  • サイズがGBのLOBの場合は、PCTINCREASEパラメータを0(ゼロ)に設定することをお薦めします。サイズが小さいか中程度のLOBの場合は、エクステント割当て数を減らすために、PCTINCREASEのデフォルト値である50に設定することをお薦めします。