14 高度な設計時の考慮事項
より高度なアプリケーション開発時の問題について設計上の考慮事項があります。
内容は次のとおりです。
14.1 LOBバッファリング・サブシステム
データベースは、高度なOCIベースのアプリケーションに対してLOBバッファリング・サブシステム(LBS)を提供する必要があります。このようなアプリケーションには、データ・カートリッジ、Webサーバー、および1つ以上のLOBの内容をクライアント・アドレス領域にバッファリングする必要がある他のクライアント・ベースのアプリケーションなどがあります。
バッファリング・サブシステムに対するクライアント側の最大使用時のメモリー要件は512
KBです。これはまた、バッファリングされたアクセス用として有効であるLOBの単一の読取りまたは書込み操作に対して指定できる最大量でもあります。
内容は次のとおりです。
14.1.1 LOBバッファリングのメリット
14.1.2 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
が使用されます。
14.1.3 LOBバッファリング・サブシステムの使用方法
14.1.3.1 LOBバッファの物理構造
各ユーザー・セッションの構造は、次のとおりです。
-
各ユーザー・セッションには、16ページの固定ページ・プールがあります。このページは、そのセッションからバッファリング・モードでアクセスされるすべてのLOBによって共有されます。
-
各ページ・サイズは、最大32KB (文字単位ではない)の固定サイズです。ページ・サイズ = n x
CHUNK
= 約32KBとなります。
LOBバッファは、これらのページの1つ以上で構成されています(セッションごとに最大16)。バッファリングされた特定の読取りまたは書込み操作に指定する必要がある最大量は512KBですが、状況が異なる場合、読取り/書込み可能な最大量は小さくなる可能性があります。
14.1.3.2 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つの原因が考えられます。
-
バッファ・プールにあるすべてのバッファが、以前の操作で使用された。
この場合、LOBの更新に使用されているロケータを介してLOBをフラッシュします。
-
以前バッファリングされた更新操作がないのにLOBをフラッシュしようとしている。
この場合、バッファをフラッシュする前に、バッファリングが可能なロケータを介してLOBに書き込みます。
14.1.4 LOBバッファのフラッシュについて
フラッシュとは、一連のプロセスを指します。ロケータを介してバッファ内のLOBにデータを書き込むと、そのロケータは、更新済ロケータになります。更新済ロケータを介してバッファのLOBデータを更新すると、フラッシュ・コールは、次のことを行います。
-
LOBのバッファの使用済ページをサーバー・ベースのLOBに書き込み、それによってLOB値を更新します。
-
更新済ロケータをリセットして、読取り一貫性のあるロケータにします。
-
フラッシュ済バッファを解放するか、バッファのページの状態を使用済から変更されていない状態に戻します。
フラッシュ後、ロケータは、読取り一貫性のあるロケータとなり、別のロケータに割り当てることができます(L2 := L1
)。
たとえば、L1
およびL2
の2つのロケータがあるとします。これらは両方とも読取り一貫性のあるロケータであり、サーバー内のLOBデータの状態との一貫性があるとします。バッファに書込みをしてLOBを更新すると、L1
は更新済ロケータになります。L1
およびL2
は、その時点で、別のバージョンのLOB値を参照しています。サーバーのLOBを更新する場合は、L1
を使用して、L2
で得た読取り一貫性のある状態を維持する必要があります。フラッシュ操作によって、フラッシュに使用したロケータに新しいスナップショット環境が書き込まれます。重要な点は、LOBバッファをフラッシュするとき、更新済ロケータ(L1
)を使用する必要があることです。読取り一貫性のあるロケータをフラッシュしようとすると、エラーが発生します。
14.1.5 LOBバッファのフラッシュ
LOBバッファのフラッシュに使用する方法によって、バッファ内のキャッシュがクリアされるかどうかが決定されます。また、パフォーマンスに次のように影響します。
-
デフォルト・モードの場合、データは、フラッシュ操作が行われたときに変更されたページ内に維持されます。その際、同じバイト範囲に対して読取りまたは書込みを行う場合、サーバーに対するラウンドトリップは必要ありません。このコンテキストでバッファをフラッシュしてもバッファ内のデータはクリアされません。また、フラッシュされたバッファによって占有されているメモリーがクライアントのアドレス領域に戻されることもありません。
ノート:
変更されていないページは、必要に応じてページ・アウトされます。
-
2つ目のケースでは、
OCILobFlushBuffer()
のフラグ・パラメータをOCI_LOB_BUFFER_FREE
に設定して、バッファ・ページを解放すると、メモリーは、クライアントのアドレス領域に戻されます。この方法を使用してバッファをフラッシュすると、サーバー上でLOB値が更新され、読取り一貫性のあるロケータが戻され、バッファ・ページが解放されます。
14.1.6 更新済LOBのフラッシュについて
14.1.7 更新済LOBのフラッシュ
外部コールアウトがPL/SQLブロックからコールされ、ロケータがパラメータとして渡される状況である場合、バッファリングを使用可能にするコールを含めてすべてのバッファリング操作を次の順序でコールアウトの内部で行う必要があります。
- 外部コールアウトを呼び出します。
- ロケータをバッファリング可能にします。
- ロケータを使用して読取り/書込みを行います。
- LOBをフラッシュします。
- バッファリングのロケータを使用禁止にします。
- PL/SQLのコール側のファンクション、プロシージャまたはメソッドに戻ります。
14.1.8 バッファリング対応のロケータの使用
バッファリング対応のロケータを使用できる場合と使用できない場合があることに注意してください。
-
バッファリング対応ロケータを使用できる場合
-
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バッファリングを使用可能にしたロケータは、トランザクションにまたがることはできません。
-
14.1.9 再選択を避けるためのロケータ状態の保存
さらにLOBバッファに書き込む前に、LOBの現在の状態を保存できます。LOBバッファリングの使用中に更新を実行すると、既存のバッファへの書込みで、サーバーへのラウンドトリップは不要なため、ロケータのスナップショット環境はリフレッシュされません。
これは、LOBバッファリングを使用しないで直接LOBを更新する場合にはあてはまりません。その場合、更新するたびにサーバーへのラウンドトリップが必要となるため、ロケータのスナップショットはリフレッシュされます。
LOBバッファを使用して書き込まれたLOBの状態を保存するには、次のステップに従います。
- LOBをフラッシュして、LOBおよびロケータ(L1)のスナップショット環境を更新します。この時点では、ロケータ(L1)の状態とLOBは同じです。
- フラッシュおよび更新に使用されたロケータ(L1)を別のロケータ(L2)に割り当てます。この時点では、2つのロケータの状態(L1およびL2)とLOBはすべて同じです。
14.1.10 OCI: LOBバッファリングの例
次に示すOCIの擬似コード例は、Oracle Databaseサンプル・スキーマに含まれるPMスキーマに基づいています。
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"); }
14.2 OPENおよびCLOSEインタフェースを使用した永続LOBのオープン
OPEN
およびCLOSE
インタフェースを使用すると、永続LOBインスタンスを明示的にオープンできます。
OPEN
インタフェースを使用してLOBインスタンスをオープンすると、そのインスタンスはCLOSE
インタフェースを使用してLOBを明示的にクローズするまでオープンされたままになります。ISOPEN
インタフェースを使用すると、永続LOBがオープンしているかどうかを確認できます。
LOBのオープン状態はLOBロケータではなくLOBインスタンスに関連付けられていることに注意してください。ロケータには、それが指すLOBインスタンスがオープンしているかどうかを示す情報は格納されません。
関連項目:
内容は次のとおりです。
14.2.1 LOBの明示的なオープンによる索引のパフォーマンス上のメリット
LOBインスタンスを明示的にオープンすると、索引付き列の永続LOBのパフォーマンスを改善できます。
LOBインスタンスを明示的にオープンしない場合、LOBに対する変更が行われるたびにLOBインスタンスが暗黙的にオープンおよびクローズされます。ドメイン索引に対するトリガーは、LOBがクローズされるたびに起動されます。この場合、LOBインスタンスに対する変更が行われるとすぐにLOB上のドメイン索引が更新されます。ドメイン索引は常に有効であり、いつでも使用できます。
LOBインスタンスを明示的にオープンすると、LOBを明示的にクローズするまで索引トリガーは起動されません。この方法を使用すると、LOBを明示的にクローズするまで不要な索引付けイベントが発生しなくなり、索引列のパフォーマンスを改善できます。LOB列のすべての索引は、そのLOBを明示的にクローズするまで無効であることに注意してください。
14.2.2 明示的にオープンしたLOBインスタンスのクローズ
LOBインスタンスを明示的にオープンした場合は、トランザクションをコミットする前にLOBをクローズする必要があります。
オープン状態にあるLOBインスタンスに対するトランザクションをコミットすると、エラーが発生します。このエラーが発生すると、LOBインスタンスが暗黙的にクローズされてLOBインスタンスに対する変更が保存され、トランザクションがコミットされますが、LOB列の索引は更新されません。この場合は、LOB列に対する索引を再作成する必要があります。
その後トランザクションをロールバックすると、LOBインスタンスは前の状態にロールバックされますが、LOBインスタンスは明示的にオープンされません。
次の状況で明示的にオープンしたLOBインスタンスは、すべてクローズする必要があります。
-
トランザクションを起動するDML文の間(
SELECT
...FOR
UPDATE
およびCOMMIT
を含む) -
自律型トランザクション・ブロック内
-
セッションの終了前(関係するトランザクションがないとき)
LOBインスタンスを明示的にクローズしない場合、セッションの終了時に暗黙的にクローズされ、索引トリガーは起動されません。
明示的にオープンしたLOBがオープン状態かクローズ状態かを追跡してください。次の場合にはエラーが発生します。
-
すでに明示的にオープンしているLOBインスタンスを明示的にオープンする場合。
-
すでに明示的にクローズしているLOBインスタンスを明示的にクローズする場合。
このエラーは、LOBインスタンスへのアクセスに同じロケータを使用しているかどうかに関係なく発生します。
14.3 読取り一貫性のあるロケータ
Oracle DatabaseのLOBに対する読取り一貫性メカニズムは、その他すべてのスカラー量に対するデータベースの読取りおよび更新のための読取り一貫性メカニズムと同じです。
LOBロケータの場合、読取り一貫性にはいくつかの特別な用途があるため、正しく理解しておく必要があります。次の各項では、読取り一貫性について説明し、互いに関連付けながら参照する必要がある例を示します。
関連項目:
-
読取り一貫性の一般的な情報は、Oracle Database概要を参照してください
内容は次のとおりです。
14.3.1 読取り一貫性のあるロケータになる、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
によって)、L2
はL1
と同様に読取り一貫性のあるロケータとなり、読み取られるデータはL1
に対するSELECT
実行時点のデータとなります。
複数のロケータが存在していることを利用して、変化するLOB値のそれぞれの値にアクセスできます。ただし、その場合は、どのロケータでどの値にアクセスしているかを常に理解しておく必要があります。
14.3.2 LOBの更新および読取り一貫性の例
読取り一貫性のあるロケータでは、SELECT
実行時期に関係なく同じLOB値が提供されます。
次に、読取り一貫性と更新の関係を簡単な例で示します。「LOBの例の表: PMスキーマのprint_media表」で説明されているprint_media
表とPL/SQLを使用して、clob_selected
、clob_update
およびclob_copied
の3つのCLOB
インスタンスをロケータとして作成します。
次のコード例では、t1
からt6
が次のように処理されます。
-
最初の
SELECT
INTO
の実行時(t1
)に、ad_sourcetext
内の値がロケータclob_selected
に対応付けられます。 -
次の操作(
t2
)で、ad_sourcetext
内の値がロケータclob_updated
に対応付けられます。t1
とt2
の間では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
と同じ値が引き続き参照されることを示します。
例14-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; /
14.3.3 更新済ロケータを介した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値を参照するロケータを再選択することになります。
14.3.4 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値を参照します。つまり、ロケータはSQLUPDATE
文によって実行された更新を認識しません。例では、後続のDBMS_LOB
.READ()
コールがこれを示しています。 -
3つ目の操作(
t3
)では、LOB値が再選択されロケータclob_selected
に挿入されます。これによって、ロケータは最新のスナップショット環境に更新され、先のSQLUPDATE
文によって行われた変更を認識できるようになります。このため、次の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; /
14.3.5 同じ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_updated
がclob_copied
に割り当てられた時点(t5
)で初めて、clob_copied
はclob_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; /
14.3.6 PL/SQL(DBMS_LOB)バインド変数を使用したLOBの更新の例
別の永続LOBを更新するためのソースとしてLOBロケータを使用する場合(SQLのINSERT
文またはUPDATE
文、DBMS_LOB
.COPY
ルーチンなど)、ソースLOBロケータ内のスナップショット環境によって、ソースとして使用されるLOB値が決まります。
ソース・ロケータ(たとえばL1
)が読取り一貫性のあるロケータの場合、L1
のSELECT
実行時点の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_copied
がclob_updated
による変更を参照しないことを示します。 -
このため(
t5
の時点で)、clob_copied
をINSERT
文の値のソースとして使用する場合、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; /
14.3.7 ロケータを使用したLOBの削除の例
この項では、PL/SQLバインド変数を使用したLOBの削除について学習します。
次の例では、特定の時点で選択されたロケータを介したLOBコンテンツを、同じトランザクション内でそのLOBが削除されている場合でも使用可能であることを示します。
次の例では、print_media
表を使用し、clob_selected
およびclob_copied
という2つのCLOBをロケータとして作成します。
この例では、t1
からt3
が次のように処理されます。
- 最初の
SELECT
INTO
の実行時(t1
)に、ad_id
の値が20020であるad_sourcetext
の値が、ロケータclob_selected
に対応付けられます。ad_id
の値が20021であるad_sourcetext
の値は、ロケータclob_copied
に関連付けられます。 - 2つ目の操作(
t2
)では、ad_id
の値が20020である行が削除されます。ただし、clob_selected
を介した値のDBMS_LOB.READ()
は(t1
)、これが読取り一貫性のあるロケータであり、SELECTの実行時と同じ値が引き続き参照されることを示します。 - 3つ目の操作(
t3
)では、clob_selected
を介して読み取られたLOBデータがLOBclob_copied
にコピーされます。clob_selected
およびclob_copied
を介した値のDBMS_LOB.READ()
はこれと同じになり、clob_selected
のSELECT
の実行時と同じ値を参照します。
INSERT INTO PRINT_MEDIA VALUES (2056, 20020, EMPTY_BLOB(), 'abcd', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL); INSERT INTO PRINT_MEDIA VALUES (2057, 20021, EMPTY_BLOB(), 'cdef', EMPTY_CLOB(), EMPTY_CLOB(), NULL, NULL, NULL, NULL); DECLARE clob_selected CLOB; clob_copied CLOB; buffer VARCHAR2(20); read_amount INTEGER := 20; read_offset INTEGER := 1; BEGIN -- At time t1: SELECT ad_sourcetext INTO clob_selected FROM PRINT_MEDIA WHERE ad_id = 20020 FOR UPDATE; SELECT ad_sourcetext INTO clob_copied FROM PRINT_MEDIA WHERE ad_id = 20021 FOR UPDATE; dbms_lob.read(clob_selected, read_amount, read_offset,buffer); dbms_output.put_line(buffer); -- Produces the output 'abcd' dbms_lob.read(clob_copied, read_amount, read_offset,buffer); dbms_output.put_line(buffer); -- Produces the output 'cdef' -- At time t2: Delete the CLOB associated with clob_selected DELETE FROM PRINT_MEDIA WHERE ad_id = 20020; dbms_lob.read(clob_selected, read_amount, read_offset,buffer); dbms_output.put_line(buffer); -- Produces the output 'abcd' -- At time t3: -- Copy using clob_selected dbms_lob.copy(clob_copied, clob_selected, 4000, 1, 1); dbms_lob.read(clob_copied, read_amount, read_offset,buffer); dbms_output.put_line(buffer); -- Produces the output 'abcd' END; /
14.4 LOBロケータとトランザクション境界
14.4.1 LOBロケータとトランザクション境界について
LOBロケータとトランザクションについては、次のことに注意してください。
-
トランザクションを開始してからロケータを選択する場合: トランザクションを開始した後にロケータを選択すると、ロケータにトランザクションIDが含まれます。トランザクションを明示的に開始しなくてもトランザクションに暗黙的に入ることができます。たとえば、
SELECT
...FOR
UPDATE
によってトランザクションは暗黙的に開始されます。このような場合、ロケータにはトランザクションIDが含まれます。 -
ロケータがトランザクションIDを含まない場合
-
トランザクション外でロケータを選択する場合: 対照的に、トランザクションの外部でロケータを選択する場合、ロケータはトランザクションIDを含みません。
-
DML文の実行の前に選択した場合: トランザクションIDは、最初のDML文が実行されるまでは割り当てられません。このため、このようなDML文の前に選択されたロケータはIDを含みません。
-
14.4.2 ロケータを使用したLOBに対する読取りおよび書込み操作
ロケータがトランザクションIDを含んでいるかどうかにかかわらず、常にロケータを使用してLOBデータを読み取ることができます。
-
ロケータを使用して書込みができない場合:
ロケータがトランザクションIDを含む場合、その特定のトランザクション外でLOBに書き込むことはできません。
-
ロケータを使用して書込みができる場合:
ロケータがトランザクションIDを含まない場合、トランザクションを明示的または暗黙的に開始した後、LOBに書き込むことができます。
-
シリアライズ可能なトランザクションでロケータを使用して読取りまたは書込みができない場合:
ロケータが古いトランザクションのトランザクションIDを含み、現在のトランザクションがシリアライズ可能である場合、このロケータを使用して読取りまたは書込みを行うことはできません。
-
シリアライズ不能なトランザクションでロケータを使用して読取りはできるが書込みができない場合:
トランザクションがシリアライズ不能である場合、トランザクション外で読み取ることはできますが、書き込むことはできません。
「トランザクション境界外でのロケータの選択」、「トランザクション境界内でのロケータの選択」、「複数のトランザクションにまたがることはできないLOBロケータ」および「トランザクションにまたがらないロケータの例」の各例は、ロケータとシリアライズ可能でないトランザクション間の関係を示しています
14.4.3 トランザクション境界外でのロケータの選択
2つの使用例により、トランザクション外でロケータが選択された場合に、シリアライズ可能でないトランザクションでロケータを使用する方法について説明します。
シナリオ1:
-
現行トランザクションを持たないロケータを選択します。この時点ではロケータはトランザクションIDを含みません。
-
トランザクションを開始します。
-
ロケータを使用してLOBからデータを読み取ります。
-
トランザクションをコミットまたはロールバックします。
-
ロケータを使用してLOBからデータを読み取ります。
-
トランザクションを開始します。ロケータはトランザクションIDを含みません。
-
ロケータを使用してデータをLOBに書き込みます。この操作は、ロケータが書込みの前にトランザクションIDを含まないため有効です。このコールの後、ロケータはトランザクションIDを含むようになります。
シナリオ2:
- 現行トランザクションを持たないロケータを選択します。この時点ではロケータはトランザクションIDを含みません。
- トランザクションを開始します。ロケータはトランザクションIDを含みません。
- ロケータを使用してLOBからデータを読み取ります。ロケータはトランザクションIDを含みません。
- ロケータを使用してデータをLOBに書き込みます。この操作は、ロケータが書込みの前にトランザクションIDを含まないため有効です。このコールの後、ロケータはトランザクションIDを含むようになります。続けてLOBの読取りまたは書込みを行うことができます。
- トランザクションをコミットまたはロールバックします。ロケータは引き続きトランザクションIDを含みます。
- ロケータを使用してLOBからデータを読み取ります。この操作は有効です。
- トランザクションを開始します。ロケータは前のトランザクションのIDを含んでいます。
- ロケータを使用してデータをLOBに書き込みます。ロケータが現行トランザクションに一致するトランザクションIDを含まないため、この書込み操作は失敗します。
14.4.4 トランザクション境界内でのロケータの選択
2つの使用例により、トランザクション内でロケータが選択された場合に、シリアライズ可能でないトランザクションでロケータを使用する方法について説明します。
シナリオ1:
-
トランザクションの中でロケータを選択します。この時点では、ロケータはトランザクションIDを含んでいます。
-
トランザクションを開始します。ロケータは前のトランザクションのIDを含んでいます。
-
ロケータを使用してLOBからデータを読み取ります。ロケータの中のトランザクションIDは現在のトランザクションに一致していませんが、この操作は有効です。
関連項目:
ロケータを使用したLOBデータの読取りの詳細は、「読取り一貫性のあるロケータ」を参照してください。
-
ロケータを使用してデータをLOBに書き込みます。ロケータの中のトランザクションIDが現在のトランザクションに一致していないため、この操作は失敗します。
シナリオ2:
- トランザクションを開始します。
- ロケータを選択します。ロケータがトランザクションの中で選択されたため、トランザクションIDが含まれています。
- ロケータを使用してLOBの読取りまたは書込みを行います。これらの操作は有効です。
- トランザクションをコミットまたはロールバックします。ロケータは引き続きトランザクションIDを含みます。
- ロケータを使用してLOBからデータを読み取ります。ロケータの中にトランザクションIDがあり、そのトランザクションはすでにコミットまたはロールバックされていますが、この操作は有効です。
- ロケータを使用してデータをLOBに書き込みます。ロケータの中のトランザクションIDはすでにコミットまたはロールバックされたトランザクション用であるため、この操作は失敗します。
14.4.5 複数のトランザクションにまたがることはできない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)の変更操作で使用するには、再選択する必要があります。
14.4.6 トランザクションにまたがらないロケータの例
この例では、「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; /
14.5 オブジェクト・キャッシュ内の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)
属性を使用してオブジェクトを作成する場合、BFILE
はNULL
に設定されます。これは、BFILE
から読み取られる前に有効なディレクトリ・オブジェクト名およびファイル名を使用して更新する必要があります。
14.6 サイズがTBのLOBのサポート
14.6.1 サイズが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
には、オペレーティング・システムに基づくその他のファイル・サイズ制限も適用されます。
14.6.2 サイズが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です。
この記憶域制限は、サイズがTBのLOBをサポートしている環境では、すべてのLOB型に適用されます。ただし、CLOB
型とNCLOB
型のサイズは文字単位で、BLOB
型のサイズはバイト単位であることに注意してください。
14.6.4 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パッケージ・プロシージャおよびタイプ・リファレンスを参照してください。
14.6.5 OCIでのサイズがTBのLOBの使用
Oracle Call Interface APIには、すべてのサイズのLOBを操作するための関数セットが用意されています。
OCILobGetChunkSize()
は、BLOB
の場合はバイト単位で、CLOB
の場合は文字単位で値を戻します。可変幅文字セットの場合、値は幅に応じたUnicode文字数となります。チャンクに格納されるバイト数は、内部記憶域のオーバヘッドにより、実際にはCHUNK
パラメータのサイズより小さくなります。OCILobGetStorageLimit()
関数は、現在のデータベース・インストレーションにおける内部LOBの最大許容サイズをバイト単位で戻します。LOB全体が読み取られるストリーム・モードが使用される場合は、チャンク・サイズを取得する必要はありません。
関連項目:
LOBをサポートするOCI関数の詳細は、Oracle Call Interfaceプログラマーズ・ガイドを参照してください
14.7 サイズが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など)を選択してエクステント作成の頻度を削減するか、またはより頻繁にトランザクションをコミットして、ロールバック・セグメントの領域を再利用します。
14.7.1 サイズが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に設定することをお薦めします。