この章の内容は次のとおりです。
この項では、LOBデータ型を使用するアプリケーションのパフォーマンスに関するガイドラインについて説明します。
チャンクとは1つまたは複数のOracleブロックです。LOBを含む表を作成する場合に、LOB用のチャンク・サイズを指定できます。これは、LOB値に対してアクセスまたは変更を行うときに、Oracle Databaseが使用するデータ・サイズに対応します。チャンクの一部はシステム関連の情報を格納し、残りはLOB値を格納します。使用するAPIには、LOBチャンク内で使用され、LOB値を格納している領域の量を戻すファンクションがあります。PL/SQLでは、DBMS_LOB.GETCHUNKSIZE
を使用します。OCIでは、OCILobGetChunkSize()
を使用します。SecureFilesの場合は、表領域のブロック・サイズの使用可能なデータ領域が戻されます。
データベース表内のほとんどのLOBのサイズが小さく(8KB以下)、8KBを超えるLOBを含む行が少ない場合は、次のガイドラインに従ってデータベースのパフォーマンスを最大化します。
ENABLE STORAGE IN ROW
を使用します。
DB_BLOCK_SIZE
初期化パラメータを8KBに設定し、8KBのチャンク・サイズを使用します。
CACHE
、PCTVERSION
、LOBセグメント用のCHUNK
など、他のパラメータのチューニングについては、「LOB記憶域パラメータ」を参照してください。
次のガイドラインを使用して、BasicFiles LOBでのパフォーマンスを最適化します。
大きなデータ・チャンクでは一度に読取り/書込みをする(可能な場合): LOBは大きいため、大きなピースのLOB値を一度に読取りおよび書込みすることによりパフォーマンスを最適化できます。これは次の点で有効です。
クライアント側からLOBにアクセスし、クライアントがサーバーと異なるノードにある場合、大量の読取りおよび書込みを行うことでネットワーク・オーバーヘッドが削減されます。
NOCACHE
オプションを使用する場合、少量の読取りおよび書込みを行うたびにI/Oが発生します。大量に読取りおよび書込みを行うことでI/Oが削減されます。
LOBに書込みを行うと、新しいバージョンのLOBチャンクが作成されます。そのため、一度の書込み量が少ないと、書込みが行われるたびに新しいバージョンを作成するコストがかかります。ロギングが有効な場合、チャンクはREDOログにも格納されます。
データのスモール・チャンクの読取り/書込みにLOBバッファリングを使用: クライアントでチャンクの小さいLOBデータの読取りおよび書込みを行う必要がある場合、LOBバッファリングを使用します。OCILobEnableBuffering()
、OCILobDisableBuffering()
、OCILobFlushBuffer()
、OCILobWrite2()
およびOCILobRead2()
を参照してください。基本的に、LOBバッファリングは、チャンクの小さいLOBデータの読取り/書込みを行う前にオンにします。
コールバックでOCILobRead2()および OCILobWrite2()を使用: これによりLOBとの間でデータがストリーミングされます。入力時に、amount
パラメータに書込み全体の長さが設定されていることを確認してください。可能な場合は、LOBチャンク・サイズの倍数のサイズの読取りおよび書込みを行います。
LOBのチェックアウト/チェックイン・モデルを使用: LOBは次の操作のために最適化されます。
LOB値全体を置換するSQLのUPDATE
。
クライアントにLOBデータ全体をコピーし、そのLOBデータをクライアント側で変更し、LOBデータ全体をデータベースに再度コピーする操作。これは、ストリームのあるOCILobRead2
()およびOCILobWrite2
()を使用して行われます。
頻繁に変更をコミットします。
前述の一般的なLOBパフォーマンスに関するガイドライン「LOBパフォーマンスのガイドライン」に加えて、一時LOBの使用に対するガイドラインを次に示します。
デフォルトのシステム表領域のかわりに、一時LOB記憶域に別の一時表領域を使用します。これによって、データを永続LOBから一時LOBにコピーする場合のデバイスの競合を回避します。
新しく提供された拡張SQLセマンティクス機能をアプリケーションで使用する場合、以前よりさらに多くの一時LOBがSQLおよびPL/SQLで暗黙的に作成されます。これらの一時LOBを格納するための一時表領域が、使用しているアプリケーションに対して十分大きいことを確認してください。特に、これらの一時LOBが暗黙的に作成されるのは、次の事項を使用または実行する場合です。
LOBに対するSQLファンクション
LOBに対するPL/SQL組込み文字ファンクション
VARCHAR2
からCLOB
、RAW
からBLOB
への変数割当て
LONG
からLOB
への移行
PL/SQLでは、可能なかぎりNOCOPYを使用して、参照によって一時LOBパラメータを渡します。参照によるパラメータの受渡しおよびパラメータのエイリアシングの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。
一時LOBに対してバッファ・キャッシュを利用します。CACHEパラメータをTRUEに設定して作成した一時LOBは、バッファ・キャッシュを介して移動できます。CACHEパラメータを設定しなかった場合、一時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を指すようになります。
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の割当てを適切に解除しないと、一時表領域が一杯となり、パフォーマンスが低下します。
LOBでSQLセマンティクスを使用する場合、次のパフォーマンスの問題に注意してください。
一時表領域が、アウトラインに格納されたLOBに対して十分な大きさであることを確認してください。サイズが約4000バイトを超える永続LOBは、LOB列の外に格納されます。
可能なかぎり、不要な一時LOBインスタンスを解放します。一時LOBインスタンスを明示的に解放しないと、アプリケーションの実行中もLOBがそのまま存在します。さらに、LOBが宣言されたスコープの実行中もインスタンスが存在します。
スレッド環境では、LOBへのデータの移動に使用できる手順が2つありますが、その1つは適切ではありません。
推奨手順
注意:
|
推奨手順は次のとおりです。
空のLOBをINSERT
し、LOBロケータをRETURNING
します。
このロケータを使用してデータをLOBに移動します。
COMMIT
します。これでROWロックが解放され、LOBデータが永続になります。
また、LOB列またはLOB属性の4000バイトを超えるデータを直接挿入できます。
不適切な手順
スレッド環境を使用する場合に新規の接続が必要となる次の手順は、パフォーマンスに悪影響を与えるため、お薦めしません。
空の(NULL
でない)LOBを作成します。
空のLOBを使用してINSERT
を実行します
入力直後の行をSELECT-FOR-UPDATE
します。
データをLOBに移動します。
COMMIT
します。これでROW
ロックが解放され、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の読取り操作の合計として定義されています。単一のLOB API読取りが、物理または論理ディスク・ブロックへの複数の読取りに対応する場合もあります。
LOB書込みは、セッションまたはシステム内で実行されるLOB APIの書込み操作の合計として定義されています。単一の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アクセス統計を取得するために作成されたものです。
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;