ヘッダーをスキップ
Oracle® Database 2日で開発者ガイド
11gリリース2 (11.2)
B56265-06
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

8 有効なアプリケーションの作成

この章の内容は、次のとおりです。


参照:

Oracle Database用に最適化されたアプリケーションを作成およびデプロイする方法の詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。

スケーラブルなアプリケーションの作成

この項の内容は次のとおりです。

スケーラブルなアプリケーションについて

スケーラブルなアプリケーションは、システム・リソースの使用量の増加に比例して、より多くのワークロードを処理できます。たとえば、ワークロードが倍増した場合、スケーラブルなアプリケーションでは、システム・リソースが2倍使用されます。

非スケーラブルなアプリケーションでは、システム・リソースが完全に消耗されるため、アプリケーション・ワークロードを増やした場合、スループットは向上しません。非スケーラブルなアプリケーションの場合、スループットが固定され、レスポンス時間が低下します。

リソースの消耗の例:

  • ハードウェアの消耗

  • ディスクの入力/出力(I/O)不足を必然的に引き起こす、大規模トランザクションでの表スキャン

  • ネットワークおよびスケジューリングのボトルネックを引き起こす過剰なネットワーク・リクエスト

  • メモリー割当てによって、ページングとスワッピングが発生する場合

  • プロセスやスレッドの過剰な割当てによって、オペレーティング・システムのスラッシングが発生する場合

ユーザー移入およびデータ量にかかわらず、同じリソースを使用し、システム・リソースに過負荷をかけないようにアプリケーションを設計してください。

バインド引数を使用したスケーラビリティの向上

バインド引数を正しく使用すると、効率的でスケーラブルなアプリケーションを開発できます。

サブプラグラムがパラメータを持ち、その値をインボーカが指定するのと同じように、SQL文はバインド引数のプレースホルダを持ち、その値(バインド引数と呼ばれる)は実行時に指定されます。サブプログラムがコンパイルされると、様々なパラメータで何度も実行されるのと同じように、バインド引数のプレースホルダを持つSQL文は、ハード解析されると、様々なバインド引数でソフト解析されます。

最適化および行ソース生成を含むハード解析は、CPUに大きく負担をかける処理です。最適化と行ソース生成がスキップされ、ただちに実行されるソフト解析は、同じ文のハード解析より通常大幅に処理が速くなります。(ハード解析とソフト解析の違いを含む、SQL処理の概要は、『Oracle Database概要』を参照してください。)

ハード解析はCPUに負担をかける処理であるだけでなく、他の多くの処理と同時に実行できないため非スケーラブルな処理です。同時実行性およびスケーラビリティの詳細は、「同時実行性およびスケーラビリティについて」を参照してください。

例8-1は、バインド変数のない問合せとバインド変数のある意味的に同等の問合せのパフォーマンスの違いを示しています。前者は、速度が遅く、より多くのラッチが使用されます(ラッチがスケーラビリティに与える影響の詳細は、「ラッチおよび同時実行性について」を参照)。パフォーマンス統計を収集し表示するために、この例では、Runstatsツール(「Runstatsによるプログラミング手法の比較」で説明)が使用されています。


注意:

  • 例8-1は、単一のユーザーのパフォーマンス・コストを示しています。ユーザーが追加されると、コストは急速に増大します。

  • 例8-1の結果は、この設定で作成されました。

    SET SERVEROUTPUT ON FORMAT TRUNCATED
    

例8-1 パフォーマンスを向上させるバインド変数

CREATE TABLE t ( x VARCHAR2(5) );
 
DECLARE
  TYPE rc IS REF CURSOR;
  l_cursor rc;
BEGIN
  runstats_pkg.rs_start;  -- Collect statistics for query without bind variable
 
  FOR i IN 1 .. 5000 LOOP
    OPEN l_cursor FOR 'SELECT x FROM t WHERE x = ' || TO_CHAR(i);
    CLOSE l_cursor;
  END LOOP;
 
  runstats_pkg.rs_middle;  -- Collect statistics for query with bind variable
 
  FOR i IN 1 .. 5000 LOOP
    OPEN l_cursor FOR 'SELECT x FROM t WHERE x = :x' USING i;
    CLOSE l_cursor;
  END LOOP;
 
  runstats_pkg.rs_stop(500);  -- Stop collecting statistics
end;
/

結果は次のようになります。

Run 1 ran in 740 hsec
Run 2 ran in 30 hsec
Run 1 ran in 2466.67% of the time of run 2

Name                                   Run 1         Run 2    Difference
STAT...recursive cpu usage               729            19          -710
STAT...CPU used by this sessio           742            30          -712
STAT...parse time elapsed              1,051             4        -1,047
STAT...parse time cpu                  1,066             2        -1,064
STAT...session cursor cache hi             1         4,998         4,997
STAT...table scans (short tabl         5,000             1        -4,999
STAT...parse count (total)            10,003         5,004        -4,999
LATCH.session idle bit                 5,003             3        -5,000
LATCH.session allocation               5,003             3        -5,000
STAT...execute count                  10,003         5,003        -5,000
STAT...opened cursors cumulati        10,003         5,003        -5,000
STAT...parse count (hard)             10,001             5        -9,996
STAT...CCursor + sql area evic        10,000             1        -9,999
STAT...enqueue releases               10,008             7       -10,001
STAT...enqueue requests               10,009             7       -10,002
STAT...calls to get snapshot s        20,005         5,006       -14,999
STAT...calls to kcmgcs                20,028            35       -19,993
STAT...consistent gets pin (fa        20,013            17       -19,996
LATCH.call allocation                 20,002             6       -19,996
STAT...consistent gets from ca        20,014            18       -19,996
STAT...consistent gets                20,014            18       -19,996
STAT...consistent gets pin            20,013            17       -19,996
LATCH.simulator hash latch            20,014            11       -20,003
STAT...session logical reads          20,080            75       -20,005
LATCH.shared pool simulator           20,046             5       -20,041
LATCH.enqueue hash chains             20,343            15       -20,328
STAT...recursive calls                40,015        15,018       -24,997
LATCH.cache buffers chains            40,480           294       -40,186
STAT...session pga memory max        131,072        65,536       -65,536
STAT...session pga memory            131,072        65,536       -65,536
LATCH.row cache objects              165,209           139      -165,070
STAT...session uga memory max        219,000             0      -219,000
LATCH.shared pool                    265,108           152      -264,956
STAT...logical read bytes from   164,495,360       614,400  -163,880,960

Run 1 latches total compared to run 2 -- difference and percentage
         Run 1         Run 2          Diff       Pct
       562,092           864      -561,228  2,466.67%
 
PL/SQL procedure successfully completed.

注意:

  • 文字列リテラルではなくバインド引数を使用すると、SQLインジェクション攻撃に強いコードを最も効率よく作成できます。詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

  • バインド引数は、データ・ウェアハウス・システムの効率性を損なう場合があります。ほとんどの問合せは非常に多くの時間を要するため、オプティマイザは、最適な汎用問合せではなく、問合せごとに最適なプランの作成を試みます。バインド引数を使用した場合、オプティマイザにより最適な汎用問合せが強制的に作成されることがあります。データ・ウェアハウス・システムのパフォーマンスの向上の詳細は、『Oracle Databaseデータ・ウェアハウス・ガイド』を参照してください。


ソフト解析はハード解析よりも効率的ですが、文を何度もソフト解析するコストは依然として非常に高くなります。アプリケーションの効率性およびスケーラビリティを最大化するには、解析を最小化してください。解析を最小化する最も簡単な方法は、PL/SQLの使用です。

PL/SQLを使用したスケーラビリティの向上

この項の内容は次のとおりです。

PL/SQLによる解析の最小化の方法

データベース・アクセスに最適化されたPL/SQLは、文を暗黙的にキャッシュします。PL/SQLでは、カーソルを閉じた場合、カーソルはパースペクティブから閉じられますが(つまり、開いているカーソルが必要な場合にそのカーソルを使用できない)、実際にはPL/SQLはカーソルを開いたままにし、その文をキャッシュします。キャッシュされた文を再度使用する場合、PL/SQLは同じカーソルを使用することで、解析を回避します。(必要に応じてPL/SQLはキャッシュされた文を閉じます。たとえば、プログラムが他のカーソルを開く必要があるが、開くとOPEN_CURSORSのinit.ora設定を超えるような場合です。)

PL/SQLは、実行時に変化しないSQL文のみをキャッシュできます。

EXECUTE IMMEDIATE文について

EXECUTE IMMEDIATE文の基本構文は次のとおりです。

EXECUTE IMMEDIATE sql_statement

sql_statementは、SQL文を表す文字列です。sql_statementEXECUTE IMMEDIATE文の実行ごとに同じ値を持つ場合、PL/SQLはEXECUTE IMMEDIATE文をキャッシュできます。sql_statementEXECUTE IMMEDIATE文の実行ごとに異なる可能性がある場合、PL/SQLはEXECUTE IMMEDIATE文をキャッシュできません。


参照:


OPEN FOR文について

OPEN FOR文の基本構文は次のとおりです。

OPEN cursor_variable FOR query

アプリケーションは、cursor_variableを閉じる前に様々な問合せに対して開くことができます。PL/SQLは、実行時まで様々な問合せの数を特定できないため、PL/SQLはOPEN FOR文をキャッシュできません。

カーソル変数を使用する必要がない場合、パフォーマンスおよびプログラミングの容易性を向上するために、宣言されたカーソルを使用してください。詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。


参照:


DBMS_SQLパッケージについて

DBMS_SQLパッケージは、動的SQL文を作成、実行および説明するためのAPIです。DBMS_SQLパッケージの使用はEXECUTE IMMEDIATE文の使用よりも手間がかかりますが、PL/SQLコンパイラがコンパイル時に出力ホスト変数(選択リスト項目)または入力バインド変数の数値またはタイプを決定できない場合にDBMS_SQLパッケージを使用する必要があります。


参照:

  • DBMS_SQLパッケージをどのようなときに使用するかの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

  • DBMS_SQLパッケージの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。

  • 「EXECUTE IMMEDIATE文について」


バルクSQLについて

バルクSQLは、PL/SQLとSQLの間のラウンド・トリップ数を削減し、これによりリソースの使用が減少します。バルクSQLを使用しない場合、データベースから1度に1行取得し(SQL)、処理して(PL/SQL)、データベースに返します(SQL)。バルクSQLを使用する場合、データベースから行セットを取得し、行セットを処理して、データベースにセット全体を返します。

データベースから複数の行を取得しデータベースに返す場合は、例8-2に示すように、バルクSQLを使用することをお薦めします。次の例に示すように、複数の行を取得するが、それらを返す必要がない場合は、バルクSQLは必要ありません。

FOR x IN (SELECT * FROM t WHERE ... )  -- Retrieve row set (implicit array fetch)
  LOOP
    DBMS_OUTPUT.PUT_LINE(t.x);          -- Process rows but do not return them
  END LOOP;

例8-2は、表t、列object_nameをループし、100行のセットを取得し、処理して、データベースに返します。(バルクFETCH文を100行に制限する場合、明示的なカーソルが必要です。)

例8-2 バルクSQL

CREATE OR REPLACE PROCEDURE bulk AS
  TYPE ridArray IS TABLE OF ROWID;
  TYPE onameArray IS TABLE OF t.object_name%TYPE;
 
  CURSOR c is SELECT ROWID rid, object_name  -- explicit cursor
              FROM t t_bulk;
 
  l_rids    ridArray;
  l_onames  onameArray;
  N         NUMBER := 100;
BEGIN
  OPEN c;
  LOOP
    FETCH c BULK COLLECT
    INTO l_rids, l_onames LIMIT N;   -- retrieve N rows from t

    FOR i in 1 .. l_rids.COUNT
      LOOP                           -- process N rows
        l_onames(i) := substr(l_onames(i),2) || substr(l_onames(i),1,1);
      END LOOP;

      FORALL i in 1 .. l_rids.count  -- return processed rows to t
        UPDATE t
        SET object_name = l_onames(i)
        WHERE ROWID = l_rids(i);
        EXIT WHEN c%NOTFOUND;
  END LOOP;
  CLOSE c;
END;
/

例8-3は、バルクSQLを使用しないで例8-2と同じジョブを実行します。

例8-3 バルクSQLを使用しない場合

CREATE OR REPLACE PROCEDURE slow_by_slow AS
BEGIN
  FOR x IN (SELECT rowid rid, object_name FROM t t_slow_by_slow)
    LOOP
      x.object_name := substr(x.object_name,2) || substr(x.object_name,1,1);

      UPDATE t
      SET object_name = x.object_name
      WHERE rowid = x.rid;
    END LOOP;
END;

例8-2および例8-3TKPROFレポートが示すとおり、このジョブでバルクSQLを使用すると、CPU時間が約50%減ります。

SELECT ROWID RID, OBJECT_NAME FROM T T_BULK
 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      721      0.17       0.17          0      22582          0       71825
********************************************************************************
UPDATE T SET OBJECT_NAME = :B1 WHERE ROWID = :B2
 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute    719     12.83      13.77          0      71853      74185       71825
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      720     12.83      13.77          0      71853      74185       71825


SELECT ROWID RID, OBJECT_NAME FROM T T_SLOW_BY_SLOW
 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total      721      0.17       0.17          0      22582          0       71825
********************************************************************************
UPDATE T SET OBJECT_NAME = :B2 WHERE ROWID = :B1
 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute  71824     21.25      22.25          0      71836      73950       71824
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total    71825     21.25      22.25          0      71836      73950       71824

ただし、このジョブのバルクSQLの使用は、TKPROFレポートに示すとおり、単一のSQL文の使用よりもCPU時間およびコードを多く使用します。

UPDATE T SET OBJECT_NAME = SUBSTR(OBJECT_NAME,2) || SUBSTR(OBJECT_NAME,1,1)
 
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      1.30       1.44          0       2166      75736       71825
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        2      1.30       1.44          0       2166      75736       71825

参照:

  • 一括SQLの概要は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。

  • 一括SQLを使用する場合のさらに固有の情報は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。

  • バルクSQLの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。


同時実行性およびスケーラビリティについて

アプリケーションの処理の同時実行性が高くなれば、拡張性も高くなります。

同時実行性とは、複数のトランザクションを同時に実行することです。同時実行されるトランザクション内の文は、同じデータを更新できます。同時実行されるトランザクションは、意味のある一貫した結果を作成する必要があります。このため、マルチユーザー・データベースには次の機能が備わっている必要があります。

  • 複数のユーザーの同時データ・アクセスを可能にするデータの同時実行性

  • ユーザー自身のトランザクションおよび他のユーザーのコミット・トランザクションによる見える変更を含む、一貫したデータ・ビューを各ユーザーに表示する、データの一貫性

Oracle Databaseは、複数バージョン一貫性モデル、様々なロック・タイプ、およびトランザクション分離レベルを使用して、データの一貫性を維持します。Oracle Databaseのロック・メカニズムの概要は、『Oracle Database概要』を参照してください。Oracle Databaseのトランザクション分離レベルの概要は、『Oracle Database概要』を参照してください。

トランザクションが同時に実行されるときに一貫したトランザクション動作を表すために、データベース調査者は、シリアライズ可能と呼ばれるトランザクション分離カテゴリを定義しています。シリアライズ可能トランザクションは、単一ユーザー・データベースである環境で動作します。シリアライズ可能トランザクションは特定の場合に望ましいですが、作業負荷の99%の場合、読取りコミット済分離で十分です。

Oracle Databaseには、同時実行性およびスケーラビリティを向上させる機能(順序、ラッチ、非ブロック読取り/書込み、および共有SQLなど)があります。

この項の内容は次のとおりです。


参照:

データの同時実行性および一貫性の詳細は、『Oracle Database概要』を参照してください。

順序および同時実行性について

順序は、複数のユーザーが一意の整数を生成する際に使用するスキーマ・オブジェクトであり、一意の主キーが必要な場合に大変便利です。

順序を使用しない場合、一意の主キー値はプログラム的に作成する必要があります。ユーザーは、最近作成された値を選択し、増分することで、新しい主キー値を取得します。この手法はトランザクション中にロックが必要で、複数のユーザーが次の主キー値を待機します(つまり、トランザクションのシリアル化)。順序を使用すると、シリアライズ化が不要になり、アプリケーションの同時実行性およびスケーラビリティが向上します。


参照:


ラッチおよび同時実行性について

ラッチは、共有データ構造へのマルチユーザー・アクセスを調整する、単純な低レベルのシリアライズ化メカニズムです。ラッチによって、複数のプロセスからアクセスされる共有メモリー・リソースが破損しないように保護されます。

ラッチを増やすと、同時実行性に基づく待機が増加するため、スケーラビリティが低下します。開発中に少し速く実行するアプローチか、より少ないラッチを使用するアプローチのいずれかを使用できる場合は、後者を使用してください。


参照:

  • ラッチの詳細は、『Oracle Database概要』を参照してください。

  • 単一オブジェクトに対するラッチに類似するmutexの詳細は、『Oracle Database概要』を参照してください。


非ブロック読取り/書込みおよび同時実行性について

Oracle Databaseでは、非ブロック読取り/書込みを使用すると、問合せを同時に実行し、ブロックまたは停止することなく読取り中のデータを変更できます。非ブロック読取り/書込みでは、あるセッションがデータの変更中に、別のセッションがそのデータを読み取ることができます。

共有SQLおよび同時実行性について

Oracle DatabaseがSQL文を実行可能なオブジェクトにコンパイルすると、そのオブジェクトが存在するかぎりそのオブジェクトを他のセッションが再利用できるようになります。共有SQLと呼ばれるこのOracle Database機能を使用すると、リソースに大きな負担をかける操作であるSQL文のコンパイルおよび最適化を、同じSQL文がセッションで使用されるたびに行うのではなく、1度実行するだけで済みます。


参照:

共有SQLの詳細は、『Oracle Database概要』を参照してください。

同時セッション数の制限

同時セッション数が増えると、同時実行に基づく待機が増加し、レスポンス時間が遅くなります。

使用しているコンピュータにn CPUコアがある場合、実際には最高でnセッションが同時にアクティブになります。追加の同時セッションがそれぞれアクティブになるには、CPUコアが使用可能になるまで待機する必要があります。一部の待機セッションがI/Oのみを待機している場合、同時セッション数がnをわずかに超えて増加すると、実行時のパフォーマンスがわずかに向上する場合があります。ただし、同時セッション数が大幅に増加すると、実行時パフォーマンスが著しく低下します。

SESSIONS初期化パラメータは、システム内の同時ユーザーの最大数を決定します。詳細は、『Oracle Databaseリファレンス』を参照してください。


参照:

12 CPUコアのコンピュータで同時セッション数を数千から96に減らした場合の効果の動画については、http://www.youtube.com/watch?v=xNDnVOCdvQ0を参照してください。

Runstatsによるプログラミング手法の比較

この項の内容は次のとおりです。

Runstatsについて

Runstatsツールを使用すると、2つのプログラミング手法のパフォーマンスを比較して、いずれの手法が優れているかを確認できます。

Runstatsでは、次を測定できます。

  • 1/100秒(hsec)単位での各手法の経過時間

  • 1つ目の手法の経過時間を2つ目の手法の経過時間に対するパーセントで表した値

  • 2つの手法のシステム統計(解析コールなど)

  • 2つの手法のラッチ

先行する計測の中で最も重要なことはラッチです(「ラッチおよび同時実行性について」を参照)。


参照:

Runstatsの使用例である例8-1

Runstatsの設定

この項では、ビューおよび一時表を使用するパッケージとして実装するRunstatsツールの設定方法を説明します。


注意:

次の手順のステップ1では、動的パフォーマンス・ビューV$STATNAMEV$MYSTATおよびV$LATCHSELECT権限が必要です。この権限を取得できない場合、この権限を持つ別のユーザーにステップ1のビューの作成、およびそのビューに対するSELECT権限の付与を依頼してください。

Runstatsツールを設定する手順:

  1. Runstatsで使用されるビューを作成します。

    CREATE OR REPLACE VIEW stats
    AS SELECT 'STAT...' || a.name name, b.value
    FROM V$STATNAME a, V$MYSTAT b
    WHERE a.statistic# = b.statistic#
    UNION ALL
    SELECT 'LATCH.' || name, gets
    FROM V$LATCH;
    
  2. Runstatsで使用される一時表を作成します。

    DROP TABLE run_stats;
    
    CREATE GLOBAL TEMPORARY TABLE run_stats
    ( runid VARCHAR2(15),
      name VARCHAR2(80),
      value INT )
    ON COMMIT PRESERVE ROWS;
    
  3. このパッケージ仕様を作成します。

    CREATE OR REPLACE PACKAGE runstats_pkg
    AS
      PROCEDURE rs_start;
      PROCEDURE rs_middle;
      PROCEDURE rs_stop( p_difference_threshold IN NUMBER DEFAULT 0 );
    end;
    /
    

    パラメータp_difference_thresholdは、Runstatsによって表示される統計の量およびラッチ・データを制御します。Runstatsは、2つの手法の差異がp_difference_thresholdよりも大きい場合のみデータを表示します。デフォルトでは、Runstatsはすべてのデータを表示します。

  4. このパッケージ本体を作成します。

    CREATE OR REPLACE PACKAGE BODY runstats_pkg
    AS
      g_start NUMBER;
      g_run1 NUMBER;
      g_run2 NUMBER;
     
      PROCEDURE rs_start
      IS
      BEGIN
        DELETE FROM run_stats;
     
        INSERT INTO run_stats
        SELECT 'before', stats.* FROM stats;
     
        g_start := DBMS_UTILITY.GET_TIME;
      END rs_start;
     
      PROCEDURE rs_middle
      IS
      BEGIN
        g_run1 := (DBMS_UTILITY.GET_TIME - g_start);
     
        INSERT INTO run_stats
        SELECT 'after 1', stats.* FROM stats;
     
        g_start := DBMS_UTILITY.GET_TIME; 
      END rs_middle;
     
      PROCEDURE rs_stop( p_difference_threshold IN NUMBER DEFAULT 0 )
      IS
      BEGIN
        g_run2 := (DBMS_UTILITY.GET_TIME - g_start);
     
        DBMS_OUTPUT.PUT_LINE 
          ('Run 1 ran in ' || g_run1 || ' hsec');
     
        DBMS_OUTPUT.PUT_LINE 
          ('Run 2 ran in ' || g_run2 || ' hsec');
     
        DBMS_OUTPUT.PUT_LINE 
          ('Run 1 ran in ' || round(g_run1/g_run2*100, 2) || '% of the time of run 2');
     
        DBMS_OUTPUT.PUT_LINE( CHR(9) );
     
        INSERT INTO run_stats
        SELECT 'after 2', stats.* FROM stats;
     
        DBMS_OUTPUT.PUT_LINE 
          ( RPAD( 'Name', 30 ) ||
            LPAD( 'Run 1', 14) ||
            LPAD( 'Run 2', 14) ||
            LPAD( 'Difference', 14)
          );
     
        FOR x IN
        ( SELECT RPAD( a.name, 30 ) ||
                 TO_CHAR( b.value - a.value, '9,999,999,999' ) ||
                 TO_CHAR( c.value - b.value, '9,999,999,999' ) ||
                 TO_CHAR( ( (c.value - b.value) - (b.value - a.value)),
                   '9,999,999,999' ) data
          FROM run_stats a, run_stats b, run_stats c
          WHERE a.name = b.name
            AND b.name = c.name
            AND a.runid = 'before'
            AND b.runid = 'after 1'
            AND c.runid = 'after 2'
            AND (c.value - a.value) > 0
            AND abs((c.value - b.value) - (b.value - a.value)) >
              p_difference_threshold
         ORDER BY ABS((c.value - b.value) - (b.value - a.value))
        ) LOOP
            DBMS_OUTPUT.PUT_LINE( x.data ); 
        END LOOP;
     
        DBMS_OUTPUT.PUT_LINE( CHR(9) );
     
        DBMS_OUTPUT.PUT_LINE(
          'Run 1 latches total compared to run 2 -- difference and percentage' );
     
        DBMS_OUTPUT.PUT_LINE 
          ( LPAD( 'Run 1', 14) ||
            LPAD( 'Run 2', 14) ||
            LPAD( 'Diff', 14) ||
            LPAD( 'Pct', 10)
          );
     
        FOR x IN
        ( SELECT TO_CHAR( run1, '9,999,999,999' ) ||
                 TO_CHAR( run2, '9,999,999,999' ) ||
                 TO_CHAR( diff, '9,999,999,999' ) ||
                 TO_CHAR( ROUND( g_run1/g_run2*100, 2), '99,999.99' ) || '%' data
          FROM ( SELECT SUM (b.value - a.value) run1,
                        SUM (c.value - b.value) run2,
                        SUM ( (c.value - b.value) - (b.value - a.value)) diff
                 FROM run_stats a, run_stats b, run_stats c
                 WHERE a.name = b.name
                   AND b.name = c.name
                   AND a.runid = 'before'
                   AND b.runid = 'after 1'
                   AND c.runid = 'after 2'
                   AND a.name like 'LATCH%'
               )
        ) LOOP
            DBMS_OUTPUT.PUT_LINE( x.data ); 
        END LOOP;
     
      END rs_stop;
     
    END;
    /
    

参照:


Runstatsの使用

Runstatsを使用して2つのプログラミング手法を比較するには、次の構文を使用してrunstats_pkgプロシージャを匿名ブロックから起動します。

[ DECLARE local_declarations ]
BEGIN
  runstats_pkg.rs_start;
  code_for_first_technique
  runstats_pkg.rs_middle;
  code_for_second_technique
  runstats_pkg.rs_stop(n);
END;
/

参照:

Runstatsの使用例である例8-1

推奨されるプログラミング・プラクティス

この項の内容は次のとおりです。

インスツルメンテーション・パッケージの使用

Oracle Databaseにはインスツルメンテーション・パッケージが用意されており、このパッケージに含まれるサブプログラムを使用して、必要なときにいつでもアプリケーション・トレース情報を生成できます。このトレース情報を使用すると、デバッガを使用せずにアプリケーションをデバッグでき不正なコードを特定できます。インスツルメンテーションは、アプリケーションに多数の機能を提供しているため、オーバーヘッドではありません。オーバーヘッドは、多くの利点を失わずに削除できる部分です。

Oracle Databaseが提供しているインスツルメンテーション・パッケージの一部を次に示します。

  • DBMS_APPLICATION_INFO: システム管理者は、アプリケーションのパフォーマンスをモジュール別に追跡できます。

    DBMS_APPLICATION_INFOの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。

  • DBMS_SESSION: アプリケーションが、セッション情報にアクセスし、プリファレンスとセキュリティ・レベルを設定できるようにします。

    DBMS_SESSIONの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。

  • UTL_FILE: アプリケーションが、オペレーティング・システムのテキスト・ファイルを読取りおよび書込みできるようにします。

    UTL_FILEの詳細は、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。


参照:

Oracle Databaseに含まれるPL/SQLパッケージの概要は『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』を参照してください。

統計の収集およびアプリケーション・トレース

データベース統計ではデータベースの負荷のタイプ、およびデータベースで使用する内部および外部リソースに関する情報が提供されます。ADDMを使用したデータベースでパフォーマンスの問題を正確に診断するには、統計が使用可能である必要があります。統計の収集の詳細は、『Oracle Database 2日でパフォーマンス・チューニング・ガイド』を参照してください。


注意:

Oracle Enterprise Managerを使用できない場合、『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』の記載に従い、DBMS_MONITORサブプログラムを使用して統計を収集できます。

Oracle Databaseには、Oracle Databaseアプリケーションの監視および分析に使用できるトレース・ツールがいくつか用意されています。詳細は、『Oracle Database SQLチューニング・ガイド』を参照してください。

既存機能の使用

アプリケーションを開発するときは、プログラミング言語、オペレーティング・システム、Oracle Database、およびOracle Databaseで提供されているPL/SQLパッケージおよびタイプの既存の機能を可能なかぎり使用してください。既存の機能を使用しているアプリケーションは、そうではないアプリケーションよりも開発およびメンテナンスが簡単であり、また実行速度も速くなります。

多くの開発者が開発に再利用している既存の機能は次のとおりです。

  • 制約

    制約についての概要は、「表でのデータ整合性の保証」を参照してください。

  • SQLファンクション(SQLに組み込まれているファンクション)

    SQLファンクションの詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

  • 順序(一意の連続的な値の生成が可能)

    「順序の作成および管理」を参照してください。

  • 監査(選択したユーザーのデータベース・アクションの監視および記録)

    監査の概要は、『Oracle Database 2日でセキュリティ・ガイド』を参照してください。

  • レプリケーション(分散データベース・システムを構成する複数のデータベースで、表などのデータベース・オブジェクトをコピーし、メンテナンスするプロセス)

    レプリケーションの概要は、「Oracle Databaseアドバンスト・レプリケーション」を参照してください。

  • メッセージ・キューイング(Webベースのビジネス・アプリケーション間の通信方法)

    Oracle Databaseアドバンスト・キューイング(AQ)の概要は、『Oracle Streamsアドバンスト・キューイング・ユーザーズ・ガイド』を参照してください。

  • レコード変更履歴のメンテナンス

    ワークスペース・マネージャの概要は、『Oracle Database Workspace Manager開発者ガイド』を参照してください。

例8-4では、2つの同時トランザクションが、表に格納されたメッセージをデキューします(つまり、各トランザクションが、表の次の未処理行を検出しロックします)。(『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』に記載されているように)DBMS_AQ.DEQUEUEプロシージャを単純に起動するのではなく、この例では、表のファンクション索引が作成され、次にそのファンクションが各トランザクションで使用され、行が取得されてメッセージが表示されます。

例8-4 同時デキュー・トランザクション

表の作成:

DROP TABLE t;
CREATE TABLE t
  ( id             NUMBER PRIMARY KEY,
    processed_flag VARCHAR2(1),
    payload        VARCHAR2(20)
  );

表の索引の作成:

CREATE INDEX t_idx ON
  t( DECODE( processed_flag, 'N', 'N' ) );

表への移入:

INSERT INTO t
  SELECT r,
         CASE WHEN MOD(r,2) = 0 THEN 'N' ELSE 'Y' END,
         'payload ' || r
  FROM (SELECT LEVEL r FROM DUAL CONNECT BY LEVEL <= 5);

表の表示:

SELECT * FROM t;

結果:

        ID P PAYLOAD
---------- - --------------------
         1 Y payload 1
         2 N payload 2
         3 Y payload 3
         4 N payload 4
         5 Y payload 5
 
5 rows selected.

1つ目のトランザクション:

DECLARE
  l_rec t%ROWTYPE;
  CURSOR c IS
    SELECT *
    FROM t
    WHERE DECODE(processed_flag,'N','N') = 'N'
    FOR UPDATE
    SKIP LOCKED;
BEGIN
  OPEN c;
 
  FETCH c INTO l_rec;
 
  IF ( c%FOUND ) THEN
    DBMS_OUTPUT.PUT_LINE( 'Got row ' || l_rec.id || ', ' || l_rec.payload );
  END IF;
 
  CLOSE c;
END;
/

結果:

Got row 2, payload 2

同時トランザクション:

DECLARE
  PRAGMA AUTONOMOUS_TRANSACTION;
  l_rec t%ROWTYPE;
  CURSOR c IS
    SELECT *
    FROM t
    WHERE DECODE(processed_flag,'N','N') = 'N'
    FOR UPDATE
    SKIP LOCKED;
BEGIN
  OPEN c;
 
  FETCH c INTO l_rec;
 
  IF ( c%FOUND ) THEN
    DBMS_OUTPUT.PUT_LINE( 'Got row ' || l_rec.id || ', ' || l_rec.payload );
  END IF;
 
  CLOSE c;
  COMMIT;
END;
/

結果:

Got row 4, payload 4

例8-4のコードは、制限されたDBMS_AQ.DEQUEUE呼出しのような機能を実装します。既存の機能(この場合、ファンクション・ベース索引)を使用して節約された開発時間が大きくなる場合があります。


参照:

  • 『Oracle Database PL/SQLパッケージおよびタイプ・リファレンス』

  • (各リリースの)『Oracle Database新機能ガイド』

  • (各リリースの)『Oracle Database概要』


エディショニング・ビューによるデータベース表のカバー

アプリケーションでデータベース表が使用されている場合、停止時間を最小限にする、または発生させないように、エディションベース再定義(EBR)を使用して、使用中のアプリケーションのデータベース・コンポーネントをアップグレードできるように各表をエディショニング・ビューでカバーします。EBRの詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。エディショニング・ビューの詳細は、『Oracle Databaseアドバンスト・アプリケーション開発者ガイド』を参照してください。

推奨されるセキュリティ・プラクティス

アプリケーションを構成するスキーマ・オブジェクトの権限を付与する際に、最小限の権限のみを付与するという原則に従ってください。つまり、ユーザーには、必要な権限のみを付与してください。最小限の権限のみを付与する原則の詳細は、『Oracle Database 2日でセキュリティ・ガイド』を参照してください。


参照:

コードに対するSQLインジェクション攻撃に最も効果的な方法である、文字列リテラルのかわりにバインド引数を使用する方法の詳細は、「バインド引数を使用したスケーラビリティの向上」を参照してください。