プライマリ・コンテンツに移動
Oracle® Database開発ガイド
12c リリース1 (12.1)
B71295-06
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次
索引へ移動
索引

前
 
次
 

6 アプリケーション開発者用のSQL処理

この章では、Oracle DatabaseでのSQL文の処理方法について、アプリケーション開発者が知っておく必要がある内容について説明します。この章を読む前に、『Oracle Database概要』でSQL文およびSQL処理の基本情報を確認してください。

内容は次のとおりです。

6.1 SQL文の処理の説明

ここでは、SQL文の実行時に処理の各ステージで何が行われるかを、例としてデータ操作言語(DML)文を使用して説明します。

ある部門の全従業員の昇給を行うためにPro*Cプログラムを使用しているとします。プログラムはOracle Databaseに接続しており、操作を行うユーザーはemployees表を所有するHRスキーマに接続しています。プログラムには次のSQL文を埋め込むことができます。

EXEC SQL UPDATE employees SET salary = 1.10 * salary
  WHERE department_id = :department_id;

プログラムでは、SQL文の実行時に使用するバインド変数プレースホルダ:department_idの値が提供されます。

内容は次のとおりです。

6.1.1 SQL文の処理のステージ


注意:

DML文はすべてのステージを使用します。トランザクション管理、セッション管理およびシステム管理のSQL文が使用するのは、ステージ2および8のみです。

  1. カーソルをオープンまたは作成します。

    プログラム・インタフェース・コールにより、SQL文を予想して、カーソルがオープンまたは作成されます。ほとんどのアプリケーションではカーソルの作成は暗黙的(自動的)に行われます。プリコンパイラ・プログラムでは、カーソルを暗黙的または明示的に作成します。

  2. 文を解析します。

    SQL文がユーザー・プロセスからOracle Databaseに渡され、文の解析済表現が共有SQL領域にロードされます。Oracle Databaseは、解析中に多くのエラーを捕捉できます。


    注意:

    データ定義言語(DDL)文では、解析にデータ・ディクショナリの参照と実行が含まれます。


    参照:


  3. 文が問合せかどうかを判別します。

  4. 文が問合せの場合、結果を記述します。


    注意:

    このステージが必要になるのは、結果の性質が不明な場合のみです。たとえば、ユーザーが対話的に問合せを入力する場合などです。

    Oracle Databaseによって結果の性質(データ型、長さおよび名前)が決まります。

  5. 文が問合せの場合、出力を定義します。

    フェッチされる各値を受け取るために定義する変数の場所、サイズおよびデータ型を指定します。このような変数は定義変数と呼ばれます。必要な場合にはOracle Databaseによってデータ型の変換が行われます。


    参照:

    DEFINEステージの詳細は、『Oracle Database概要』を参照してください。

  6. 変数がある場合はバインドします。

    Oracle DatabaseはSQL文の意味を判別していますが、文を実行するために十分な情報は得ていません。Oracle Databaseでは、文に指定されているすべてのバインド変数プレースホルダの値が必要です。例では、:department_idの値が必要です。これらの値を獲得するプロセスは変数のバインドと呼ばれます。

    値の場所(メモリー・アドレス)をプログラムで指定する必要があります。アプリケーションのエンドユーザーは、Oracle Databaseユーティリティから値の入力を求められることがあるため、バインド変数プレースホルダの値を指定していると認識していない場合もあります。

    値の場所(つまり参照によるバインド)がプログラムによって指定されるため、値が変更された場合でも、文の再実行の前に変数を再バインドする必要はありません。Oracle Databaseは文の実行のたびにそのアドレスから変数の値を取得します。

    Oracle Databaseでデータ型変換を実行する必要がある場合は、デフォルトでまたは暗黙的に指定する場合を除き、各値のデータ型と長さも指定する必要があります。


    参照:

    値のデータ型と長さの指定の詳細は、次の資料を参照してください。
    • 『Oracle Call Interfaceプログラマーズ・ガイド』

    • 『Pro*C/C++プログラマーズ・ガイド』


  7. (オプション)文をパラレル化します。

    Oracle Databaseでは、問合せと一部のデータ定義言語(DDL)操作(索引作成、副問合せによる表作成、パーティションに対する操作など)をパラレル化することができます。パラレル化では、処理が早く完了するように、複数のサーバー・プロセスでSQL文の処理を実行します。

  8. 文を実行します。

    Oracle Databaseは文を実行します。文が問合せまたはINSERT文の場合、データは変更されないためデータベースは行をロックする必要はありません。ただし、文がUPDATE文またはDELETE文の場合は、それにより影響を受けるすべての行を、そのトランザクションの次のCOMMITROLLBACKまたはSAVEPOINTまで、データベースがロックします。これによりデータの整合性が確保されます。

    一部の文では実行が複数回行われるように指定できます。これは配列処理と呼ばれます。実行回数をnと指定すると、バインドおよび定義の場所が、サイズnの配列の先頭であるとみなされます。

  9. 文が問合せの場合、行をフェッチします。

    Oracle Databaseは行を選択し、問合せにORDER BY句が含まれる場合は、行の順序付けを行います。後続の各フェッチにより結果セットの行が1行ずつ取得され、最後の行がフェッチされるまで続きます。

  10. カーソルをクローズします。

    Oracle Databaseはカーソルをクローズします。


注意:

トランザクション管理、セッション管理またはシステム管理SQL文を再実行するには、別のEXECUTE文を使用します。

6.1.2 共有SQL領域

Oracle Databaseは、アプリケーションが類似したSQL文をデータベースに送信すると自動的に検出します。その文の最初の処理時に使用されたSQL領域が共有されます。つまり、後で出現する同じ文の処理に使用されます。このため、一意の文に対して存在する共有SQL領域は1つのみです。共有SQL領域は共有メモリー領域であるため、すべてのOracle Databaseプロセスが共有SQL領域を使用できます。SQL領域の共有により、データベース・サーバーでのメモリー使用量が減るため、システム・スループットが向上します。

Oracle Databaseでは、文が類似しているか同一かを決定する際に、ユーザーやアプリケーションによって直接発行されたSQL文と、DDL文によって内部的に発行された再帰的SQL文の両方が比較されます。


参照:

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

  • 共有SQLの詳細は、Oracle Database SQLチューニング・ガイドを参照してください


6.2 操作のトランザクションへのグループ化

内容は次のとおりです。


参照:

トランザクションの基本情報は、『Oracle Database概要』を参照してください。

6.2.1 トランザクションへの操作のグループ化方法の決定

一般に、トランザクションへの操作のグループ化方法の決定は、Oracle Databaseのプログラム・インタフェースを使用するアプリケーション開発者が考慮します。トランザクションのグループ化方法を決定する際は、次の規則に従います。

  • 論理単位ごとに作業が完成し、データの一貫性が保たれるように、トランザクションを定義します。

  • すべての参照表の中のデータは、トランザクションが開始する前および終了した後で、一貫した状態であることを確認します。

  • 各トランザクションは、データに対する一貫した変更を1つ含むSQL文またはPL/SQLブロックのみで構成されていることを確認します。

たとえば、口座間で預金を移動できるWebアプリケーションを作成すると仮定します。このトランザクションには、一方の口座の借方への記帳(1つのSQL文で実行)ともう一方の口座の貸方への記帳(もう1つのSQL文で実行)が含まれる必要があります。2つの文で1つの作業単位となり、両方が成立するか両方が不成立かのいずれかである必要があります。つまり、片方の文はもう片方のコミットなしでコミットすることはできません。一方の口座への預金など、関連のないアクションは、同じトランザクションに含めないでください。

6.2.2 トランザクションのパフォーマンスの改善

アプリケーション開発者は、パフォーマンスの改善を試みる必要があります。アプリケーションの設計および作成では、次のパフォーマンス改善のテクニックを使用することを検討してください。

  • 各トランザクションに対して、次を行います。

    1. 単一のSQL文を使用できる場合、それを使用します。

    2. 単一のSQL文を使用できないが、PL/SQLを使用できる場合、できるだけ小さいPL/SQLを使用します。

      PL/SQLの詳細は、第II部「アプリケーション開発者用のPL/SQL」を参照してください。

    3. (ディレクトリの読取りなど、実行する必要のあることができないため) PL/SQLを使用できない場合、Javaを使用します。

    4. Javaを使用できない場合(非常に低速な場合など)、または既存の第三世代言語(3GL)コードがある場合、外部Cサブプログラムを使用します。

    アプリケーションでのJavaおよびCの使用方法の詳細は、第18章「複数のプログラミング言語を使用したアプリケーションの開発」を参照してください。

  • 共有SQL領域を利用できるように、SQL文の発行基準を確立します。

    Oracle Databaseが同一のSQL文を認識し、SQL文がメモリー領域を共有できるようにします。これによって、データベース・サーバー上のメモリー使用量が減少し、システム・スループットが向上します。

  • 統計を収集します。Oracle Databaseは統計を使用して、SQL文を最適化するコストベースの方法を実装することができます。必要に応じて、オプティマイザへの追加のヒントを使用できます。

    ほとんどの統計の収集にはDBMS_STATSパッケージを使用します。このパッケージでは、パラレルでの統計の収集、パーティション化されたオブジェクトのグローバル統計の収集、その他の方法での統計収集のチューニングを行うことができます。このパッケージの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

    コストベースのオプティマイザに関係しない統計収集(空きリスト・ブロックに関する情報収集など)では、SQL文ANALYZEを使用します。この文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

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

  • トランザクションを開始する前にDBMS_APPLICATION_INFO.SET_ACTIONプロシージャを起動し、トランザクションの名前をデータベースに登録し、後でOracle TraceおよびSQLトレース機能を使用してパフォーマンスをトラッキングするときに使用できるようにします。DBMS_APPLICATION_INFOパッケージの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。

  • ユーザーが作成したPL/SQLファンクションをSQL式に組み込んで、ユーザーの生産性および問合せ効率を向上させます。詳細は、11.9項「SQL文からのストアドPL/SQLファンクションの起動」を参照してください。


参照:

トランザクション管理の詳細は、『Oracle Database概要』を参照してください。

6.2.3 コミットREDO動作の管理

トランザクションがOracle Databaseを更新するとき、これに対応するREDOエントリが生成されます。このREDOエントリは、トランザクションが完了するまで、Oracle DatabaseによりREDOログにバッファリングされます。トランザクションがコミットされると、それに応じてログ・ライター・プロセス(LGWR)により、トランザクション内のすべての変更のバッファリングされたREDOエントリに対し、REDOレコードがディスクに書き込まれます。デフォルトでは、REDOエントリは、コールがクライアントに戻される前に、Oracle Databaseによりディスクに書き込まれます。この動作のためにアプリケーションはREDOエントリがディスクに永続的に書き込まれるのを待機する必要があり、コミットに待機時間が発生します。

Oracle Databaseでは、アプリケーションのニーズに応じてコミットREDOの処理を変更できます。アプリケーションで非常に高いトランザクション・スループットが必要とされるため、コミットでの待機時間を短くするためにコミットの永続性を放棄してもよい場合は、デフォルトのCOMMITオプションを変更して、データベースでデータがオンラインREDOログへ書き込まれるのをアプリケーションが待機しなくて済むようにできます。

表6-1COMMITオプションの一覧です。


注意:

NOWAITオプションを指定した場合、コミット・メッセージの受信後、REDOログ・レコードへの書込み前に障害が発生すると、トランザクションにその変更が永続的であると誤って示す可能性があります。

表6-1 COMMIT文のオプション

オプション 効果

WAIT
(デフォルト)

対応するREDO情報がオンラインREDOログに永続的に書き込まれるまで、COMMIT文が戻されないことを確認します。このCOMMIT文から成功として戻されたコミットをクライアントが受信した場合、そのトランザクションは永続的なメディアにコミットされています。

ログに正常に書き込まれた後に障害が発生すると、成功メッセージがクライアントに戻されない場合があります。その場合、クライアントはトランザクションがコミットされたかどうかを判断できません。

NOWAIT
(WAIT
の代替)

COMMIT文は、REDOログへの書込みが完了したかどうかにかかわらず、クライアントに戻されます。この動作はトランザクションのスループットを向上させます。

BATCH
(IMMEDIATE
の代替)

REDO情報は、同時に実行しているトランザクションとともにREDOログにバッファリングされます。十分なREDO情報が収集されると、REDOログのディスク書込みが開始されます。この動作では、複数のトランザクションのREDO情報が1回のI/O操作でログに書き込まれるため、グループ・コミットと呼ばれます。

IMMEDIATE
(デフォルト)

LGWRがトランザクションのREDO情報をログに書き込みます。この操作オプションではディスクI/Oが発生するため、トランザクションのスループットが低下する場合があります。


COMMITオプションを変更するには、COMMIT文(『Oracle Database SQL言語リファレンス』を参照)、または適切な初期化パラメータを使用します。初期化パラメータの詳細は、『Oracle Databaseリファレンス』を参照してください。


注意:

分散トランザクションの場合、デフォルトのIMMEDIATEおよびWAITアクションは変更できません。

Oracle Call Interface(OCI)を使用するアプリケーションの場合、アプリケーション内でOCITransCommitファンクションに次のフラグを設定することにより、REDO動作を変更できます。

  • OCI_TRANS_WRITEWAIT

  • OCI_TRANS_WRITENOWAIT

  • OCI_TRANS_WRITEBATCH

  • OCI_TRANS_WRITEIMMED


注意:

OCI_TRANS_WRITENOWAITの使用時には、トランザクションが強制終了、強制起動、インスタンス障害またはノード障害で警告なしに消失する可能性があります。Oracle RACシステムでは非同期にコミットした変更は、他のインスタンスですぐに読み込めない場合があります。


参照:

OCITransCommitファンクションの詳細は『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

NOWAITおよびBATCHオプションの指定では、問題が発生しやすい期間が短くなります(このような期間には、アプリケーションがコミット済として認識するトランザクションをOracle Databaseがロールバックできます)。アプリケーションは、次のような状況に対応できる必要があります。

  • データベース・ホストの障害。バッファには格納されていても、まだオンラインREDOログに書き込まれていないREDOエントリが失われる可能性があります。

  • ファイルI/Oの問題で、LGWRによるバッファ済REDOエントリのディスクへの書込みが妨げられる場合。REDOログが多重化されていない場合、コミットが失われます。

6.2.4 リカバリ可能な停止後のトランザクション結果の確認

リカバリ可能な停止は、アプリケーション(クライアント)とOracle Database (サーバー)間の接続を切断する、システム、ハードウェア、通信または記憶域の障害です。停止後、アプリケーションには切断に関するエラー・メッセージが表示されます。接続が切断されたときに実行されていたトランザクションは、進行中のトランザクションで、完了までコミットまたは実行された、またはされていない可能性があります。

停止からリカバリするには、アプリケーションが進行中のトランザクションの結果(コミットされたかどうか、およびセッションの状態が意図どおりに変更されたかどうか)を確認する必要があります。トランザクションがコミットされていなかった場合、アプリケーションはトランザクションを再発行するか、エンドユーザーに未コミット・ステータスを戻すことができます。トランザクションがコミットされていた場合、アプリケーションは切断エラーではなくコミット済ステータスをエンドユーザーに戻すことができます。トランザクションがコミットされていると同時に完了している場合、アプリケーションは新規セッションを使用してセッションの状態を再確立することによって処理を続行できる可能性があります。

進行中のトランザクションの結果をアプリケーションに提供し、トランザクションが重複していないことを確認するために使用できるOracle Database機能は、トランザクション・ガードで、そのApplication Program Interface (API)はPL/SQLプロシージャDBMS_APP_CONT.GET_LTXID_OUTCOMEです。

内容は次のとおりです。

6.2.4.1 トランザクション・ガードの理解

トランザクション・ガードは、停止後に進行中のトランザクションの結果をアプリケーションに提供するために使用できるOracle Databaseツールです。アプリケーションはトランザクション・ガードを使用して、停止後に既知の結果を(コミット済または未コミット)をエンドユーザーに提供できるとともに、必要に応じて、トランザクションがコミットされておらず状態が正しい場合はトランザクションをリプレイすることもできます。

トランザクション・ガードは、6.2.4.2項で説明するように、API、PL/SQLプロシージャDBMS_APP_CONT.GET_LTXID_OUTCOMEを介してトランザクション結果を提供します。

トランザクション・ガードは、失敗したセッション上の最後の進行中のトランザクションを識別するグローバル一意識別子である論理トランザクション識別子(LTXID)に依存します。データベースは、トランザクションがコミットされるとLTXIDを記録し、コミット・メッセージとともに新規LTXIDをクライアントに(クライアントのラウンド・トリップごとに)戻します。クライアント・ドライバは、次回のCOMMIT時に使用するLTXIDを常に保持します。


注意:

  • 通信エラーを実際の結果に置き換えるために、リカバリ可能なエラーが原因で失敗したセッションの結果を検索する場合のみ、トランザクション・ガードを使用してください。

  • トランザクション・ガードは独自のセッションで使用しないでください。

  • トランザクション・ガードは稼働中のセッションで使用しないでください。

    稼働中のトランザクションを停止するには、ローカル・インスタンスまたはリモート・インスタンスでALTER SYSTEM KILL SESSION IMMEDIATEを使用してください。


6.2.4.1.1 トランザクション・ガードがLTXIDを使用する方法

トランザクション・ガードは、LTXIDを次のように使用します。

  • トランザクションの実行中、Oracle Database (サーバー)とアプリケーション(クライアント)の両方が次回のCOMMIT時に使用するLTXIDを保持します。

  • トランザクションがコミットされると、Oracle DatabaseはトランザクションとともにLTXIDを記録します。LTXIDがすでにコミットされているかブロックされている場合、データベースはエラーを発生させ、トランザクションの重複を阻止します。

  • LTXIDは、RETENTION_TIMEOUTパラメータによって指定されている時間にわたってOracle Database内に存在し続けます。デフォルトは24時間です。この値を変更する手順は、次のとおりです。

    • Real Application Clustersを実行している場合、『Oracle Real Application Clusters管理およびデプロイメント・ガイド』で説明されている、サーバー制御ユーティリティ(SRVCTL)を使用します。

    • Real Application Clustersを使用していない場合、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』で説明されている、DBMS_SERVICEパッケージを使用します。

    トランザクションがリモートであるか分散されている場合、LTXIDはローカル・データベース内に永続化されます。

    LTXIDは標準REDO適用時にData GuardおよびActive Data Guardに転送されます。

  • リカバリ可能なエラーの後:

    • トランザクションがコミットされていない場合、Oracle DatabaseはLTXIDをブロックすることにより、同じLTXIDを持つ以前の進行中のトランザクションをコミットできないようにします。

      この動作により、アプリケーションは未コミット結果をユーザーに戻すことができ、ユーザーは実行内容を決定できるようになります。また、アプリケーションは必要に応じて、アプリケーションを安全にリプレイすることもできます。

    • トランザクションがコミットされている場合、アプリケーションはこの結果をエンドユーザーに戻すことができ、状態が正しい場合、アプリケーションは継続できます。

  • トランザクションがロールバックされる場合、Oracle DatabaseはLTXIDを再利用します。


参照:


6.2.4.2 DBMS_APP_CONT.GET_LTXID_OUTCOMEの理解

PL/SQLプロシージャDBMS_APP_CONT.GET_LTXID_OUTCOMEは、トランザクション・ガードのAPIです。停止後、アプリケーションはOracle Databaseに再接続してから、このプロシージャを呼び出して進行中のトランザクションの結果を確認できます。

DBMS_APP_CONT.GET_LTXID_OUTCOMEには、次のパラメータがあります。

パラメータ名 データ型 パラメータ・モード
CLIENT_LTXID RAW IN 進行中のトランザクションのLTXID
COMMITTED BOOLEAN OUT 進行中のトランザクションがコミットされた場合はTRUE、そうでない場合はFALSE
USER_CALL_COMPLETED BOOLEAN OUT 進行中のトランザクションが完了した場合はTRUE、そうでない場合はFALSE

内容は次のとおりです。


参照:

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

6.2.4.2.1 CLIENT_LTXIDパラメータ

アプリケーション(クライアント)が進行中のトランザクションのLTXIDを使用してDBMS_APP_CONT.GET_LTXID_OUTCOMEを呼び出すには、クライアント・ドライバを使用してクライアント・ドライバで使用中の最後のLTXIDを取得する必要があります。クライアント・ドライバは、次回コミットするトランザクションのLTXIDを保持します。この場合、このLTXIDは停止時に進行中のトランザクション用です。JDBC-ThinドライバにはgetLTXID、ODP.NETにはLogicalTransactionId、OCIおよびOCCIにはLTXIDとともにOCI_ATTR_GETを使用します。

JDBC-Thinドライバには、クライアント・ドライバのLTXIDが変更されるたびにトリガーされるコールバックも用意されています。このコールバックを使用して、使用対象の現在のLTXIDを保持できます。このコールバックは、実行の繰返しをブロックする必要があるアプリケーション・サーバーおよびアプリケーションに対して特に役立ちます。


注意:

アプリケーションは、LTXIDをDBMS_APP_CONT.GET_LTXID_OUTCOMEに渡す直前にこれを取得する必要があります。LTXIDを事前に取得すると、以前のLTXIDがDBMS_APP_CONT.GET_LTXID_OUTCOMEに渡される可能性があり、この場合、リクエストが拒否されます。

クライアント・ドライバの詳細は、クライアント・ドライバの関連ドキュメントを参照してください。次に例を示します。

  • JDBCタイプ2およびタイプ4ドライバの場合、『Oracle Database JDBC開発者ガイド』を参照してください。

  • OCIドライバ(OCCIを含む)の場合、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

  • ODP.NETドライバの場合は、『Oracle Data Provider for .NET開発者ガイドfor Microsoft Windows』を参照してください。

6.2.4.2.2 COMMITTEDパラメータ

DBMS_APP_CONT.GET_LTXID_OUTCOMEからアプリケーションに制御が戻された後、アプリケーションは、仮パラメータCOMMITTEDに対応する実際のパラメータの値を確認することにより、進行中のトランザクションがコミットされたかどうかを確認できます。

実際のパラメータの値がTRUEである場合、トランザクションはコミットされています。

実際のパラメータの値がFALSEである場合、トランザクションはコミットされていません。このため、アプリケーションがコードUNCOMMITTEDをエンドユーザーに戻したり、これを使用してトランザクションをリプレイしても安全です。

アプリケーションがUNCOMMITTEDを戻した後に以前のセッションがトランザクションをコミットしないようにするために、DBMS_APP_CONT.GET_LTXID_OUTCOMEによってLTXIDがブロックされます。LTXIDをブロックすることにより、エンドユーザーが未コミット状態に基づいて決定を下したり、アプリケーションがトランザクションをリプレイできるようになり、トランザクションの重複が阻止されます。

6.2.4.2.3 USER_CALL_COMPLETEDパラメータ

一部のトランザクションは完了時に情報を戻します。たとえば、成功時にコミット(自動コミット)を使用するトランザクションは、影響を受ける行の数、またはSELECT文の場合は行自体を戻すことができます。OUTパラメータを持つPL/SQLサブプログラムを呼び出すトランザクションは、これらのパラメータの値を戻します。PL/SQLファンクションを呼び出すトランザクションは、ファンクション値を戻します。また、PL/SQLサブプログラムを呼び出すトランザクションは、COMMIT文を実行してからこれ以外の処理を行うことができます。

進行中のトランザクションが完了時に戻す情報、またはデータベースの変更のコミット後にトランザクションが行うセッション状態の変更をアプリケーションが必要とする場合、アプリケーションは、進行中のトランザクションが完了したかどうかを確認する必要があります。これを行うには、仮パラメータUSER_CALL_COMPLETEDに対応する実際のパラメータの値を確認します。

実際のパラメータの値がTRUEである場合、トランザクションは完了しており、アプリケーションには続行する必要がある情報および処理が存在します。

実際のパラメータの値がFALSEである場合、クライアントからのコールは完了していない可能性があります。このため、アプリケーションには続行する必要がある情報および処理が存在しない可能性があります。

6.2.4.2.4 例外

アプリケーション(クライアント)とOracle Database (サーバー)が同期していない場合、DBMS_APP_CONT.GET_LTXID_OUTCOMEプロシージャにより、次のいずれかの例外が発生します。

例外 説明
ORA-14950: SERVER_AHEAD サーバーがクライアントより進んでいます。つまり、アプリケーションがDBMS_APP_CONT.GET_LTXID_OUTCOMEに渡したLTXIDにより、進行中のトランザクションより古いトランザクションが識別されます。

アプリケーションは、LTXIDをDBMS_APP_CONT.GET_LTXID_OUTCOMEに渡す直前にこれを取得する必要があります。

ORA-14951 - CLIENT_AHEAD クライアントがサーバーより進んでいます。サーバーが、以前の状態にフラッシュバックされたか(第16章「Oracle Flashback Technologyの使用」を参照)、メディア・リカバリを使用してリカバリされたか、以前にオープンされたスタンバイ・データベースであり、データが紛失しました(メディア・リカバリおよびスタンバイ・データベースの詳細は、『Oracle Data Guard概要および管理』を参照)。
ORA-14906 - SAME_SESSION LTXIDを所有するセッションでのGET_LTXID_OUTCOMEの実行はサポートされていません。これは、この実行によってこのセッションに対する追加処理がブロックされるためです。
ORA-14909 - COMMIT_BLOCKED 同じユーザー名を持つ別のユーザーによってGET_LTXID_OUTCOMEを使用してセッションのコミットがブロックされています。GET_LTXID_OUTCOMEはデッド・セッションに対してのみ呼び出す必要があります。ライブ・セッションのブロックは、ALTER SYSTEM KILL SESSION IMMEDIATEを使用した方が確実に実行できます。不明な点は、アプリケーション管理者に問い合せてください。
ORA-14952 GENERAL ERROR DBMS_APP_CONT.GET_LTXID_OUTCOMEでは、進行中のトランザクションの結果を確認できません。トランザクションの処理中にエラーが発生し、エラー・スタックにエラー詳細が表示されます。

6.2.4.3 トランザクション・ガードの使用

アプリケーション(クライアント)は、エラー・メッセージを受け取った後、次の手順を実行してトランザクション・ガードを使用する必要があります。

  1. エラーの原因が停止(リカバリ可能)であるかどうかを確認します。

    手順については、クライアント・ドライバの関連ドキュメントを参照してください(OCI、OCCIおよびODP.NETはOCI_ATTRIBUTE、JDBCはisRecoverable)。

  2. エラーがリカバリ可能である場合、クライアント・ドライバのAPIを使用して、進行中のトランザクションの論理トランザクション識別子(LTXID)を取得します。

    手順については、クライアント・ドライバの関連ドキュメントを参照してください。

  3. データベースを再接続します。

    アプリケーションが取得するセッションには、新規セッションまたはプール・セッションがあります。

  4. 手順2のLTXIDを使用してDBMS_APP_CONT.GET_LTXID_OUTCOMEを呼び出します。

  5. 仮パラメータCOMMITTEDに対応する実際のパラメータの値を確認します。

    値がTRUEである場合、進行中のトランザクションがコミットされたことをアプリケーションに伝えます。アプリケーションはこの結果をエンドユーザーに戻すことができ、状態が正しい場合、アプリケーションは継続できます。

    値がFALSEである場合、アプリケーションはUNCOMMITTEDまたは同様のメッセージをユーザーに戻すことができ、ユーザーは次の手順を選択できるようになります。オプションとして、アプリケーションはユーザーに対してトランザクションをリプレイできます。次に例を示します。

    1. 必要な場合、クライアント側で状態の変更をクリーン・アップします。

    2. 進行中のトランザクションを再発行します。

    進行中のトランザクションを再発行しないときに、進行中のトランザクションが完了時に戻す情報や、データベースの変更のコミット後にトランザクションが実行する処理をアプリケーションが必要としない場合は、続行します。それ以外の場合、仮パラメータUSER_CALL_COMPLETEDに対応する実際のパラメータの値を確認します。

    値がTRUEである場合、続行します。

    値がFALSEである場合、アプリケーションを続行できないことをアプリケーション・ユーザーに伝えます。

6.3 読取り専用トランザクションでの反復可能読取りの保証

デフォルトでは、Oracle Databaseでは、文レベルの読取り一貫性は保証されますが、トランザクション・レベルの読取り一貫性は保証されません。文レベルの読取り一貫性を持つ場合、ある文に含まれる問合せは、この文の存続中、一貫性のあるデータを生成します。その他の文による変更は反映されません。トランザクション・レベルの読取り一貫性(反復可能読取り)を持つ場合、あるトランザクションに含まれる問合せは、このトランザクションの存続中、一貫性のあるデータを生成します。その他のトランザクションによる変更は反映されません。

DML文を含まないトランザクションに対するトランザクション・レベルの読取り一貫性を保証するには、このトランザクションを読取り専用に指定します。読取り専用トランザクションに含まれる問合せには、このトランザクションが開始される前にコミットされた編集以外見えないため、トランザクションの存続中、問合せの結果の一貫性が保たれます。

読取り専用トランザクションは、データ・ロックを追加取得することなしに、トランザクション・レベルの読取り一貫性を提供します。したがって、読取り専用トランザクションがデータを問い合せている間、別のトランザクションが同じデータを問い合せ、更新することができます。

ある読取り専用トランザクションは次の文で始まります。

SET TRANSACTION READ ONLY [ NAME string ];

SET TRANSACTION READ ONLY文の前に来ることができるのは、DDL文のみです。SET TRANSACTION READ ONLY文の実行が正常に終了した後で、このトランザクションはSELECT(ただし、FOR UPDATEなしで)、COMMITROLLBACK、または非DML文(SET ROLEALTER SYSTEMLOCK TABLEなど)のみを含むことができます。COMMITROLLBACK、またはDDL文は読取り専用トランザクションで終わります。


参照:

SET TRANSACTION文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

実行時間の長い問合せでは、読取り一貫性(CR)操作に必要なUNDO情報が無効になったためにエラーが発生することがあります。これは、アクティブなトランザクションによって、コミット済UNDOブロックが上書きされた場合に起こります。

自動UNDO管理を行うと、データベース管理者(DBA)は、パラメータUNDO_RETENTIONを使用してUNDO情報のデータベースでの保存期間を明示的に制御できます。たとえば、UNDO_RETENTIONが30分である場合、すべてのコミット済UNDO情報は、最低30分間データベースに保持されます。この設定により、実行時間が30分以内の問合せでOERエラー「スナップショットが古すぎます。」が発生することはなくなります。


参照:

  • 読取り一貫性の詳細は、『Oracle Database概要』を参照してください。

  • 実行時間の長い問合せおよび再開可能領域割当ての詳細は、『Oracle Database管理者ガイド』を参照してください。


6.4 明示的な表のロック

Oracle Databaseには、データの同時実行性、データの整合性、および文レベルでの読取り一貫性を保持するための、デフォルト・ロック・メカニズムが用意されています(これらの詳細は、『Oracle Database概要』を参照)。ただし、これらのメカニズムは表を明示的にロックすることによりオーバーライドできます。表の明示的なロックは次のような状況で役立ちます。

次のどのSQL文を使用しても、デフォルト・ロックをトランザクション・レベルでオーバーライドできます。

  • LOCK TABLE(『Oracle Database SQL言語リファレンス』を参照)

  • FOR UPDATE句付きSELECT文(『Oracle Database SQL言語リファレンス』を参照)

  • READ ONLYまたはISOLATION LEVEL SERIALIZABLEオプションを指定したSET TRANSACTION(『Oracle Database SQL言語リファレンス』を参照)

これらの文によって取得されるロックは、トランザクションのコミット後またはロールバック後に解除されます。


参照:

  • ALTER SESSION文のISOLATION_LEVELパラメータの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。


DMLロックの最大数は、DML_LOCKS初期化パラメータ(『Oracle Databaseリファレンス』を参照)によって決定されます。通常、デフォルト値には十分な値が設定されていますが、明示的なロックを使用する際に、この値を大きくする必要がある場合があります。


注意:

いずれかのレベルでOracle Databaseのデフォルト・ロックをオーバーライドする場合は、データ整合性が保証されていること、データ同時実行性が許容されていること、およびデッドロックの可能性がないか、デッドロックが適切に処理されていることを確認してください。

内容は次のとおりです。

6.4.1 表のロックを取得するために必要な権限

自らのスキーマの表に対して表のロックを取得するためには、特別な権限は必要ありません。他スキーマ内の表に対して表ロックを取得するには、LOCK ANY TABLEシステム権限またはその表に対する(SELECTUPDATEなどの)オブジェクト権限が必要です。

6.4.2 ロック方法の選択

LOCK TABLE文が実行されると、トランザクションは指定された表ロックを明示的に取得します。LOCK TABLE文は、デフォルト・ロックを明示的にオーバーライドします。ビューに対してLOCK TABLE文が発行されると、基礎となる実表がロックされます。次の文は、その文が含まれるトランザクションのために、employees表およびdepartments表に対する排他表ロックを取得します。

LOCK TABLE employees, departments IN EXCLUSIVE MODE NOWAIT;

ロック・モードが同じ場合は、ロックする表またはビューを複数指定できます。ただし、各LOCK TABLE文に指定できるロック・モードは1つのみです。


注意:

表がロックされると、その表のすべての行がロックされます。他のユーザーは、その表を変更できません。個々の行のロックについて詳細は、6.4.4項を参照してください。

LOCK TABLE文では、表ロックの待ち時間も指示できます。

  • ロックを待機しない場合は、NOWAITまたはWAIT 0を指定します。

    表ロックがすぐに使用可能である場合にのみ表ロックを取得します。すぐに使用可能でない場合は、その時点ではロックを使用できないことを示すエラーが戻されます。

  • 表ロックを取得するためにn秒まで待機する場合は、WAIT nを指定します。ここで、nは0より大きく、100000以下です。

    n秒経過しても表ロックを使用できない場合は、その時点ではロックを使用できないことを示すエラーが戻されます。

  • ロックを取得するまで無限に待機する場合は、NOWAITおよびWAITのいずれも指定しないでください。

    データベースは表が使用可能になるまで無限に待機し、表をロックして制御を戻します。データベースでDML文と同時にDDL文を実行すると、タイムアウトやデッドロックが発生する場合があります。これらのタイムアウトやデッドロックがデータベースで検出されると、エラーが戻されます。

内容は次のとおりです。


参照:

LOCK TABLE文の構文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

6.4.2.1 ROW SHARE MODEおよびROW EXCLUSIVE MODEでロックする場合

共有(ROW SHARE MODE)表ロック、および排他(ROW EXCLUSIVE MODE)表ロックは最も高い同時実効性を提供します。次のような場合に使用します。

  • トランザクション内で表を更新する前に、別のトランザクションが共有表ロック、行共有表ロックまたは排他表ロックを割り込んで取得しないようにする必要がある場合。

    別のトランザクションが共有表ロック、行共有表ロックまたは排他表ロックを割り込んで取得した場合、他のどのトランザクションも、そのロックしているトランザクションがコミットまたはロールバックされるまで、表を更新できません。

  • トランザクションで表を変更できるようになるまで、表の変更または削除を防止する必要がある場合。

6.4.2.2 SHARE MODEでロックする場合

SHARE MODE表ロックは非常に制限の多いデータ・ロックです。次のような場合に使用します。

  • トランザクションが表を問い合せるのみで、そのトランザクションの存続中一貫した一連の表データを必要とする場合。

  • 表に対してSHARE MODEロックを保持するすべてのトランザクションがコミットまたはロールバックするまで、ロックされている表を更新しようとする他のトランザクションを阻止できる場合。

  • 他のトランザクションが、ロックされている表に対して同時にSHARE MODE表ロックを取得でき、またトランザクション・レベルの読取り一貫性のオプションを使用できる場合。


    注意:

    同じトランザクション内で、後から表を更新しないこともあります。ただし、複数のトランザクションが同じ表に対して共有表ロックを同時に保持している場合は(SELECT FOR UPDATE文の結果によって行ロックが保持されている場合でも)、どのトランザクションも表を更新できません。したがって、同じ表に対する同時共有表ロックがよく発生する場合は、更新処理を継続できず、デッドロックがよく発生することになります。このような場合には、かわりに共有行排他ロックまたは排他表ロックを使用してください。

シナリオ:employeesおよびbudget_tabは、第3の表departmentsの一貫した一連のデータを必要とします。特定の部門番号に関して、employeesおよびbudget_tabの情報を更新し、この2つのトランザクションの間にメンバーが部門に追加されないように保証するものとします。

解決方法: 例6-1のようにdepartments表をSHARE MODEでロックします。departments表の更新はまれなため、ロックしても他の多くのトランザクションの待機時間が長くなることはありません。

例6-1 SHARE MODEでのLOCK TABLE

-- Create and populate table:
 
DROP TABLE budget_tab;
CREATE TABLE budget_tab (
  sal     NUMBER(8,2),
  deptno  NUMBER(4)
);
 
INSERT INTO budget_tab (sal, deptno)
  SELECT salary, department_id
  FROM employees;
 
-- Lock departments and update employees and budget_tab:
 
LOCK TABLE departments IN SHARE MODE;
 
UPDATE employees
  SET salary = salary * 1.1
  WHERE department_id IN
    (SELECT department_id FROM departments WHERE location_id = 1700);
 
UPDATE budget_tab
SET sal = sal * 1.1
WHERE deptno IN
  (SELECT department_id FROM departments WHERE location_id = 1700);
 
COMMIT;  -- COMMIT releases lock

6.4.2.3 SHARE ROW EXCLUSIVE MODEでロックする場合

共有(SHARE ROW EXCLUSIVE MODE)表ロックは、次のような場合に使用します。

  • トランザクションで、特定の表についてトランザクション・レベルの読取り一貫性があり、かつ、ロックされている表が更新可能であることが必要な場合。

  • 他のトランザクションによる(SELECT FOR UPDATEを使用した)明示的な行ロックの可能性を考慮していない場合。ロック中のトランザクション内のUPDATEおよびINSERT文が待機させられ、デッドロックが発生する可能性があります。

  • このように動作するトランザクションが1つのみ必要な場合。

6.4.2.4 EXCLUSIVE MODEでロックする場合

排他(EXCLUSIVE MODE)表ロックは、次のような場合に使用します。

  • トランザクションが、ロックされている表にすぐに更新アクセスをする必要がある場合。トランザクションが排他表ロックを保持していると、他のトランザクションはロックされた表の中の特定の行をロックできません。

  • トランザクションがコミットまたはロールバックされるまで、ロックされた表に対してトランザクション・レベルの読取り一貫性が保持される場合。

  • 低レベルのデータ同時実行性を意識する必要がなく、排他表ロックを要求するトランザクションを順次待機させて表を順番に更新させる場合。

6.4.3 Oracle Databaseによる表ロック制御

Oracle Databaseに表ロック制御を任せると、アプリケーションに必要なプログラム・ロジックが少なくて済みます。ただし、表ロックを自分で管理する場合より制御範囲が小さくなります。

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE文またはALTER SESSION ISOLATION LEVEL SERIALIZABLE文を発行すると、基礎となるロック・プロトコルを変更しなくても、ANSIのシリアライズ可能性を維持できます。この手法によって、ANSIのシリアライズ可能性を維持しながら表へ同時アクセスできます。表をロックすることによって、同時実行性が大幅に減少します。


参照:

  • SET TRANSACTION文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

  • ALTER SESSION文の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。


これらのパラメータの設定変更は、インスタンスの停止時にのみ行ってください。複数インスタンスが単一データベースにアクセスする場合は、すべてのインスタンスでこれらのパラメータの設定を同じにする必要があります。

6.4.4 明示的な行ロック

FOR UPDATE句を含むSELECT文を使用すると、デフォルト・ロックをオーバーライドできます。この文は、選択されている行がその後の文で更新されることを想定し、(UPDATE文のように)選択された行に対する明示的な行ロックを取得します。

SELECT FOR UPDATE文を使用すると、その行を変更せずに行をロックできます。たとえば、『Oracle Database PL/SQL言語リファレンス』では、いくつかのトリガーで参照整合性を実装する方法を示しています。EMP_DEPT_CHECKトリガーでは、参照される親キー値を含む行が、トランザクションの存続中は同じ値のままであることを保証するためにロックされます。親キーが更新または削除された場合、参照整合性違反になります。

SELECT FOR UPDATE文は、ユーザーが1行以上の特定行のフィールドを変更できるような対話型プログラムでよく使用されます(時間がかかることがあります)。行を更新している対話型プログラム・ユーザーが常に1ユーザーのみであるように、行がロックされます。

カーソル定義にSELECT FOR UPDATE文が使用される場合は、カーソルがオープンされるとき(最初のフェッチの前)に結果セット内の行がロックされます。行は、カーソルからフェッチされるときに、個別にロックされるわけではありません。カーソルをオープンしたトランザクションがコミットまたはロールバックされたときにのみ、ロックが解除されます。カーソルがクローズされるときには、ロックは解除されません。

SELECT FOR UPDATE文の結果セット内の各行は個別にロックされます。SELECT FOR UPDATE文は、競合している行ロックを他のトランザクションが解除するまで待機します。したがって、SELECT FOR UPDATE文が表の行を多数ロックし、表に対して非常に多くの更新アクティビティが発生する場合は、EXCLUSIVE表ロックを取得する方がパフォーマンスが改善する場合があります。


注意:

問合せの実行中に、SELECT FOR UPDATEに対する結果セットが変化する場合があります。たとえば、問合せ開始後に問合せで選択された列が更新されたり、行が削除された場合です。このような場合、SELECT FOR UPDATEは変更されなかった行でロックを取得し、このロックを使用して表の読取り一貫性スナップショットを取得してから、残りのロックを取得するために問合せを再起動します。

アプリケーションでSELECT FOR UPDATE文を使用する場合に、競合するロック・リクエストによってユーザーが原因のデッドロックが発生しないことを保証できない場合は(たとえば、表の同時実行DML文がSELECT FOR UPDATE文の問合せの結果セットに影響を与えないことを確認することによって)、このようなデッドロック(ORA-00060)をアプリケーションが常に適切に処理するようにコーディングします。


デフォルトでは、要求された行ロックが取得されるまでSELECT FOR UPDATE文は待機します。この動作を変更するには、SELECT FOR UPDATE文でNOWAITWAIT、またはSKIP LOCKED句を使用します。このような句の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

6.4.5 明示的なロックにおける同時実行性の例

表6-2に、LOCK TABLE文およびFOR UPDATE句を含むSELECT文が使用された場合に、データの同時実行性、整合性および一貫性がOracle Databaseでどのように維持されるかを示します。簡略にするため、ORA-00054のメッセージ・テキスト(「リソース・ビジー、NOWAITが指定されていました。)は記載していません。ユーザーが入力するテキストは太字で表示しています。


注意:

ハイブリッド列圧縮(HCC)で圧縮された表では、DML文によって行ではなく圧縮単位がロックされます。特定のOracleストレージ・システムの機能であるHCCの詳細は、『Oracle Database概要』を参照してください。

表6-2 明示的なロックにおける同時実行性の例

トランザクション1 タイム・ポイント トランザクション2
LOCK TABLE hr.departments
IN ROW SHARE MODE;

Statement processed.

1



2

DROP TABLE hr.departments;

DROP TABLE hr.departments
* 
ORA-00054

(トランザクション1によって表がロックされているため、排他DDLロックを取得できません。)


3

LOCK TABLE hr.departments
IN EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

4

SELECT location_id
FROM hr.departments
WHERE department_id = 20
FOR UPDATE OF location_id;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.
UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;

(トランザクション2によって同じ行がロックされているため待機します。)

5



6

ROLLBACK;

(行ロックを解放します。)

1 row processed.

ROLLBACK;

7


LOCK TABLE hr.departments
IN ROW EXCLUSIVE MODE;
 
Statement processed.

8



9

LOCK TABLE hr.departments
IN EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

10

LOCK TABLE hr.departments
IN SHARE ROW EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

11

LOCK TABLE hr.departments
IN SHARE ROW EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

12

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;
 
1 row processed.

13

ROLLBACK;
SELECT location_id
FROM hr.departments
WHERE department_id = 20
FOR UPDATE OF location_id;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

14



15

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;
 
1 row processed.

(トランザクション1によって同じ行がロックされているため待機します。)

ROLLBACK;

16



17

1 row processed.

(競合するロックが解放されました。)

ROLLBACK;
LOCK TABLE hr.departments
IN ROW SHARE MODE
 
Statement processed.

18



19

LOCK TABLE hr.departments
IN EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

20

LOCK TABLE hr.departments
IN SHARE ROW EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

21

LOCK TABLE hr.departments
IN SHARE MODE;
 
Statement processed.

22

SELECT location_id
FROM hr.departments
WHERE department_id = 20;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

23

SELECT location_id
FROM hr.departments
WHERE department_id = 20
FOR UPDATE OF location_id;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

24

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;

(トランザクション1に競合する表ロックがあるため待機します。)

ROLLBACK;

25



26

1 row processed.

(競合する表ロックが解放されました。)

ROLLBACK;
LOCK TABLE hr.departments
IN SHARE ROW EXCLUSIVE MODE;
 
Statement processed.

27



28

LOCK TABLE hr.departments
IN EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

29

LOCK TABLE hr.departments
IN SHARE ROW EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

30

LOCK TABLE hr.departments
IN SHARE MODE
NOWAIT;
 
ORA-00054

31

LOCK TABLE hr.departments
IN ROW EXCLUSIVE MODE
NOWAIT;
 
ORA-00054

32

LOCK TABLE hr.departments
IN SHARE MODE
NOWAIT;
 
ORA-00054

33

SELECT location_id
FROM hr.departments
WHERE department_id = 20;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

34

SELECT location_id
FROM hr.departments
WHERE department_id = 20
FOR UPDATE OF location_id;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

35

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;

(トランザクション1に競合する表ロックがあるため待機します。)

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 20;

(トランザクション2によって同じ行がロックされているため待機します。)

36

(デッドロックが発生します。)

Cancel operation.

ROLLBACK;

37



38

1 row processed.
LOCK TABLE hr.departments
IN EXCLUSIVE MODE;

39



40

LOCK TABLE hr.departments
IN EXCLUSIVE MODE;

ORA-00054

41

LOCK TABLE hr.departments
IN ROW EXCLUSIVE MODE
NOWAIT;

ORA-00054

42

LOCK TABLE hr.departments
IN SHARE MODE;

ORA-00054

43

LOCK TABLE hr.departments
IN ROW EXCLUSIVE MODE
NOWAIT;

ORA-00054

44

LOCK TABLE hr.departments
IN ROW SHARE MODE
NOWAIT;

ORA-00054

45

SELECT location_id
FROM hr.departments
WHERE department_id = 20;
 
LOCATION_ID
-----------
DALLAS
 
1 row selected.

46

SELECT location_id
FROM hr.departments
WHERE department_id = 20
FOR UPDATE OF location_id;

(トランザクション1に競合する表ロックがあるため待機します。)

UPDATE hr.departments
SET department_id = 30
WHERE department_id = 20;
 
1 row processed.

47


COMMIT;

48



49

0 rows selected.

(トランザクション1の競合するロックが解放されました。)

SET TRANSACTION READ ONLY;

50


SELECT location_id
FROM hr.departments
WHERE department_id = 10;
 
LOCATION_ID
-----------
BOSTON

51



52

UPDATE hr.departments
SET location_id = 'NEW YORK'
WHERE department_id = 10;
 
1 row processed.
SELECT location_id
FROM hr.departments
WHERE department_id = 10;
 
LOCATION_ID
-----------
BOSTON

(トランザクション1には、未コミット・データが表示されません。)

53



54

COMMIT;
SELECT location_id
FROM hr.departments
WHERE department_id = 10;
 
LOCATION_ID
-----------
BOSTON

(トランザクション2がコミットした後でも同じ結果になります。)

55


COMMIT;

56


SELECT location_id
FROM hr.departments
WHERE department_id = 10;
 
LOCATION_ID
-----------
NEW YORK

(コミット済データが表示されます。)

57



6.5 Oracle Lock Managementサービス(ユーザー・ロック)の使用

DBMS_LOCKパッケージのサブプログラムを起動することで、アプリケーションはOracle Lock Managementサービス(ユーザー・ロック)を使用できます。アプリケーションは、特定のモードのロックを要求し、同一または別のインスタンスの別のサブプログラムで認識できる一意の名前を付け、ロック・モードを変更し、解除できます。確保されるユーザー・ロックはOracle Databaseロックであるため、デッドロックの検出などのデータベース・ロックのすべての機能を持っています。分散トランザクションで使用されるユーザー・ロックは、COMMITと同時に解除されるようになっていることを確認してください。解除されないと、検出されないデッドロックが発生する可能性があります。


参照:

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

内容は次のとおりです。

6.5.1 ユーザー・ロックを使用する場合

次のような場合、ユーザー・ロックが役立ちます。

  • 端末などの装置に対して排他的アクセスを可能にする場合

  • アプリケーション・レベルの読込みロックを適用する場合

  • ロックの解除を検出し、アプリケーションの終了後にクリーンアップする場合

  • アプリケーションを同期化して、順次処理を実行する場合

例6-2は、複数のユーザーが1つのデバイスにアクセスする必要がある場合に、競合が発生しないように保証するためのPro*COBOLプリコンパイラでのロックの使用方法を示しています。

例6-2 Pro*COBOLプリコンパイラによるロックの使用方法

****************************************************************** 
* Print Check                                                    * 
* Any cashier may issue a refund to a customer returning goods.  * 
* Refunds under $50 are given in cash, more than $50 by check.   * 
* This code prints the check. One printer is opened by all       * 
* the cashiers to avoid the overhead of opening and closing it   * 
* for every check, meaning that lines of output from multiple    * 
* cashiers can become interleaved if you do not ensure exclusive * 
* access to the printer. The DBMS_LOCK package is used to        * 
* ensure exclusive access.                                       * 
****************************************************************** 
CHECK-PRINT 
*    Get the lock "handle" for the printer lock. 
   MOVE "CHECKPRINT" TO LOCKNAME-ARR. 
   MOVE 10 TO LOCKNAME-LEN. 
   EXEC SQL EXECUTE 
      BEGIN DBMS_LOCK.ALLOCATE_UNIQUE ( :LOCKNAME, :LOCKHANDLE ); 
      END; END-EXEC. 
*   Lock the printer in exclusive mode (default mode).
   EXEC SQL EXECUTE 
      BEGIN DBMS_LOCK.REQUEST ( :LOCKHANDLE ); 
      END; END-EXEC. 
*   You now have exclusive use of the printer, print the check. 
  ... 
*   Unlock the printer so other people can use it 
EXEC SQL EXECUTE 
      BEGIN DBMS_LOCK.RELEASE ( :LOCKHANDLE ); 
      END; END-EXEC.

6.5.2 ロックの表示および監視

表6-3では、インスタンスで処理中のトランザクションのロック情報を表示する、Oracle Databaseの機能について説明しています。

表6-3 ロック情報の表示方法

ツール 説明

パフォーマンス監視データ・ディクショナリ・ビュー

『Oracle Database管理者ガイド』を参照してください。

UTLLOCKT.SQL

UTLLOCKT.SQLスクリプトは、ツリー構造の簡単なロック待機グラフを表示します。スクリプトの実行にSQLツール(SQL*Plusなど)を使用し、システム内のロック待機中のセッション、およびそれに対応するブロッキング・ロックを出力します。このスクリプト・ファイルの位置は、オペレーティング・システムによって異なります。(UTLLOCKT.SQLを使用する前に、CATBLOCK.SQLスクリプトを実行する必要があります。)


6.6 シリアライズ可能トランザクションを使用した同時実行性の制御

Oracle Databaseは、デフォルトでは、同時に実行されるトランザクションの同じ表および同じデータ・ブロック内での行の修正、追加または削除を許可しています。トランザクションAが表を変更すると、トランザクションAがコミットされるまで、同時に実行しているトランザクションでは参照できません。トランザクションAが、(DML文またはSELECT FOR UPDATE文を使用して)別のトランザクションBによってロックされている行を更新または削除しようとすると、トランザクションAのDML文は、トランザクションBがコミットまたはロールバックされるまでブロックされます。この同時実行性モデルは、より高度な同時実行性が提供されて、パフォーマンスが改善されるため、ほとんどのアプリケーションに適しています。

ただし、まれにシリアライズ可能なトランザクションが必要な場合もあります。シリアライズ可能トランザクションは、シリアル・モードで同時に実行します。シリアル・モードでは、同時トランザクションは、それらのトランザクションが順次(つまり一度に1つずつ)実行された場合に可能となるデータベースの変更のみが可能です。シリアライズ可能トランザクションの開始以降に、別のトランザクションにより変更されたデータを変更しようとすると、ORA-08177が発生します。

シリアライズ可能トランザクションがORA-08177で失敗した場合、アプリケーションは次のいずれかで対処できます。

  • そのポイントまで実行された作業をコミットします。

  • その他の様々な文を、トランザクション内の直前のセーブポイントまでロールバックした後に実行します。

  • トランザクションをロールバックし再度実行します。

    トランザクションがトランザクション・スナップショットを取得するため、操作が成功する可能性が高くなります。


    ヒント:

    トランザクションのロールバックおよび再実行によるパフォーマンスのオーバーヘッドを最小化するには、他の同時トランザクションと競合する可能性のあるDML文は、できるだけトランザクションの始めの方に置くようにしてください。


注意:

シリアライズ可能トランザクションは遅延セグメント作成または時間隔パーティション化とともに動作しません。セグメントが作成されていない空の表、またはセグメントがまだない時間隔パーティション化された表のパーティションにデータを挿入しようとすると、エラーが発生します。

内容は次のとおりです。

6.6.1 トランザクションの相互作用と分離レベル

ANSI/ISO SQL標準は3種類のトランザクションの相互作用を定義しています。

トランザクションの相互作用 定義
内容を保証しない読込み トランザクションAはトランザクションBによるコミットされていない変更を読み込みます。
反復不能な読込み トランザクションAはデータを読み込み、トランザクションBがデータを変更して変更をコミットし、トランザクションAがデータを再度読み込み、変更を確認します。
仮読込み トランザクションAが問合せを実行し、トランザクションBが新しい行を挿入して変更をコミットし、トランザクションAが問合せを繰り返して新しい行を確認します。

トランザクションが可能な相互作用の種類は分離レベルによって決まります。ANSI/ISO SQL標準は4レベルの分離を定義しています。表6-4に、各分離レベルで可能な相互作用の種類を示します。

表6-4 ANSI/ISO SQL分離レベルおよび可能なトランザクションの相互作用

分離レベル 内容を保証しない読込み 反復不能な読込み 仮読込み

READ UNCOMMITTED

可能性あり

可能性あり

可能性あり

READ COMMITTED

可能性なし

可能性あり

可能性あり

REPEATABLE READ

可能性なし

可能性なし

可能性あり

SERIALIZABLE

可能性なし

可能性なし

可能性なし


表6-5に、Oracle Databaseで用意されるANSI/ISO SQLトランザクション分離レベルを示します。

表6-5 Oracle Databaseで用意されるANSI/ISO SQL分離レベル

分離レベル Oracle Databaseで用意される

READ UNCOMMITTED

いいえ、Oracle Databaseでは「内容を保証しない読込み」は許可されません。他のデータベース製品の中には、スループットの改善のために、この方法を使用するものもありますが、スループットの高いOracle Databaseでは不要です。

READ COMMITTED

はい(デフォルト)。Oracle Databaseでは、問合せの始め(スナップショット時)にコミット済のデータのみを参照するため、READ COMMITTED分離についてANSI/ISO SQL標準以上の整合性を提供します。

REPEATABLE READ

はい、トランザクション分離レベルをSERIALIZABLEに設定する場合。

SERIALIZABLE

はい、トランザクション分離レベルをSERIALIZABLEに設定する場合。


図6-1に、任意のトランザクション(SERIALIZABLEまたはREAD COMMITTEDのいずれか)とシリアライズ可能トランザクションとの相互作用を示します。

図6-1 シリアライズ可能トランザクションと別のトランザクションの相互作用

図6-1の説明が続きます
「図6-1 シリアライズ可能トランザクションと別のトランザクションの相互作用」の説明

6.6.2 分離レベルの設定

トランザクション分離レベルをセッション全体のトランザクションに設定するには、ALTER SESSION文を使用します。『Oracle Database SQL言語リファレンス』を参照してください。

トランザクション分離レベルを特定のトランザクションに設定するには、SET TRANSACTION文のISOLATION LEVEL句を使用します。SET TRANSACTION文は『Oracle Database SQL言語リファレンス』で説明しているように、トランザクションの最初の文である必要があります。


注意:

トランザクション分離レベルをSERIALIZABLEに設定する場合は、ALTER TABLE文を使用して、INITRANSパラメータを3以上に設定する必要があります。多数のトランザクションが同じブロックを更新する表では、この値を大きくします。INITRANSの詳細は、『Oracle Database SQL言語リファレンス』を参照してください。

6.6.3 シリアライズ可能トランザクションおよび参照整合性

Oracle Databaseは、SERIALIZABLEトランザクション内であっても読込みロックを使用しないため、あるトランザクションによって読み込まれたデータは、別のトランザクションでオーバーライドできます。そのため、アプリケーション・レベルでデータベースの整合性チェックを実行するトランザクションでは、読み込んだデータはトランザクション中には変更されないと考えないでください(そのような変更がトランザクションからはわからない場合も)。SERIALIZABLEトランザクションを使用した場合でも、アプリケーション・レベルの整合性チェックのコードを十分注意して作成してください。

図6-2に、2つの表の間の参照整合性の親子関係を保持するために、アプリケーション・レベルでチェックを実行するトランザクションAとB (READ COMMITTEDまたはSERIALIZABLEのいずれか)を示します。トランザクションAは、親表に問い合せて特定の主キー値を持つ行が存在するかどうかをチェックした後、対応する子行を子表に挿入します。トランザクションBは、子表に問い合せて特定の主キー値を持つ子行が存在しないことをチェックした後、親表から対応する親行を削除します。この場合、両方のトランザクションが読み込んだデータは、そのトランザクションが完了する前には変更されないものと想定しています(確認はしません)。

図6-2 参照整合性チェック

図6-2の説明が続きます
「図6-2 参照整合性チェック」の説明

トランザクションAが実行した問合せのために、トランザクションBが親行を削除できなくなることはありません。トランザクションBが実行した問合せのために、トランザクションAが子行を挿入できなくなることもありません。したがって、次のことが起こる可能性があります。

  1. トランザクションAは親表に問い合せて、指定した親行を確認します。

  2. トランザクションBは子表に問い合せて、指定した親行が子行を持たないことを確認します。

  3. 指定した親行を確認して、トランザクションAは対応する子行を子表に挿入します。

  4. 指定した親行が子行を持たないことを確認して、トランザクションBは指定した親行を親表から削除します。

    これで、トランザクションAが手順3で挿入した子行には親行がなくなりました。

このような結果は、AおよびBの両方がSERIALIZABLEトランザクションであったとしても発生する可能性があります。どちらのトランザクションも、整合性チェックのために読み込んだデータに対する変更を他方が妨げないからです。

あるトランザクションが問い合せたデータが別のトランザクションにより同時に変更または削除されないようにするには、ANSI/ISO SQL標準のSERIALIZABLE分離レベルが提供するよりも高いレベルのトランザクション分離が必要です。ただしOracle Databaseでは、次のことが可能です。

  • トランザクションAはSELECT FOR UPDATE文を使用して、親行を問い合せてロックし、トランザクションBがその行を削除しないようにします。

    SELECT文のFOR UPDATE句の詳細は、 『Oracle Database SQL言語リファレンス』を参照してください。

  • トランザクションBは、処理手順を逆にして、トランザクションAが親行を見つけられないようにします(その結果、子行を挿入できなくなります)。つまりトランザクションBは次のことができます。

    1. 親行を削除します。

    2. 子表を問い合せます。

    3. 削除した親行に、子表内の子行がある場合は、親行の削除をロールバックします。

または、トリガーを使用して参照整合性を施行することもできます。トランザクションAが親表を問い合せるかわりに、子表で次を行う行レベルBEFORE INSERTトリガーを定義します。

  • SELECT FOR UPDATE文を使用して親表を問い合せ、親行が存在するかどうかを確認することで、子行を挿入するトランザクションの処理中に、親行がデータベース内に残るようにします。

  • 親行が存在しない場合、子行の挿入は拒否されます。


参照:

トリガーを使用した親表と子表の間の参照整合性の維持の詳細は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

トリガーによって発行されたSQL文は、そのトリガーを起動した文のコンテキスト内で実行されます(トリガー起動文とトリガー起動された文は同じデータベース状態を参照します)。そのため、READ COMMITTEDトランザクションがトリガー起動文を実行する場合、トリガー起動された文は、トリガー起動文の実行開始時点のデータベースを参照します。SERIALIZABLEトランザクションがトリガー起動文を実行する場合、トリガー起動された文は、そのトランザクションの開始時点のデータベースを参照します。いずれの場合も、トリガーで SELECT FOR UPDATEを使用すると、参照整合性が正しく施行されます。

6.6.4 READ COMMITTEDおよびSERIALIZABLE分離レベル

Oracle Databaseでは、READ COMMITTEDおよびSERIALIZABLEという2つのトランザクション分離レベルが用意されています。どちらのレベルも高度な一貫性および同時実行性を提供し、競合を軽減し、実社会でのアプリケーション用に設計されています。この項では、2つの分離レベルを比較し、その選択方法を説明します。

内容は次のとおりです。

6.6.4.1 トランザクション集合の整合性の違い

どの読込みにおいても、同じコミット済トランザクション集合によって書き込まれたデータが戻される場合、操作(問合せまたはトランザクション)は、「トランザクション集合整合である」といいます。トランザクション集合整合でない操作では、ある集合のトランザクションの変更が反映される読込みと、他のトランザクションによって行われた変更が反映される読込みが存在します。そのような操作では、そのデータベースは、コミットされたトランザクション集合が反映されていない状態のデータベースのように見えます。

内容は次のとおりです。

6.6.4.1.1 Oracle Database

READ COMMITTED分離レベルでのOracle Databaseトランザクションは、問合せによって読み込まれたすべての行が、その問合せが始まる前にコミットされている必要があるため、文単位でのトランザクション集合整合です。

SERIALIZABLE分離レベルでのOracle Databaseトランザクションは、SERIALIZABLEトランザクション内のすべての文が、トランザクション開始時点のデータベースのイメージに対して実行されるため、トランザクション単位でのトランザクション集合整合です。

6.6.4.1.2 他のデータベース・システム

他のデータベース・システムでは、READ UNCOMMITTED分離レベルでの1回の問合せは、トランザクション集合整合ではありません。別のトランザクションによって行われた変更のサブセットしか見えないからです。たとえば、ディテール表とマスター表を結合すると、別のトランザクションによって挿入されたマスター・レコードを見ることはできますが、そのトランザクションによって挿入された対応するディテールは見えません(その逆も同じです)。READ COMMITTED分離レベルではこのような問題は回避されるため、読込みロック・システムより高い整合性が得られます。

読込みロック・システムでは、同時更新ができないようにするかわりに、REPEATABLE READ分離レベルによって、トランザクション・レベルではなく、文レベルでトランザクション集合の整合性が提供されます。仮読込み保護がないため、同一のトランザクションによる2つの問合せが、トランザクションの別の集合によってコミットされたデータを参照できます。これらのシステムでは、スループットに制限がありデッドロックされやすいSERIALIZABLE分離レベルの場合のみ、トランザクション・レベルでのトランザクション集合の整合性が提供されます。

6.6.4.2 トランザクション分離レベルの選択

トランザクション分離レベルの選択は、パフォーマンスと整合性の必要性、およびアプリケーション・コーディング要件により異なります。同時実行性(トランザクションのスループット)と整合性はトレードオフの関係にあります。トランザクションの分離レベルの選択では、アプリケーションと作業負荷を考慮します。異なるトランザクションにはそれぞれ個別の分離レベルを選択できます。

多数のユーザーが、トランザクションを同時に次々に送る環境の場合、予期されるトランザクション到着頻度、応答時間要件、および必要な整合性の程度を考慮します。

READ COMMITTED分離レベルでは、一部のトランザクションについては(仮読込みおよび反復不能な読込みからの)一貫性のない結果が生成される可能性は多少高くなりますが、かなり高い同時実行性を提供できます。

SERIALIZABLE分離レベルの場合は、仮読込みおよび反復不能な読込みから保護されているため、より高い整合性が提供され、読込み/書込みトランザクションが問合せを2回以上実行する場合にはこの分離レベルは重要です。ただし、SERIALIZABLE分離レベルでは、アプリケーションが「このトランザクションのアクセスをシリアル化できません」というエラーの有無を確認する必要があり、多数の同時トランザクションが更新のために同じデータにアクセスする環境では、スループットはかなり低下する可能性があります。

6.6.3項で説明したように、READ COMMITTEDまたはSERIALIZABLEトランザクションのいずれでも読込みは書込みをブロックしません。

表6-6に、READ COMMITTEDトランザクションとSERIALIZABLEトランザクションの類似点および相違点の概要を示します。

表6-6 コミット読込みトランザクションとシリアライズ可能トランザクションの比較

操作 コミット読込み シリアライズ可能

内容を保証しない書込み

可能性なし

可能性なし

内容を保証しない読込み

可能性なし

可能性なし

反復不能な読込み

可能性あり

可能性なし

仮読込み

可能性あり

可能性なし

ANSI/ISO SQL 92への準拠

あり

あり

スナップショット読込み時間

トランザクション

トランザクション集合の整合性

文レベル

トランザクション・レベル

行レベル・ロック

あり

あり

読込みが書込みをブロック

なし

なし

書込みが読込みをブロック

なし

なし

異なる行の書込みが書込みをブロック

なし

なし

同じ行の書込みが書込みをブロック

あり

あり

阻止しているトランザクションの待機

あり

あり

エラー「このトランザクションのアクセスをシリアル化できません」の発生する可能性

なし

あり

阻止しているトランザクションの終了後のエラー

なし

なし

阻止しているトランザクションのコミット後のエラー

なし

あり


6.7 非ブロック化およびブロック化のDDL文

DDL文の非ブロック化とブロック化の違いは、表または索引のどちらか(表によって異なる)を変更するDDL文についてのみ重要です。

オブジェクトXに影響するDDL文をセッションが発行すると、Xを参照するすべての同時DML文がコミットされるかロールバックされるまで、セッションは待機します。

セッションが待機している間、同時セッションが新規のDML文を発行する可能性があります。DDL文が非ブロック化の場合、新規のDML文はただちに実行されます。DDL文がブロック化の場合、新規のDML文はDDL文の完了後に実行され、成功するかエラーが発生します。

DDL_LOCK_TIMOUTパラメータはブロック化DDL文に影響します(が、非ブロック化DDL文には影響しません)。したがって、ブロック化DDL文の完了にはエラーORA-00054 (「リソース・ビジー。NOWAITが指定されているか、タイムアウトしました」)が伴う場合があります。DDL_LOCK_TIMOUTパラメータの詳細は、『Oracle Databaseリファレンス』を参照してください。

表のパーティションに適用されるDDL文は、そのパーティションについてはブロック化、同じ表の他のパーティションについては非ブロック化です。

非ブロック化DDL文の一覧については、『Oracle Database SQL言語リファレンス』を参照してください。


注意:

サプリメンタル・ロギングがデータベース・レベルで(マルチテナント・コンテナ・データベースまたはプラガブル・データベースについて)有効になっている場合、データベースでは非ブロック化DDL文がブロック化DDL文のように扱われます。サプリメンタル・ロギングの有効化および無効化の詳細は、『Oracle Database SQL言語リファレンス』を参照してください。


注意:

自律型トランザクションでは非ブロック化DDL文を発行しないでください。自律型トランザクションの詳細は、「自律型トランザクション」を参照してください。

6.8 自律型トランザクション


注意:

自律型トランザクションでは非ブロック化DDL文を発行しないでください。非ブロック化DDL文の詳細は、「非ブロック化およびブロック化のDDL文」を参照してください。

自律型トランザクション(AT)は、別のトランザクション(メイン・トランザクション(MT))によって開始される独立したトランザクションです。自律型トランザクションを使用すると、メイン・トランザクションを停止して、SQL操作を実行し、そのSQL操作をコミットまたはロールバックした後でメイン・トランザクションを再開できます。

たとえば、株式売買トランザクションでは、売買が成功するかどうかに関係なく、顧客情報のコミットが必要な場合があります。または、トランザクションがロールバックされた場合でも、エラー・メッセージをデバッグ表にログする必要がある場合もあります。自律型トランザクションを使用すると、これらのタスクを実行できます。

自律型トランザクションは自律型スコープ内、つまり、自律型ルーチン(AUTONOMOUS_TRANSACTIONプラグマでマークされるルーチン)の範囲内で実行されます。このコンテキストでは、ルーチンは次のいずれかになります。

  • スキーマレベル(ネストしていない)の無名PL/SQLブロック

  • スタンドアロン・サブプログラム、パッケージ・サブプログラムまたはネストしたサブプログラム

  • ADTのメソッド

  • 非複合トリガー

自律型ルーチンで複数の自律型トランザクションをコミットできます。

図6-3に、メイン・トランザクション(proc1)から自律型ルーチン(proc2)へ制御が行き来する様子を示します。自律型ルーチンは、制御がメイン・トランザクションに戻る前に2つのトランザクション(AT1およびAT2)をコミットします。

図6-3 トランザクション制御フロー

図6-3の説明が続きます
「図6-3 トランザクション制御フロー」の説明

自律型トランザクションの実行可能セクションに入ると、メイン・トランザクションが停止します。自律型トランザクションを終了すると、メイン・トランザクションが再開します。COMMITおよびROLLBACKによって、アクティブな自律型トランザクションは終了しますが、自律型トランザクションは終了しません。図6-3に示すとおり、1つのトランザクションが終了すると、次のSQL文が別のトランザクションを開始します。

自律型トランザクションの特長をさらに示します。

  • 自律型トランザクションが加える変更は、メイン・トランザクションの状態または最終的な処理には依存しません。次に例を示します。

    • 自律型トランザクションは、メイン・トランザクションによって加えられた変更を認識しません。

    • 自律型トランザクションがコミットまたはロールバックしても、メイン・トランザクションの結果には影響しません。

  • 自律型トランザクションが加える変更は、そのトランザクションがコミットした直後に他のトランザクションで参照できます。このため、メイン・トランザクションがコミットするまで待機しなくても、ユーザーは更新された情報にアクセスできます。

  • 自律型トランザクションは他の自律型トランザクションを開始できます。

図6-4に、自律型トランザクションが従う実行順序の例を示します。

図6-4 自律型トランザクションの順序

図6-4の説明が続きます
「図6-4 自律型トランザクションの順序」の説明

内容は次のとおりです。


参照:

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

6.8.1 自律型トランザクションの例

この項では、自律型トランザクションの例を示します。

内容は次のとおりです。

例に示すとおり、自律型トランザクションおよびメイン・トランザクションを使用する場合は、4種類の結果が考えられます(表6-7を参照)。自律型トランザクションの結果とメイン・トランザクションの結果の間に依存性はありません。

表6-7 トランザクションの結果

自律型トランザクション メイン・トランザクション

コミット

コミット

コミット

ロールバック

ロールバック

コミット

ロールバック

ロールバック


6.8.1.1 商品の注文

図6-5に、顧客がある商品を注文する例を示します。購買契約が成立しなくても、その顧客の情報(名前、住所、電話番号など)は顧客情報表にコミットされます。

図6-5 例: 購買指示

図6-5の説明が続きます
「図6-5 例: 購買指示」の説明

6.8.1.2 銀行口座からの出金処理

この例では、顧客は銀行口座から払戻しを実行しようとします。この処理で、メイン・トランザクションは2つの自律型トランザクション・スコープ(ATスコープ1またはATスコープ2)のいずれかを起動します。

このトランザクションで考えられる使用例を次に示します。

6.8.1.2.1 使用例1: 十分な預金残高がある場合

払戻しに十分な預金残高があり、銀行が払戻しに応じます。(図6-6を参照)。

図6-6 預金払戻し - 十分な預金残高がある場合

図6-6の説明は次にあります。
「図6-6 預金払戻し - 十分な預金残高がある場合」の説明

6.8.1.2.2 使用例2: 貸越し保護があり預金残高が不十分な場合

払戻しに十分な預金残高はありませんが、この顧客には貸越し保護があるため、銀行が払戻しに応じます(図6-7を参照)。

図6-7 預金払戻し - 貸越し保護があり預金残高が不十分な場合

図6-7の説明が続きます
「図6-7 預金払戻し - 貸越し保護があり預金残高が不十分な場合」の説明

6.8.1.2.3 使用例3: 貸越し保護がなく預金残高が不十分な場合

払戻しに十分な預金残高はなく、この顧客には貸越し保護もありません。したがって、銀行は払戻しを差し止めます(図6-8を参照)。

図6-8 預金払戻し - 貸越し保護がなく預金残高が不十分な場合

図6-8の説明が続きます
「図6-8 預金払戻し - 貸越し保護がなく預金残高が不十分な場合」の説明

6.8.2 自律型ルーチンの宣言

自律型ルーチンを宣言するには、PRAGMA AUTONOMOUS_TRANSACTIONを使用します。これは、PL/SQLコンパイラに対して、ルーチンを自律型としてマークするように指示します。

例6-3では、ファンクションbalanceが自律型です。

例6-3 パッケージ・サブプログラムの自律型としてのマーク

-- Create table for package to use:
 
DROP TABLE accounts;
CREATE TABLE accounts (account INTEGER, balance REAL);
 
-- Create package:
 
CREATE OR REPLACE PACKAGE banking AS
  FUNCTION balance (acct_id INTEGER) RETURN REAL;
  -- Additional functions and packages
END banking;
/
CREATE OR REPLACE PACKAGE BODY banking AS
  FUNCTION balance (acct_id INTEGER) RETURN REAL IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    my_bal  REAL;
  BEGIN
    SELECT balance INTO my_bal FROM accounts WHERE account=acct_id;
    RETURN my_bal;
  END;
  -- Additional functions and packages
END banking;
/

参照:

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

6.9 記憶域割当てエラー後の実行の再開

長時間にわたって実行されるトランザクションが記憶域割当てエラー状態によって中断されたときに、アプリケーションによって問題が発生した文を一時停止し、問題を修正した後でその文を再開することが可能です。この機能を、再開可能記憶域割当てといいます。この機能によって、時間がかかるロールバックを回避できます。また、操作を小さく分割したり、処理過程を追跡するコードを作成する必要がなくなります。


参照:

再開可能領域割当ての詳細は、『Oracle Database管理者ガイド』を参照してください。

内容は次のとおりです。

6.9.1 再開可能記憶域割当てを含む操作

問合せ、DML操作および特定のDDL操作には、次のエラーの後で再開可能な記憶域割当てが含まれます。

  • ORA-01653などの領域不足エラー

  • ORA-01628などの領域制限エラー

  • ORA-01536などの領域割当てエラー

再開可能記憶域割当ては、操作がSQL文によって直接実行されているか、またはSQL*Loader、ストアド・サブプログラム、無名PL/SQLブロック、OCIStmtExecuteなどのOCIコール内で実行されている場合に適用されます。

ディクショナリ管理された表領域では、ロールバック・セグメントの上限に達するか、エクステントの最大数に達した場合、索引または表の作成操作を再開できません。ローカル管理表領域および自動UNDO管理を、再開可能記憶域割当てと組み合せて使用してください。

6.9.2 一時停止された記憶域割当ての処理

記憶域割当てエラーのためにアプリケーション内の文が一時停止された場合、アプリケーションはエラー・コードを受信しません。このため、アプリケーションでAFTER SUSPENDトリガーを使用するか、またはDBAが一時停止された文を定期的に確認する必要があります。

問題が(通常DBAによって)修正されると、一時停止された文は自動的に実行を再開します。タイムアウト周期が終了するまでに問題が修正されなかった場合は、その文によってSERVERERROR例外が発生します。

内容は次のとおりです。

6.9.2.1 アプリケーションのAFTER SUSPENDトリガーの使用

アプリケーションで、AFTER SUSPENDトリガーはDBMS_RESUMABLEパッケージ内のサブプログラムを起動して、問題に関する情報を取得できます(『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照)。次にトリガーはオペレータに電子メールなどを使用して情報を送信できます。

トリガー自体の中で領域不足エラーが発生する可能性を減らすには、トリガーを自律型トランザクションとして宣言します。自律型トランザクションとして、トリガーはSYSTEM表領域内のロールバック・セグメントを使用します。一時停止された文が保持するロックによってトリガーにデッドロック状態が発生した場合、そのトリガーは終了され、アプリケーションは文の一時停止が発生しなかった場合の本来のエラー状態を受信します。トリガーによって領域不足状態が発生した場合、そのトリガーおよび一時停止された文は両方ともロールバックされます。ロールバックを回避するには、トリガー内の例外ハンドラを使用し、文が再開されるまで待ちます。

トリガーの一般的な情報は、『Oracle Database PL/SQL言語リファレンス』を参照してください。

例6-4のトリガーは、データベース内の記憶域エラーを処理します。いくつかのエラーでは、このトリガーは文を終了し、エラーに関する警告を電子メールを使用してDBAに通知します。他の一時的なエラーでは、8時間以内に記憶域の問題が解決していることを想定して、8時間後に文を再開するように指定しています。この例を実行するには、SYSDBAとしてデータベースに接続する必要があります。

例6-4 AFTER SUSPENDトリガーによる一時停止された記憶域割当ての処理

-- Create table used by trigger body
 
DROP TABLE rbs_error;
CREATE TABLE rbs_error (
  SQL_TEXT VARCHAR2(64),
  ERROR_MSG VARCHAR2(64),
  SUSPEND_TIME VARCHAR2(64)
);
 
-- Resumable Storage Allocation

CREATE OR REPLACE TRIGGER suspend_example
  AFTER SUSPEND
  ON DATABASE
DECLARE
  cur_sid           NUMBER;
  cur_inst          NUMBER;
  err_type          VARCHAR2(64);
  object_owner      VARCHAR2(64);
  object_type       VARCHAR2(64);
  table_space_name  VARCHAR2(64);
  object_name       VARCHAR2(64);
  sub_object_name   VARCHAR2(64);
  msg_body          VARCHAR2(64);
  ret_value         BOOLEAN;
  error_txt         VARCHAR2(64);
  mail_conn         UTL_SMTP.CONNECTION;
BEGIN
 SELECT DISTINCT(SID) INTO cur_sid FROM V$MYSTAT;
 cur_inst := USERENV('instance');
 ret_value := DBMS_RESUMABLE.SPACE_ERROR_INFO
              (err_type,
              object_owner,
              object_type,
              table_space_name,
              object_name,
              sub_object_name);
 IF object_type = 'ROLLBACK SEGMENT' THEN
   INSERT INTO rbs_error
     (SELECT SQL_TEXT, ERROR_MSG, SUSPEND_TIME
      FROM DBA_RESUMABLE
      WHERE SESSION_ID = cur_sid
      AND INSTANCE_ID = cur_inst);

    SELECT ERROR_MSG INTO error_txt
    FROM DBA_RESUMABLE
    WHERE SESSION_ID = cur_sid
    AND INSTANCE_ID = cur_inst;

    msg_body :=
     'Space error occurred: Space limit reached for rollback segment '
     || object_name || ' on ' || to_char(SYSDATE, 'Month dd, YYYY, HH:MIam')
     || '. Error message was: ' || error_txt;

    mail_conn := UTL_SMTP.OPEN_CONNECTION('localhost', 25);
    UTL_SMTP.HELO(mail_conn, 'localhost');
    UTL_SMTP.MAIL(mail_conn, 'sender@localhost');
    UTL_SMTP.RCPT(mail_conn, 'recipient@localhost');
    UTL_SMTP.DATA(mail_conn, msg_body);
    UTL_SMTP.QUIT(mail_conn);
    DBMS_RESUMABLE.ABORT(cur_sid);
  ELSE
    DBMS_RESUMABLE.SET_TIMEOUT(3600*8);
  END IF;
  COMMIT;
END;
/

6.9.2.2 一時停止された文の確認

アプリケーションがAFTER SUSPENDトリガーを使用しない場合、DBAは静的データ・ディクショナリ・ビューDBA_RESUMABLE(『Oracle Databaseリファレンス』を参照)を使用して、一時停止された文を定期的に確認する必要があります。

DBAは動的パフォーマンス・ビューV$_SESSION_WAIT(『Oracle Databaseリファレンス』参照)から追加の情報を取得することができます。