15 パフォーマンスのガイドライン
LOBデータ型を使用するアプリケーションのパフォーマンスに関するガイドラインがあります。
15.1 LOBパフォーマンスのガイドライン
LOBデータ型を使用するアプリケーションに適用されるパフォーマンスに関する様々なガイドラインがあります。
15.1.1すべてのLOB
この項では、LOBの使用に関するガイドラインについて説明します。
15.1.1.1 チャンク・サイズ
チャンクとは1つまたは複数のOracleブロックです。LOBを含む表を作成する場合に、LOB用のチャンク・サイズを指定できます。
これは、LOB値に対してアクセスまたは変更を行うときに、Oracle Databaseが使用するデータ・サイズに対応します。チャンクの一部はシステム関連の情報を格納し、残りはLOB値を格納します。使用するAPIには、LOBチャンク内で使用され、LOB値を格納している領域の量を戻すファンクションがあります。PL/SQLでは、DBMS_LOB.GETCHUNKSIZE
を使用します。OCIでは、OCILobGetChunkSize()
を使用します。SecureFilesの場合は、表領域のブロック・サイズの使用可能なデータ領域が戻されます。
15.1.1.2 LOBプリフェッチ
LOBプリフェッチでは、データの最初の部分をプレビューしたり、格納されているデータにLOBロケータ・インタフェースを使用してアクセスできます
LOBプリフェッチでは、次の操作を実行できます。
-
データの最初の部分をプレビューします
-
格納されているデータにロケータ・インタフェースを使用してアクセスします
15.1.2 永続LOB
15.1.2.1 サイズの小さいBasicFiles LOBのパフォーマンスに関するガイドライン
データベース表内のほとんどのLOBのサイズが小さい場合、次のガイドラインに従ってください。
データベース表内のLOBが8KB以下で、8KBを超えるLOBを含む行が少ない場合は、次のガイドラインに従ってデータベースのパフォーマンスを最大化します。
-
ENABLE STORAGE IN ROW
を使用します。 -
DB_BLOCK_SIZE
初期化パラメータを8KBに設定し、8KBのチャンク・サイズを使用します。 -
関連項目:
LOBセグメント用の
CACHE
、PCTVERSION
、CHUNK
などの他のパラメータのチューニングについては、「LOB記憶域パラメータ」を参照してください
15.1.2.2 BasicFiles LOBの一般的なパフォーマンスに関するガイドライン
BasicFiles LOBでのパフォーマンスは最適化できます。
次のガイドラインを使用して、BasicFiles LOBでのパフォーマンスを最適化します。
-
可能な場合、大きいデータ・チャンクの読取り/書込みを一度に行います。
LOBは大きいため、チャンクの大きい複数のLOB値の読取りおよび書込みを一度に行うことにより、最良のパフォーマンスを実現できます。これは次の点で有効です。
-
クライアント側からLOBにアクセスし、クライアントがサーバーと異なるノードにある場合、大量の読取りおよび書込みを行うことでネットワーク・オーバーヘッドが削減されます。
-
NOCACHE
オプションを使用する場合、少量の読取りおよび書込みを行うたびにI/Oが発生します。大量に読取りおよび書込みを行うことでI/Oが削減されます。 -
LOBに書込みを行うと、新しいバージョンのLOBチャンクが作成されます。そのため、一度の書込み量が少ないと、書込みが行われるたびに新しいバージョンを作成するコストがかかります。ロギングが有効な場合、チャンクはREDOログにも格納されます。
-
-
コールバックとともに
OCILobRead2()
およびOCILobWrite2()
を使用します。これにより、データがLOBに、またはLOBからストリームされるようにします。入力時に、
amount
パラメータに書込み全体の長さが設定されていることを確認してください。可能な場合は、LOBチャンク・サイズの倍数のサイズの読取りおよび書込みを行います。 -
LOB用のチェックアウトまたはチェックイン・モデルを使用します。
LOBは、次の操作を行うために最適化されています。
-
LOB値全体を置換するSQLの
UPDATE
。 -
クライアントにLOBデータ全体をコピーし、そのLOBデータをクライアント側で変更し、LOBデータ全体をデータベースに再度コピーする操作。これは、ストリームのある
OCILobRead2
()およびOCILobWrite2
()を使用して行われます。
-
-
頻繁に変更をコミットします。
15.1.3 一時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を指すようになります。
15.2 スレッド環境における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データが永続になります。
15.3 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「統計の説明」
15.3.1 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 chunksize. 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); 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;