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

前
次

14 パフォーマンスのガイドライン

LOBデータ型を使用するアプリケーションのパフォーマンスに関するガイドラインがあります。

LOBパフォーマンスのガイドライン

LOBデータ型を使用するアプリケーションに適用されるパフォーマンスに関する様々なガイドラインがあります。

チャンク・サイズ

チャンクとは1つまたは複数のOracleブロックです。LOBを含む表を作成する場合に、LOB用のチャンク・サイズを指定できます。

これは、LOB値に対してアクセスまたは変更を行うときに、Oracle Databaseが使用するデータ・サイズに対応します。チャンクの一部はシステム関連の情報を格納し、残りはLOB値を格納します。使用するAPIには、LOBチャンク内で使用され、LOB値を格納している領域の量を戻すファンクションがあります。PL/SQLでは、DBMS_LOB.GETCHUNKSIZEを使用します。OCIでは、OCILobGetChunkSize()を使用します。SecureFilesの場合は、表領域のブロック・サイズの使用可能なデータ領域が戻されます。

サイズの小さいBasicFiles LOBのパフォーマンスに関するガイドライン

データベース表内のほとんどのLOBのサイズが小さい場合、次のガイドラインに従ってください。

データベース表内のLOBが8KB以下で、8KBを超えるLOBを含む行が少ない場合は、次のガイドラインに従ってデータベースのパフォーマンスを最大化します。

  • ENABLE STORAGE IN ROWを使用します。

  • DB_BLOCK_SIZE初期化パラメータを8KBに設定し、8KBのチャンク・サイズを使用します。

  • LOBセグメント用のCACHEPCTVERSIONCHUNKなどの他のパラメータのチューニングについては、LOB記憶域パラメータを参照してください。

BasicFiles LOBの一般的なパフォーマンスに関するガイドライン

BasicFiles LOBでのパフォーマンスは最適化できます。

次のガイドラインを使用して、BasicFiles LOBでのパフォーマンスを最適化します。

  • 可能な場合、大きいデータ・チャンクの読取り/書込みを一度に行います。

    LOBは大きいため、チャンクの大きい複数のLOB値の読取りおよび書込みを一度に行うことにより、最良のパフォーマンスを実現できます。これは次の点で有効です。

    1. クライアント側からLOBにアクセスし、クライアントがサーバーと異なるノードにある場合、大量の読取りおよび書込みを行うことでネットワーク・オーバーヘッドが削減されます。

    2. NOCACHEオプションを使用する場合、少量の読取りおよび書込みを行うたびにI/Oが発生します。大量に読取りおよび書込みを行うことでI/Oが削減されます。

    3. LOBに書込みを行うと、新しいバージョンのLOBチャンクが作成されます。そのため、一度の書込み量が少ないと、書込みが行われるたびに新しいバージョンを作成するコストがかかります。ロギングが有効な場合、チャンクはREDOログにも格納されます。

  • LOBバッファリングを使用してチャンクの小さいデータの読取りおよび書込みを行います。

    クライアントでチャンクの小さいLOBデータの読取りおよび書込みを行う必要がある場合、LOBバッファリングを使用します。OCILobEnableBuffering()OCILobDisableBuffering()OCILobFlushBuffer()OCILobWrite2()およびOCILobRead2()を参照してください。基本的に、LOBバッファリングは、チャンクの小さいLOBデータの読取り/書込みを行う前にオンにします。

    関連項目:

    LOBバッファリングの詳細は、LOBバッファリング・サブシステム(LBS)を参照してください。

  • コールバックとともにOCILobRead2()およびOCILobWrite2()を使用します。

    これにより、データがLOBに、またはLOBからストリームされるようにします。入力時に、amountパラメータに書込み全体の長さが設定されていることを確認してください。可能な場合は、LOBチャンク・サイズの倍数のサイズの読取りおよび書込みを行います。

  • LOB用のチェックアウトまたはチェックイン・モデルを使用します。

    LOBは、次の操作を行うために最適化されています。

    • LOB値全体を置換するSQLのUPDATE

    • クライアントにLOBデータ全体をコピーし、そのLOBデータをクライアント側で変更し、LOBデータ全体をデータベースに再度コピーする操作。これは、ストリームのあるOCILobRead2()およびOCILobWrite2()を使用して行われます。

  • 頻繁に変更をコミットします。

一時LOBパフォーマンスのガイドライン

一般的なLOBパフォーマンスに関するガイドラインLOBパフォーマンスのガイドラインに加えて、一時LOBの使用に対するガイドラインを次に示します。

  • デフォルトのシステム表領域のかわりに、一時LOB記憶域に別の一時表領域を使用します

    これによって、データを永続LOBから一時LOBにコピーする場合のデバイスの競合を回避します。

    新しく提供された拡張SQLセマンティクス機能をアプリケーションで使用する場合、以前よりさらに多くの一時LOBがSQLおよびPL/SQLで暗黙的に作成されます。これらの一時LOBを格納するための一時表領域が、使用しているアプリケーションに対して十分大きいことを確認してください。特に、これらの一時LOBが暗黙的に作成されるのは、次の事項を使用または実行する場合です。

    • LOBに対するSQLファンクション

    • LOBに対するPL/SQL組込み文字ファンクション

    • VARCHAR2からCLOBRAWからBLOBへの変数割当て

    • LONGから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を指すようになります。

  • 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の割当てを適切に解除しないと、一時表領域が一杯となり、パフォーマンスが低下します。

SQLセマンティクスとLOBのパフォーマンスに関する考慮事項

LOBでSQLセマンティクスを使用する場合、次のパフォーマンスの問題に注意してください。

  • 一時表領域が、アウトラインに格納されたLOBに対して十分な大きさであることを確認してください。サイズが約4000バイトを超える永続LOBは、LOB列の外に格納されます。

  • 可能なかぎり、不要な一時LOBインスタンスを解放します。一時LOBインスタンスを明示的に解放しないと、アプリケーションの実行中もLOBがそのまま存在します。さらに、LOBが宣言されたスコープの実行中もインスタンスが存在します。

    関連項目:

    LOBに対するSQLセマンティクスのサポートの詳細は、「SQLセマンティクスとLOB」を参照してください。

スレッド環境におけるLOBへのデータの移動

スレッド環境では、LOBへのデータの移動に使用できる手順が2つありますが、その1つは適切ではありません。

推奨手順

注意:

  • この手順では、空のLOBを作成する必要はありません。

  • INSERT/UPDATE文にRETURNING句を使用することで、ロックされたLOBロケータを戻すことができます。これによって、前述の手順3のSELECT-FOR-UPDATEを行う必要がなくなります。

推奨手順は次のとおりです。

  1. 空のLOBをINSERTし、LOBロケータをRETURNINGします。

  2. このロケータを使用してデータをLOBに移動します。

  3. COMMITします。これでROWロックが解放され、LOBデータが永続になります。

また、LOB列またはLOB属性の4000バイトを超えるデータを直接挿入できます。

不適切な手順

スレッド環境を使用する場合に新規の接続が必要となる次の手順は、パフォーマンスに悪影響を与えるため、お薦めしません。

  1. 空の(NULLでない)LOBを作成します。

  2. 空のLOBを使用してINSERTを実行します

  3. 入力直後の行をSELECT-FOR-UPDATEします。

  4. データをLOBに移動します。

  5. COMMITします。これでROWロックが解放され、LOBデータが永続になります。

LOBアクセス統計

Oracle Database 10gリリース2から、LOB固有の3つのセッションレベルの統計、LOB読取り、LOB書込みおよび未調整LOB書込みが提供されています。

セッション統計は、V$MYSTATV$SESSTATおよびV$SYSSTATの動的パフォーマンス・ビューからアクセスできます。これらのビューに対して問合せを行うには、SELECT_CATALOG_ROLESELECT 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アクセス統計の取得の例

この例では、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;