15 パフォーマンスのガイドライン
LOBデータ型を使用するアプリケーションのパフォーマンスに関するガイドラインがあります。
LOBパフォーマンスのガイドライン
LOBには、データ・インタフェースを使用するかLOB APIを使用してアクセスできます。この項では、LOBを使用する際のパフォーマンス・ガイドラインを示します。
すべてのLOB
- I/Oを最小限にするには:
- ブロック境界でデータの読取りおよび書込みを行います。これにより、UNDO生成を最小限にするなどの様々な方法でI/Oが最適化されます。一時LOBおよびSecureFile LOBの場合、表領域ブロック・サイズの使用可能データ領域は、PLSQLでは
DBMS_LOB.GETCHUNKSIZE
というAPI、OCIではOCILobGetChunkSize()
によって戻されます。ループで書き込む場合は、1つのデータベース・ブロックに書き込む必要があるすべての内容を1回の書込みコールで書き込むようにコードを設計し、それによって、同じブロックに連続的に書き込まれることがないようにします。 - 一度に大量のデータの読取りおよび書込みを行います。
- 前述の2つの推奨事項を組み合せて行うには、
DBMS_LOB.GETCHUNKSIZE/OCILobGetChunkSize()
APIによって返されたデータベース・ブロック・サイズに大きい整数を乗算したサイズで、読取りおよび書込みを行います。
- ブロック境界でデータの読取りおよび書込みを行います。これにより、UNDO生成を最小限にするなどの様々な方法でI/Oが最適化されます。一時LOBおよびSecureFile LOBの場合、表領域ブロック・サイズの使用可能データ領域は、PLSQLでは
- サーバーとのラウンドトリップの回数を最小限にするには:
- LOBデータの最大サイズを把握しており、LOB全体を読み取るか書き込む場合は、次に説明するように、データ・インタフェースを使用します。LOB全体のサイズを単一のバッファとして割り当てるか、ピース単位方式またはコールバック方式を使用することができます。
- 読取り操作の場合は、OCIで
OCIDefineByPos()
関数、JDBCでDefineColumnType()
関数を使用して、LOBを文字型またはバイナリ型として定義します。 - 書込み操作の場合は、OCIで
OCIBindByPos()
関数、JDBCでsetString()
メソッドまたはsetBytes()
メソッドを使用して、LOBを文字型またはバイナリ型としてバインドします。
- 読取り操作の場合は、OCIで
- それ以外の場合は、次のようにLOB APIを使用します。
- 読取りにはLOBのプリフェッチを使用します。その列に大部分のLOB値を含めることができるようにLOBプリフェッチ・サイズを定義します。
-
OCILobRead2
操作またはOCILobWrite2
操作を使用する際にピース単位方式またはコールバック方式を使用し、サーバーとのラウンドトリップを最小限にします。
- LOBデータの最大サイズを把握しており、LOB全体を読み取るか書き込む場合は、次に説明するように、データ・インタフェースを使用します。LOB全体のサイズを単一のバッファとして割り当てるか、ピース単位方式またはコールバック方式を使用することができます。
関連項目:
永続LOB用のデータ・インタフェース永続LOB
前述の、すべてのLOBに当てはまるパフォーマンス・ガイドラインに加え、ここでは、永続LOBを使用する際のパフォーマンス・ガイドラインを示します。
- 1つのトランザクション内でコールを連続して行うことで、単一のLOBへの書込みを最大限にします。頻繁にLOBを切り替えるかDML文をインターリーブすると、キャッシュが最大効率になるのを防ぐことができます。
- セーブポイントの作成やコミットは頻繁に実行しないでください。これにより、書込みの間のキャッシュの利点がなくなります。
注意:
永続LOBの格納にはSecurefile LOBを使用することをお薦めしています。そのため、この章では、Securefileストレージのみに焦点を当てます。永続LOBに関する文脈では、LOBとは、他に示されていないかぎり、Securefile LOBを意味します。一時LOB
一般的なLOBパフォーマンスに関するガイドライン「LOBパフォーマンスのガイドライン」に加えて、一時LOBの使用に対するガイドラインを次に示します。
-
パフォーマンスを向上させるために、PGAメモリーを使用して一時LOBを格納します。
-
デフォルトのシステム表領域のかわりに、一時LOB記憶域に別の一時表領域を使用します
これによって、データを永続LOBから一時LOBにコピーする場合のデバイスの競合を回避します。
新しく提供された拡張SQLセマンティクス機能をアプリケーションで使用する場合、以前よりさらに多くの一時LOBがSQLおよびPL/SQLで暗黙的に作成されます。これらの一時LOBを格納するための一時表領域が、使用しているアプリケーションに対して十分大きいことを確認してください。特に、これらの一時LOBが暗黙的に作成されるのは、次の事項を使用または実行する場合です。
-
LOBに対するSQLファンクション
-
LOBに対するPL/SQL組込み文字ファンクション
-
VARCHAR2
からCLOB
、RAW
からBLOB
への変数割当て。 -
LONG
からLOB
への移行
-
-
LOBでSQL演算子を使用する場合、PGAメモリーおよび一時表領域は、SQL演算子によって生成される一時LOBを格納するのに十分な大きさがある必要があります。
-
SQL問合せおよびPL/SQLプログラムから戻された一時LOBを解放します
PL/SQL、C(OCI)、Javaおよび他のプログラム・インタフェースでは、SQL問合せの結果またはPL/SQLプログラムの実行によって、LOBのオペレーション・コールおよびファンクション・コールに対して一時LOBが戻されます。次に例を示します。
SELECT substr(CLOB_Column, 4001, 32000) FROM ...
PL/SQLで問合せが実行される場合、戻された一時LOBは、PL/SQLプログラム・ブロックの終了時に自動的に解放されます。任意の時点で、明示的に一時LOBを解放することもできます。OCIおよびJavaでは、戻された一時LOBを明示的に解放する必要があります。
SQL問合せから戻された一時LOBの割当てを適切に解除しないと、一時表領域が一杯となり、パフォーマンスが低下します。
-
PL/SQLでは、可能なかぎりNOCOPYを使用して、参照によって一時LOBパラメータを渡します。
関連項目:
参照によるパラメータの受渡しおよびパラメータのエイリアシングの詳細は、Oracle Database PL/SQL言語リファレンスを参照してください
-
一時LOBに対してバッファ・キャッシュを利用します。
CACHEパラメータをTRUEに設定して作成した一時LOBは、バッファ・キャッシュを介して移動できます。そうでない場合、一時LOBの読取りおよび書込みはディスクから直接行われます。
-
最適なパフォーマンスを実現するために、一時LOBでは、読取り時参照、書込み時コピーのセマンティクスが使用されます。一時LOBロケータが別のロケータに割り当てられる場合、物理LOBデータはコピーされません。どちらかのLOBロケータを使用する以降のREAD操作では、同じ物理LOBデータが参照されます。割当て後の最初のWRITE操作では、LOB値のセマンティクスを保ち、各ロケータが確実に一意のLOB値を指すように、物理LOBデータがコピーされます。このパフォーマンスの考慮事項は、主としてPL/SQL環境とOCI環境に適用されます。
PL/SQLでは、読取り時参照、書込み時コピーのセマンティクスは次のように示されます。
LOCATOR1 BLOB; LOCATOR2 BLOB; DBMS_LOB.CREATETEMPORARY (LOCATOR1,TRUE,DBMS_LOB.SESSION); -- LOB data is not copied in this assignment operation: LOCATOR2 := LOCATOR; -- These read operations refer to the same physical LOB copy: DBMS_LOB.READ(LOCATOR1, ...); DBMS_LOB.GETLENGTH(LOCATOR2, ...); -- A physical copy of the LOB data is made on WRITE: DBMS_LOB.WRITE(LOCATOR2, ...);
OCIでは、LOBロケータおよびデータの値セマンティクスを保証するために、
OCILobLocatorAssign()
を使用してLOBデータと一時LOBロケータがコピーされます。OCILobLocatorAssign()
の場合、サーバーへのラウンドトリップは発生しません。物理的な一時LOBコピーは、次のように、LOB更新APIと同じラウンドトリップでLOBの更新が発生する場合に実行されます。OCILobLocator *LOC1; OCILobLocator *LOC2; OCILobCreateTemporary(... LOC1, ... TRUE,OCI_DURATION_SESSION); /* No round-trip is incurred in the following call. */ OCILobLocatorAssign(... LOC1, LOC2); /* Read operations refer to the same physical LOB copy. */ OCILobRead2(... LOC1 ...) /* One round-trip is incurred to make a new copy of the * LOB data and to write to the new LOB copy. */ OCILobWrite2(... LOC1 ...) /* LOC2 does not see the same LOB data as LOC1. */ OCILobRead2(... LOC2 ...)
LOB値セマンティクスを意図していない場合は、次のように、Cのポインタにより参照セマンティクスを使用できます。
OCILobLocator *LOC1; OCILobLocator *LOC2; OCILobCreateTemporary(... LOC1, ... TRUE,OCI_DURATION_SESSION); /* Pointer is copied. LOC1 and LOC2 refer to the same LOB data. */ LOC2 = LOC1; /* Write to LOC2. */ OCILobWrite2(...LOC2...) /* LOC1 sees the change made to LOC2. */ OCILobRead2(...LOC1...)
-
一時LOBに対してOCI_OBJECTモードを使用します。
LOB割当てでの一時LOBのパフォーマンスを向上させるには、
OCILobLocatorAssign()
に対してOCI_OBJECT
モードを使用します。OCI_OBJECT
モードでは、データベースはディープ・コピー回数を最小限にしようとします。そのため、OCI_OBJECT
モードでソース一時LOBに対してOCILobLocatorAssign()
が実行された後、ソース・ロケータおよび宛先ロケータは、いずれかのLOBロケータにより変更されるまで同一LOBを指すようになります。
スレッド環境におけるLOBへのデータの移動
スレッド環境では、LOBへのデータの移動に使用できる手順が2つありますが、その1つは適切ではありません。
推奨手順
注意:
-
この手順では、空のLOBを作成する必要はありません。
-
INSERT/UPDATE
文にRETURNING
句を使用することで、ロックされたLOBロケータを戻すことができます。これによって、前述のステップ3のSELECT-FOR-UPDATE
を行う必要がなくなります。
推奨手順は次のとおりです。
-
空のLOBを
INSERT
し、LOBロケータをRETURNING
します。 -
このロケータを使用してデータをLOBに移動します。
-
COMMIT
します。これでROWロックが解放され、LOBデータが永続になります。
また、LOB列またはLOB属性の4000バイトを超えるデータを直接挿入できます。
不適切な手順
スレッド環境を使用する場合に新規の接続が必要となる次の手順は、パフォーマンスに悪影響を与えるため、お薦めしません。
-
空の(
NULL
でない)LOBを作成します。 -
空のLOBを使用して
INSERT
を実行します -
入力直後の行を
SELECT-FOR-UPDATE
します。 -
データをLOBに移動します。
-
COMMIT
します。これでROW
ロックが解放され、LOBデータが永続になります。
LOBアクセス統計
Oracle Database 10gリリース2から、LOB固有の3つのセッションレベルの統計、LOB読取り、LOB書込みおよび未調整LOB書込みが提供されています。
セッション統計は、V$MYSTAT
、V$SESSTAT
およびV$SYSSTAT
の動的パフォーマンス・ビューからアクセスできます。これらのビューに対して問合せを行うには、SELECT_CATALOG_ROLE
、SELECT ON SYS.V_$MYSTAT
ビューおよびSELECT ON SYS.V_$STATNAME
ビューの権限が必要です。
LOB読取りは、セッションまたはシステム内で実行されるLOB APIの読取り操作の合計として定義されています。1回のLOB API読取りが、複数回の物理/論理ディスク・ブロック読取りに相当する場合もあります。
LOB書込みは、セッションまたはシステム内で実行されるLOB APIの書込み操作の合計として定義されています。1回のLOB API書込みが、複数回の物理/論理ディスク・ブロック書込みに相当する場合もあります。
未調整LOB書込みは、開始オフセットまたはバッファ・サイズがLOBのブロック境界に揃えられていないLOB API書込み操作の数として定義付けられています。ブロックの境界に揃えられた書込みが、最も効率のよい書込み操作となります。LOBの使用可能なブロック・サイズは、LOB API(たとえば、PL/SQLを使用する場合はDBMS_LOB.GETCHUNKSIZE()
)を使用して取得できます。
次の簡単な例は、LOBの読取りまたは書込み操作時に、LOBセッション統計が更新される様子を示しています。
セッション統計は、単一セッション中にアクセスされたすべてのLOBに対する操作を総計した統計です。統計はオブジェクトごと(表、列、セグメント、オブジェクト番号など)に分離または分類されていません。
この例では、各デモごとにデータベースに再接続してV$MYSTAT
をクリアします。これは、同一セッションで実行された過去のLOB操作の結果を不明瞭にすることなく、テスト対象の操作によりLOB統計がどのように変化するかを確認できるようにするためです。
関連項目:
『Oracle Databaseリファレンス』の付録E「統計の説明」
LOBアクセス統計の取得
この例では、LOBアクセス統計の取得を示します。
rem rem Set up the user rem CONNECT / AS SYSDBA; SET ECHO ON; GRANT SELECT_CATALOG_ROLE TO pm; GRANT SELECT ON sys.v_$mystat TO pm; GRANT SELECT ON sys.v_$statname TO pm; rem rem Create a simplified view for statistics queries rem CONNECT pm; SET ECHO ON; DROP VIEW mylobstats; CREATE VIEW mylobstats AS SELECT SUBSTR(n.name,1,20) name, m.value value FROM v$mystat m, v$statname n WHERE m.statistic# = n.statistic# AND n.name LIKE 'lob%'; rem rem Create a test table rem DROP TABLE t; CREATE TABLE t (i NUMBER, c CLOB) lob(c) STORE AS (DISABLE STORAGE IN ROW); rem rem Populate some data rem rem This should result in unaligned writes, one for rem each row/lob populated. rem CONNECT pm SELECT * FROM mylobstats; INSERT INTO t VALUES (1, 'a'); INSERT INTO t VALUES (2, rpad('a',4000,'a')); COMMIT; SELECT * FROM mylobstats; rem rem Get the lob length rem rem Computing lob length does not read lob data, no change rem in read/write stats. rem CONNECT pm; SELECT * FROM mylobstats; SELECT LENGTH(c) FROM t; SELECT * FROM mylobstats; rem rem Read the lobs rem rem Lob reads are performed, one for each lob in the table. rem CONNECT pm; SELECT * FROM mylobstats; SELECT * FROM t; SELECT * FROM mylobstats; rem rem Read and manipulate the lobs (through temporary lobs) rem rem The use of complex operators like "substr()" results in rem the implicit creation and use of temporary lobs. operations rem on temporary lobs also update lob statistics. rem CONNECT pm; SELECT * FROM mylobstats; SELECT substr(c, length(c), 1) FROM t; SELECT substr(c, 1, 1) FROM t; SELECT * FROM mylobstats; rem rem Perform some aligned overwrites rem rem Only lob write statistics are updated because both the rem byte offset of the write, and the size of the buffer rem being written are aligned on the lob block size. rem CONNECT pm; SELECT * FROM mylobstats; DECLARE loc CLOB; buf LONG; chunk NUMBER; BEGIN SELECT c INTO loc FROM t WHERE i = 1 FOR UPDATE; chunk := DBMS_LOB.GETCHUNKSIZE(loc); chunk = chunk * floor(32767/chunk); /* integer multiple of chunk */ buf := rpad('b', chunk, 'b'); -- aligned buffer length and offset DBMS_LOB.WRITE(loc, chunk, 1, buf); DBMS_LOB.WRITE(loc, chunk, 1+chunk, buf); COMMIT; END; / SELECT * FROM mylobstats; rem rem Perform some unaligned overwrites rem rem Both lob write and lob unaligned write statistics are rem updated because either one or both of the write byte offset rem and buffer size are unaligned with the lob's chunksize. rem CONNECT pm; SELECT * FROM mylobstats; DECLARE loc CLOB; buf LONG; BEGIN SELECT c INTO loc FROM t WHERE i = 1 FOR UPDATE; buf := rpad('b', DBMS_LOB.GETCHUNKSIZE(loc), 'b'); -- unaligned buffer length DBMS_LOB.WRITE(loc, DBMS_LOB.GETCHUNKSIZE(loc)-1, 1, buf); -- unaligned start offset DBMS_LOB.WRITE(loc, DBMS_LOB.GETCHUNKSIZE(loc), 2, buf); -- unaligned buffer length and start offset DBMS_LOB.WRITE(loc, DBMS_LOB.GETCHUNKSIZE(loc)-1, 2, buf); COMMIT; END; / SELECT * FROM mylobstats; DROP TABLE t; DROP VIEW mylobstats; CONNECT / AS SYSDBA REVOKE SELECT_CATALOG_ROLE FROM pm; REVOKE SELECT ON sys.v_$mystat FROM pm; REVOKE SELECT ON sys.v_$statname FROM pm; QUIT;