| Oracle Database アプリケーション開発者ガイド-基礎編 10gリリース2(10.2) B19248-02 |
|
この章では、Oracle DatabaseでのSQL文の処理方法について説明します。この章を読む前に、『Oracle Database概要』の「SQLの処理」の項を読んでおいてください。
内容は次のとおりです。
この項の内容は次のとおりです。
一般に、Oracle Databaseのプログラム・インタフェースを使用するアプリケーション設計者のみが、どのタイプのアクションを1つのトランザクションとしてグループ化するかを考慮します。トランザクションのグループ化方法を決定する際は、次の規則に従います。
たとえば、口座間で預金を移動できるWebアプリケーションを作成すると仮定します。このトランザクションには、一方の口座の借方への記帳(1つのSQL文で実行)ともう一方の口座の貸方への記帳(もう1つのSQL文で実行)が含まれています。2つの文で1つの作業単位となり、両方が成立するか両方が不成立かのいずれかです。つまり、借方を伴わない貸方はコミットしません。一方の口座への新規預金など、関連のないアクションは、同じトランザクションに含めません。
アプリケーション開発者は、パフォーマンスが改善可能か検討する必要があります。アプリケーションの設計および作成では、次のパフォーマンス要件を考慮する必要があります。
USE ROLLBACK SEGMENT句を指定したSET TRANSACTIONコマンドを使用して、トランザクションをロールバック・セグメントに明示的に割り当てます。これによって、システムのパフォーマンスを低下させる可能性のある追加エクステントの動的割当ての必要がなくなります。この句は、ロールバック・セグメントをUNDOに使用する場合にのみ適用され、有効であることに注意してください。自動UNDO管理を使用する場合、Oracle Databaseではこの句は無視されます。
ANALYZEコマンドを使用して統計を収集し、Oracle DatabaseにおいてSQL文を最適化するコストベースの方法を使用可能にします。オプティマイザには、必要に応じてヒントを追加できます。
DBMS_APPLICATION_INFO.SET_ACTIONプロシージャをコールし、トランザクションを登録して名前を付け、アプリケーション全体にわたるパフォーマンス測定で使用できるようにします。後でシステムをチューニングする際に、どのトランザクションが一番多くシステム・リソースを必要とするかがわかるように、トランザクションで実行するアクションのタイプを指定する必要があります。
MAX_OPEN_CURSORSを使用してカーソル数を増加させ、プリコンパイラ・プログラムで解析頻度を削減し、パフォーマンスを改善します。
ISOLATION LEVELをSERIALIZABLEに指定したSET TRANSACTIONコマンドを使用して、ANSI/ISOシリアライズ可能トランザクションを生成します。
関連項目
トランザクションをコミットするには、COMMIT文を使用します。次の2つの文は同等で、現行のトランザクションをコミットします。
COMMIT WORK; COMMIT;
COMMIT文には、コミットされるトランザクションに関する情報を示すコメントを指定したCOMMENTパラメータを含めることができます。このオプションは、分散トランザクションをコミットするときに、トランザクションの起点に関する情報を含める場合に有効です。
COMMIT COMMENT 'Dallas/Accts_pay/Trans_type 10B';
トランザクションがデータベースを更新するとき、この更新に対応するREDOエントリが生成されます。トランザクションが完了するまで、このREDOはOracle Databaseによりメモリにバッファリングされます。トランザクションがコミットされると、それに応じてログ・ライター(LGWR)・プロセスがトランザクション内のすべての変更の蓄積されたREDOをディスクに書き込みます。デフォルトでは、REDOは、コールがクライアントに戻される前に、Oracle Databaseによりディスクに書き込まれます。この動作のためにアプリケーションはREDOがディスクに永続的に書き込まれるのを待機する必要があり、コミットに待機時間が発生します。
高いトランザクション・スループットを必要とするアプリケーションを作成する場合を考えます。コミットでの待機時間を短くするためにコミットの永続性を放棄してもよい場合は、デフォルトのCOMMITオプションを変更して、Oracle DatabaseでデータがオンラインREDOログへ書き込まれるのをアプリケーションが待機しなくて済むようにできます。
Oracle Databaseでは、アプリケーションのニーズに応じてコミットREDOの処理を変更できます。コミット動作は次の場所で変更できます。
COMMIT文のオプションを使用すると、初期化パラメータの現行の設定がオーバーライドされます。表2-1に、このどちらかで設定可能な、REDO永続オプションの説明を示します。
次の例は、初期化パラメータ・ファイルでコミット動作をBATCHおよびNOWAITに設定する方法を示しています。
COMMIT_WRITE = BATCH, NOWAIT
コミット動作は、次の例のようにALTER SYSTEMを実行することにより、システム・レベルで変更できます。
ALTER SYSTEM SET COMMIT_WRITE = BATCH, NOWAIT
初期化パラメータの設定後、オプションを指定していないCOMMIT文は、パラメータのオプションの設定に準拠するようになります。また、次の例のように、COMMIT文で直接オプションを指定することにより、現行の初期化パラメータの設定をオーバーライドできます。
COMMIT WRITE BATCH NOWAIT
どちらの例でも、コミット時に即時にログ・ライターがREDOをオンラインREDOログへ書き込む必要がないこと、および、REDOがディスクへ書き込まれるのを待機しないことが、アプリケーションで指定されています。
OCIを使用するアプリケーションの場合、アプリケーション内でOCITransCommit()ファンクションに次のフラグを設定することにより、REDO動作を変更できます。
NOWAITおよびBATCHオプションの指定は、影響の反映されやすい小さいウィンドウの場合、Oracle Databaseでトランザクションがロールバックされてもアプリケーションではコミット済として表示される可能性がありますので注意してください。アプリケーションは、次のような状況に対応できる必要があります。
トランザクションの全体または一部を(セーブポイントまで)ロールバックするには、ROLLBACK文を使用します。たとえば、次の文はどちらも、現行のトランザクション全体をロールバックします。
ROLLBACK WORK; ROLLBACK;
ROLLBACKコマンドのWORKオプションには、何も機能はありません。
現行のトランザクション内に定義されたセーブポイントまでロールバックするには、ROLLBACKコマンドのTOオプションを使用する必要があります。たとえば、次の文はいずれもPOINT1という名前のセーブポイントまで現行のトランザクションをロールバックします。
SAVEPOINT Point1; ... ROLLBACK TO SAVEPOINT Point1; ROLLBACK TO Point1;
トランザクション内にセーブポイントを定義するには、SAVEPOINTコマンドを使用します。次の文は、現行のトランザクション内にADD_EMP1という名前のセーブポイントを作成します。
SAVEPOINT Add_emp1;
前のセーブポイントと同じ識別子で2番目のセーブポイントを作成すると、前のセーブポイントが消去されます。セーブポイントを作成した後は、そのセーブポイントまでロールバックできます。
セッション当たりのアクティブ・セーブポイントの数に制限はありません。アクティブ・セーブポイントとは、最後のコミットまたは最後のロールバック以降に指定されたセーブポイントのことです。
表2-2に、トランザクション内でのCOMMIT文、SAVEPOINT文およびROLLBACK文の一連のSQL文を示し、それらの使用方法を具体的に説明します。
デフォルトでは、Oracle Databaseの一貫性モデルは、文レベルの読取り一貫性は保証しますが、トランザクション・レベルの読取り一貫性(反復可能読取り)は保証しません。トランザクション・レベルの読取り一貫性が必要で、トランザクションが更新を必要としない場合は、読取り専用トランザクションを指定できます。トランザクションを読取り専用に指定すると、どのデータベース表に対しても必要な数の問合せを実行でき、その読取り専用トランザクション内の各問合せの結果は、特定の1つの時点における一貫性が保たれています。
読取り専用トランザクションでは、トランザクション・レベルの読取り一貫性を保持するためにデータ・ロックを加えることはありません。文レベルの読取り一貫性のために使用されるマルチバージョンの一貫性モデルが、トランザクション・レベルの読取り一貫性を保持するために使用されます。すべての問合せは、読取り専用トランザクションが開始したときに決定されたシステム変更番号(SCN)の情報を戻します。データがロックされないため、読取り専用トランザクションが問い合せているデータを他のトランザクションが同時に問い合せたり更新することができます。
実行時間の長い問合せでは、読取り一貫性操作に必要なUNDO情報が無効になったためにエラーが発生することがあります。これは、アクティブなトランザクションによって、コミット済みUNDOブロックが上書きされた場合に起こります。自動UNDO管理を行うと、UNDO領域を再利用できる時点、つまり、UNDO情報の保存期間を明示的に制御できます。データベース管理者は、パラメータUNDO_RETENTIONを使用して保存期間を指定できます。
たとえば、UNDO_RETENTIONを30分に設定すると、システムのすべてのコミット済UNDO情報は、最低30分間保持されます。この設定により、通常の環境下であれば、実行時間が30分以内の問合せでOERエラー「スナップショットが古すぎます。」が発生することはなくなります。
読取り専用トランザクションは、READ ONLYオプションを含むSET TRANSACTION文で始まります。次に例を示します。
SET TRANSACTION READ ONLY;
SET TRANSACTION文は、新しいトランザクションの最初の文である必要があります。任意のDML文(問合せを含む)または他のDDL以外の文(たとえば、SET ROLE)がSET TRANSACTION READ ONLY文より前に指定されていると、エラーが戻されます。SET TRANSACTION READ ONLY文が正常に実行されると、そのトランザクションでは、SELECT文(FOR UPDATE 句なし)、COMMIT文、ROLLBACK文またはDML以外の文(たとえば、SET ROLE、ALTER SYSTEM、LOCK TABLE)のみが使用できます。これら以外の文では、エラーが戻されます。COMMIT文、ROLLBACK文またはDDL文によって、読取り専用トランザクションは終了します(DDL文によって、読取り専用トランザクションは暗黙的にコミットされ、それ自身のトランザクション内でコミットされます)。
PL/SQLでは、1行のみ戻す問合せも含めて、すべてのSQLデータ操作文に対してカーソルを暗黙的に宣言します。複数行を戻す問合せの場合、カーソルを明示的に宣言して行を別々に処理できます。
カーソルは、特定のプライベートSQL領域へのハンドルです。カーソルは、特定のプライベートSQL領域の名前と考えることができます。PL/SQLカーソル変数を使用すると、ストアド・プロシージャから複数の行を取得できます。カーソル変数を使用すると、3GLアプリケーション内のパラメータとしてカーソルを渡すことができます。カーソル変数の詳細は、『PL/SQLユーザーズ・ガイドおよびリファレンス』を参照してください。
ほとんどのOracle Databaseユーザーは、データベース・ユーティリティの自動カーソル処理を使用しますが、アプリケーション設計者は、プログラム・インタフェースを使用した方がカーソルを制御しやすくなります。アプリケーション開発では、カーソルはプログラムで使用できる名前付きのリソースであり、アプリケーションに埋め込まれたSQL文の解析に特に有効です。
1つのセッションで同時にオープンできるカーソルの総数に絶対的な制限はありませんが、次の2つの制約があります。
INIT.ORAなど)内のOPEN_CURSORS初期化パラメータによって設定されます。 プリコンパイラ・プログラムに対して明示的にカーソルを作成すると、アプリケーションのチューニング時に有効です。たとえば、カーソル数を増加させると、解析頻度が削減されパフォーマンスが改善されることがよくあります。ある時点で必要となるカーソル数がわかっている場合、その数のカーソルを必ず同時にオープンできるように宣言できます。
各ステージの実行後、カーソルはそのSQL文に関する十分な情報を保持しているため、同じカーソルに他のSQL文が対応付けられていないかぎり、最初から実行しなおさなくてもその文を再実行できます。文は、解析ステージを含めなくても再実行できます。
カーソルをいくつかオープンすることによって、いくつかのSQL文の解析済表現を保存できます。同じSQL文を繰り返し実行する場合、記述手順、定義手順、バインド手順または実行手順から開始することができ、カーソルのオープンおよび解析を繰り返す必要がなくなります。
あるカーソルのパフォーマンス特性を理解するために、DBAは、V$SQLカタログ・ビューを使用して、そのカーソルが表現する問合せのテキストを取得できます。元の問合せに対するEXPLAIN PLANの結果は、問合せの実際の処理方法とは異なる場合があります。そのためDBAは、V$SQL_PLAN、V$SQL_PLAN_STATISTICSおよびV$SQL_PLAN_STATISTICS_ALLカタログ・ビューを調べることによってより正確な情報を取得できます。
V$SQL_PLANビューには、ライブラリ・キャッシュにロードされた各子カーソルの実行計画情報が含まれています。
V$SQL_PLAN_STATISTICSビューには、各子カーソルの行ソース・レベルでの実行統計が含まれています。
V$SQL_PLAN_STATISTICS_ALLビューには、(ソートまたはハッシュ結合に)SQLメモリーを使用する行ソースのメモリー使用量統計が含まれています。このビューは、V$SQL_PLAN内の情報をV$SQL_PLAN_STATISTICSおよびV$SQL_WORKAREAからの実行統計と連結します。カーソルのクローズとは、関連付けられているプライベート領域に現在ある情報が失われ、そのメモリーの割当てが解除されることです。カーソルは、一度オープンされると次のいずれかが発生するまでクローズされません。
カーソルを取り消すと、現在のフェッチからリソースが解放されます。対応付けられたプライベート領域に現在ある情報は失われますが、カーソルはオープンしたままになり、解析され、バインド変数に対応付けられます。
Oracle Databaseでは、データの同時実行性、整合性、および文レベル読取り一貫性を保持するために、常に必要なロックが実行されます。これらのデフォルト・ロック・メカニズムはオーバーライドできます。たとえば、次のような場合に、Oracle Databaseのデフォルト・ロックのオーバーライドが必要になります。
自動ロック・メカニズムは、トランザクション・レベルでオーバーライドできます。次のSQLコマンドを含むトランザクションは、Oracle Databaseのデフォルト・ロックをオーバーライドします。
これらの文によって取得されるロックは、トランザクションのコミット後またはロールバック後に解除されます。
次の項では、Oracle Databaseのデフォルト・ロックをオーバーライドするために使用できる各オプションを説明します。DML_LOCKS初期化パラメータによって、使用可能なDMLロックの最大数が決定されます。
通常、デフォルト値には十分な値が設定されていますが、手動ロックを追加して使用する際に、この値を大きくする必要がある場合があります。
LOCK TABLE文が実行されると、トランザクションは指定された表ロックを明示的に取得します。LOCK TABLE文は、デフォルト・ロックを手動でオーバーライドします。ビューに対してLOCK TABLE文が発行されると、基礎となる実表がロックされます。次の文は、EMP_TAB表およびDEPT_TAB表を含むトランザクションにかわって、この2つの表に対する排他表ロックを取得します。
LOCK TABLE Emp_tab, Dept_tab IN EXCLUSIVE MODE NOWAIT;
ロック・モードが同じ場合は、ロックする表またはビューを複数指定できます。ただし、1つのLOCK TABLE文に指定できるロック・モードは1つのみです。
また、ロックの取得を待つか待たないかも指示できます。NOWAITオプションを指定すると、表ロックがすぐに使用可能である場合にのみ表ロックを取得します。すぐに使用可能でない場合は、その時点ではロックが使用できないことを示すエラーが戻されます。その場合は、後でリソースに対するロックを再試行できます。NOWAITを指定しないと、要求した表ロックが取得されるまで、トランザクションは処理を続行しません。表ロックに対する待ち時間が長すぎる場合は、そのロック操作を取り消して、後で再試行できます。このロジックは、アプリケーション内に作成できます。
LOCK TABLE Emp_tab IN ROW SHARE MODE; LOCK TABLE Emp_tab IN ROW EXCLUSIVE MODE;
行共有(ROW SHARE)表ロックおよび行排他(ROW EXCLUSIVE)表ロックは、最も高い同時実行性を提供します。次のような場合に使用します。
LOCK TABLE Emp_tab IN SHARE MODE;
共有表(SHARE)ロックは非常に制限の多いデータ・ロックです。次のような場合に使用します。
SHAREロックを保持するすべてのトランザクションがコミットまたはロールバックするまで、ロックされている表を更新しようとする他のトランザクションを阻止できる場合。
SHARE表ロックを取得でき、またトランザクション・レベルの読取り一貫性のオプションを使用できる場合。たとえば、2つの表EMP_TABおよびBUDGET_TABには、第3の表DEPT_TABの一貫した一連のデータが必要であると仮定します。特定の部門番号に関して、2つの表の情報を更新し、この2つのトランザクションの間に新しいメンバーが部門に追加されないように保証するものとします。
この使用例はきわめてまれな場合ですが、次の例で示すように、SHARE MODEでDEPT_TAB表をロックすることによって対処できます。DEPT_TAB表の更新はまれなため、ロックしても他の多くのトランザクションの待ち時間が長くなることはありません。
LOCK TABLE Dept_tab IN SHARE MODE; UPDATE Emp_tab SET sal = sal * 1.1 WHERE deptno IN (SELECT deptno FROM Dept_tab WHERE loc = 'DALLAS'); UPDATE Budget_tab SET Totsal = Totsal * 1.1 WHERE Deptno IN (SELECT Deptno FROM Dept_tab WHERE Loc = 'DALLAS'); COMMIT; /* This releases the lock */
LOCK TABLE Emp_tab IN SHARE ROW EXCLUSIVE MODE;
共有行排他(SHARE ROW EXCLUSIVE)表ロックは、次のような場合に使用します。
SELECT... FOR UPDATEを使用した)明示的な行ロックの可能性を考慮していない場合。ロック中のトランザクション内のUPDATEおよびINSERT文が待機させられ、デッドロックが発生する可能性があります。
LOCK TABLE Emp_tab IN EXCLUSIVE MODE;
排他(EXCLUSIVE)表ロックは、次のような場合に使用します。
自スキーマ内の表に対しては、どの種類の表ロックでも自動的に取得できます。他スキーマ内の表に対して表ロックを取得するには、LOCK ANY TABLEシステム権限またはその表に対する(SELECTやUPDATEなどの)オブジェクト権限が必要です。
Oracle Databaseに表ロック制御を任せると、アプリケーションに必要なプログラム・ロジックが少なくて済みます。ただし、表ロックを自分で管理する場合より制御範囲が小さくなります。
SET TRANSACTION ISOLATION LEVEL SERIALIZABLEコマンドまたはALTER SESSION ISOLATION LEVEL SERIALIZABLEコマンドを発行すると、基礎となるロック・プロトコルを変更しなくても、ANSIのシリアライズ可能性を維持できます。この手法によって、ANSIのシリアライズ可能性を維持しながら表へ同時アクセスできます。表をロックすることによって、同時実行性が大幅に減少します。
インスタンスが停止されているときにのみ、これらのパラメータの設定を変更可能です。複数インスタンスが単一データベースにアクセスする場合は、すべてのインスタンスでこれらのパラメータの設定を同じにする必要があります。
FOR UPDATE句を含むSELECT文を使用すると、デフォルト・ロックをオーバーライドできます。この文は、選択されている行がその後の文で更新されることを想定し、(UPDATE文のように)選択された行に対する明示的な行ロックを取得します。
SELECT... FOR UPDATE文を使用すると、実際にその行を変更せずに行をロックできます。たとえば、第9章「トリガーのコーディング」では、いくつかのトリガーで参照整合性を実装する方法を示しています。EMP_DEPT_CHECKトリガー(12-41ページの「子表に対する外部キー・トリガー」を参照)では、参照される親キー値を含む行が、トランザクションの存続中は同じ値のままであることを保証するためにロックされます。親キーが更新または削除された場合、参照整合性違反になります。
SELECT... FOR UPDATE 文は、ユーザーが1行以上の特定行のフィールドを変更できるような対話型プログラムでよく使用されます(時間がかかることがあります)。行を更新している対話型プログラム・ユーザーが常に1ユーザーのみであるように、行がロックされます。
カーソル定義にSELECT... FOR UPDATE文が使用される場合は、カーソルがオープンされるとき(最初のフェッチの前)に結果セット内の行がロックされます。行は、カーソルからフェッチされるときに、個別にロックされるわけではありません。カーソルをオープンしたトランザクションがコミットまたはロールバックされたときにのみ、ロックが解除されます。カーソルがクローズされるときには、ロックは解除されません。
SELECT... FOR UPDATE文の結果セット内の各行は、個別にロックされます。SELECT... FOR UPDATE文は、競合している行ロックを他のトランザクションが解除するまで待機します。 したがって、SELECT... FOR UPDATE 文が表の行を多数ロックし、表に対して非常に多くの更新アクティビティが発生する場合は、EXCLUSIVE表ロックを取得する方がパフォーマンスが改善する場合があります。
SELECT... FOR UPDATEを使用して行ロックを取得する場合は、NOWAITオプションを指定してロック取得時の待機を避けることができます。すぐにロックが取得できない場合は、この時点ではロックできないことを示すエラーが戻されます。その行のロックは、後で再試行できます。
デフォルトでは、要求された行ロックが取得されるまでトランザクションは待機します。行ロックに対する待ち時間が長すぎる場合は、そのロック操作を取り消して後で再試行するロジックを、アプリケーションに作成できます。
DBMS_LOCKパッケージをコールすることで、アプリケーションでOracle Lock Managementサービスを使用できます。特定のモードのロックを要求し、同一のインスタンスまたは別のインスタンスの別のプロシージャで認識できる一意の名前を付け、ロック・モードを変更し、解除することができます。確保されるユーザー・ロックはOracle Databaseロックと同一であるため、デッドロックの検出などのデータベース・ロックのすべての機能を持っています。分散トランザクションで使用されるユーザー・ロックは、COMMITと同時に解除されるようになっていることを確認してください。解除されないと、検出されないデッドロックが発生する可能性があります。
次のような場合、ユーザー・ロックを使用します。
次のPro*COBOLプリコンパイラの例は、複数のユーザーが1つの装置にアクセスする必要がある場合に、競合が発生しないように保証するためのロックの使用方法を示しています。
***************************************************************** * 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. The one printer is opened by all * * the cashiers to avoid the overhead of opening and closing it * * for every check. This means that lines of output from multiple* * cashiers could become interleaved if we don't 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. * We 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.
表2-3で、インスタンスで処理中のトランザクションに対するロック情報を表示するためのOracle Databaseの機能を説明します。
Oracle Databaseは、デフォルトでは、同時に実行されるトランザクションの同じ表および同じデータ・ブロック内での行の修正、追加または削除を許可しています。あるトランザクションによって行われた変更は、その変更を行ったトランザクションがコミットされるまで、別の同時トランザクションでは参照できません。
トランザクションAが、(DML文またはSELECT... FOR UPDATE文を使用して)別のトランザクションBによってロックされている行を更新または削除しようとすると、トランザクションAのDMLコマンドは、トランザクションBがコミットまたはロールバックされるまでブロックされます。トランザクションBがコミットされると、トランザクションAは、トランザクションBがそのデータベースに対して行った変更を参照できます。
この同時実行性モデルはほとんどのアプリケーションに適しています。これは、より高度な同時実行性が提供されて、パフォーマンスが改善されるためです。ただし、まれにシリアライズ可能なトランザクションが必要な場合もあります。シリアライズ可能トランザクションは、同時にではなく、一度に1トランザクションずつ(シリアルで)実行しているように見える方法で実行する必要があります。シリアル・モードで実行中の同時トランザクションでは、それらのトランザクションが1つずつ順次実行された場合に可能となるデータベースの変更のみが可能です。
図2-1は、シリアライズ可能トランザクション(B)と、別のトランザクション(A)との相互作用を示しています。
ANSI/ISO SQL規格SQL92は、考えられる3種類のトランザクションの相互作用、およびそれらの相互作用に対する保護を強化する4レベルの分離を定義しています。表2-4に、これらの相互作用および分離レベルの概要を示します。
| 分離レベル | 内容を保証しない読込み1 | 反復不能な読込み2 | 仮読込み3 |
|---|---|---|---|
|
|
可能性あり |
可能性あり |
可能性あり |
|
|
可能性なし |
可能性あり |
可能性あり |
|
|
可能性なし |
可能性なし |
可能性あり |
|
|
可能性なし |
可能性なし |
可能性なし |
|
1
トランザクションは、別のトランザクションで変更されたコミットされていないデータを読み込むことができます。 2 トランザクションは、別のトランザクションでコミットされたデータを再度読み込み、その新しいデータを参照します。 3 トランザクションは問合せを再実行し、コミットされた別のトランザクションによって挿入された新しい行を検出できます。 |
これらの分離レベルに関するOracle Databaseの動作について、表2-5に概要を示します。
図2-1に、シリアライズ可能トランザクション(トランザクションB)と、別のトランザクション(トランザクションA、シリアライズ可能またはコミット読込みのいずれか)との相互作用を示しています。
シリアライズ可能トランザクションが「ORA-08177: このトランザクションのアクセスをシリアル化できません」のエラーで失敗したとき、アプリケーションは次のいずれかで対処できます。
Oracle Databaseでは、同時トランザクションによるアクセスを管理するために、各データ・ブロックに制御情報を格納します。SERIALIZABLE分離レベルを使用するには、CREATE TABLEコマンドまたはALTER TABLEコマンドのINITRANS句を使用して、この制御情報の格納を取り消す必要があります。SERIALIZABLEモードを使用するには、INITRANSを3以上に設定する必要があります。
トランザクションの分離レベルは、SET TRANSACTIONコマンドのISOLATION LEVEL句を使用して変更できます。SET TRANSACTIONコマンドは、トランザクションで最初に発行されるコマンドである必要があります。
トランザクション分離レベルをセッション全体に設定するには、ALTER SESSIONコマンドを使用します。
Oracle Databaseでは、同時トランザクションによるアクセスを管理するために、各データ・ブロックに制御情報を格納します。したがって、トランザクション分離レベルをSERIALIZABLEに設定する場合は、ALTER TABLEコマンドを使用して、INITRANSを3以上に設定する必要があります。このパラメータを使用すると、Oracle Databaseは、ブロックにアクセスした最新トランザクションの履歴を記録するために十分な記憶域を、それぞれのブロック内に割り当てます。同じブロックを更新するトランザクションの数が多い表には、さらに大きい値を使用する必要があります。
Oracle Databaseは、シリアライズ可能トランザクション内であっても読込みロックを使用しないため、あるトランザクションによって読み込まれたデータは、別のトランザクションでオーバーライドできます。アプリケーション・レベルでデータベースの整合性チェックを実行するトランザクションでは、読み込んだデータはトランザクション実行中には変更されないと考えないでください(そのような変更がトランザクションからはわからない場合も)。シリアライズ可能トランザクションを使用した場合でも、アプリケーション・レベルの整合性チェックのコードを十分注意して作成しないと、データベースの不整合が発生する可能性があります。ただし、この項に示されている例は、コミット読込みトランザクションおよびシリアライズ可能トランザクションの両方に該当するため注意してください。
図2-2に、2つの表の間の参照整合性の親子関係を保持するために、アプリケーション・レベルでチェックを実行する異なる2つのトランザクションを示します。一方のトランザクションは、親表に特定の主キー値を持つ行が存在するかどうかをチェックした後、対応する子である行を挿入します。もう一方のトランザクションは、対応するディテール行が存在しないことをチェックした後、親である行を削除します。この場合、両方のトランザクションが読み込んだデータは、そのトランザクションが完了する前には変更されないものと想定しています(確認はしません)。
トランザクションAが実行した読込みのために、トランザクションBが親である行を削除できなくなることはありません。同様に、トランザクションBが子である行の問合せを行っても、トランザクションAが子である行を挿入できなくなることもありません。したがって、この使用例では、対応する親である行を持たない子である行がデータベースに残ります。どちらのトランザクションも、整合性チェックのため読み込んだデータに対する変更を他方が防げないため、両方のトランザクションがシリアライズ可能トランザクションであっても、このような不整合が発生する可能性があります。
この例に示されているとおり、一方のトランザクションで読み込まれたデータがもう一方のトランザクションで同時に書き込まれないように、処置を行う必要がある場合があります。これには、SQL92 SERIALIZABLEモードで定義されているトランザクション分離レベルよりもかなり高いレベルが必要です。
Oracle Databaseでは、前述の矛盾を簡単に防ぐことができます。
SELECT FOR UPDATEを使用して、親である行を問い合せてロックし、トランザクションBがその行を削除しないように設定できます。
Oracle Databaseでは、トランザクションAの場合のような独立した問合せのかわりに、データベース・トリガーを使用して参照整合性を施行することもできます。たとえば、子表へのINSERTによって、BEFORE INSERT行レベル・トリガーを起動して、対応する親である行の有無をチェックできます。このトリガーは、SELECT FOR UPDATEを使用して親表を問い合せ、子である行を挿入するトランザクションの処理中に、親である行が(存在する場合)データベース内に残るようにします。対応する親である行が存在しない場合、トリガーは子である行の挿入を拒否します。
データベース・トリガーによって発行されたSQL文は、そのトリガーを起動したSQL文のコンテキスト内で実行されます。1つのトリガー内で実行されるすべてのSQL文は、トリガー起動文から参照する状態と同じデータベース状態を参照します。そのため、コミット読込みトランザクションでは、トリガー内のSQL文は、トリガー文の実行開始時点のデータベースを参照し、SERIALIZABLEモードで実行するトランザクションでは、SQL文は、そのトランザクションの開始時点のデータベースを参照します。いずれの場合も、トリガーでSELECT FOR UPDATEを使用すると、参照整合性が正しく施行されます。
Oracle Databaseの場合、アプリケーション開発者は異なる特性を持つ2つのトランザクション分離レベルのうちの1つを選択できます。READ COMMITTEDおよびSERIALIZABLE分離レベルは、どちらも高度な一貫性および同時実行性を提供します。これら2つの分離レベルは競合を軽減し、実社会でのアプリケーション配置用に設計されています。この項の後半では、2つの分離レベルを比較し、その選択の際に有効な情報を示します。
Oracle DatabaseのREAD COMMITTEDおよびSERIALIZABLE分離レベルについて説明するには、次のことを考慮すると効果的です。
どの読込みにおいても、同じコミット済トランザクション集合によって書き込まれたデータが戻される操作(問合せまたはトランザクション)を、「トランザクション集合整合である」といいます。トランザクション集合整合でない操作では、ある集合のトランザクションの変更が反映される読込みと、他のトランザクションによって行われた変更が反映される読込みが存在します。そのような操作では、そのデータベースは、コミットされたトランザクション集合が反映されていない状態のデータベースのように見えます。
Oracle DatabaseのREAD COMMITTEDモードでは、問合せによって読み込まれたすべての行が、その問合せが始まる前にコミットされているため、文単位でのトランザクション集合整合です。
Oracle DatabaseのSERIALIZABLEモードでは、シリアライズ可能トランザクション内のすべての文が、トランザクション開始時点のデータベースのイメージに対して実行されるため、トランザクション単位でのトランザクション集合整合です。
他のデータベース・システムでは、READ COMMITTEDモードで問合せを1回実行すると、トランザクション集合の整合性は失われます。この問合せでは、別のトランザクションによって行われた変更のサブセット以外見えないため、トランザクション集合は整合していません。たとえば、ディテール表とマスター表を結合すると、別のトランザクションによって挿入されたマスター・レコードを見ることはできますが、そのトランザクションによって挿入された対応するディテールは見えません(その逆も同じです)。READ COMMITTEDモードではこのような問題は回避されるため、読込みロック・システムより高い整合性が得られます。
読込みロック・システムでは、同時更新ができないようにするかわりに、SQL92 REPEATABLE READ分離によって、トランザクション・レベルではなく、文レベルでトランザクション集合の整合性が提供されます。仮(phantom)の保護がないということは、同一のトランザクションによって発行された2つの問合せが、他のトランザクションの別の集合によってコミットされたデータを参照できることを意味します。これらのシステムでは、スループットに制限がありデッドロックされやすいSERIALIZABLEモードの場合のみ、トランザクション・レベルでのトランザクション集合の整合性が提供されます。
表2-6に、コミット読込みトランザクションとシリアライズ可能トランザクションとの間の主な類似点および相違点を説明します。
それぞれのアプリケーションおよび作業負荷に適した分離レベルを選択する必要があります。また、異なるトランザクションにはそれぞれ個別の分離レベルを選択できます。分離レベルは、パフォーマンスと整合性のニーズ、およびアプリケーション・コーディング要件を考慮して選択します。
多数のユーザーが、トランザクションを同時に次々に送る環境の場合、予期されるトランザクション到着頻度および応答時間要件に対するトランザクション・パフォーマンスを評価して、十分なパフォーマンスを確保しながら必要な整合性を提供する分離レベルを選択する必要があります。多くの場合、高パフォーマンス環境では、整合性と同時実行性(トランザクションのスループット)を考慮して妥協点を見つける必要があります。
どちらのOracle Database分離モードも、行レベル・ロックとOracle Databaseの複数バージョン同時実行処理制御システムとを組み合せることによって、高レベルの整合性および同時実行性(およびパフォーマンス)を提供します。Oracle Databaseでは読込みと書込みの相互干渉がないため、問合せで整合性のあるデータが参照できる一方、READ COMMITTED分離およびSERIALIZABLE分離により、コミットされていない(内容が保証されない)データの読込みを防止し高レベルの同時実行性を提供することで、高いパフォーマンスを実現しています。
READ COMMITTED分離レベルでは、一部のトランザクションについては(仮読込みおよび反復不能な読込みのため)一貫性のない結果が生成される可能性は多少高くなりますが、かなり高い同時実行性を提供できます。SERIALIZABLE分離レベルの場合は、仮読込みおよび反復不能な読込みから保護されているため、より高い整合性が提供され、読込み/書込みトランザクションが問合せを2回以上実行する場合にはこの分離レベルは重要です。ただし、SERIALIZABLEモードでは、アプリケーションが「このトランザクションのアクセスをシリアル化できません」というエラーの有無を確認する必要があり、多数の同時トランザクションが更新のために同じデータにアクセスする環境では、スループットはかなり低下する可能性があります。データベースの整合性を確認するアプリケーション・ロジックでは、いずれのモードでも読込みが書込みをブロックしないように設定する必要があります。
トランザクションをSERIALIZABLEモードで実行する場合、シリアライズ可能トランザクションの開始以降に、別のトランザクションにより変更されたデータを変更しようとすると、次のようなエラーが発生します。
ORA-08177: このトランザクションのアクセスをシリアル化できません
このエラーが発生した場合は、現行のトランザクションをロールバックし、操作を再実行します。トランザクションが新しいトランザクション・スナップショットを取得するため、操作が成功する可能性が高くなります。
トランザクションのロールバックおよび再実行によるパフォーマンスのオーバーヘッドを最小化するには、他の同時トランザクションと競合する可能性のあるDML文は、できるだけトランザクションの始めの方に置くようにしてください。
この項では、自律型トランザクション(AT)の概要およびこのトランザクションの機能を簡単に説明します。
プライマリ・トランザクションの最終結果とは関係なく、表に対して別の変更をコミットまたはロールバックすることが必要になる場合があります。たとえば、株式売買トランザクションでは、全体的な株式売買行為が実際に遂行されるかどうかに関係なく、顧客情報のコミットが必要な場合があります。またはそのトランザクションを実行中にトランザクション全体がロールバックされた場合でも、エラー・メッセージをデバッグ表にログする必要がある場合もあります。自律型トランザクションを使用すると、これらのタスクを実行できます。
自律型トランザクション(AT)は、別のトランザクション(メイン・トランザクション(MT))によって開始される独立したトランザクションです。自律型トランザクションを使用すると、メイン・トランザクションを停止して、SQL操作を実行し、そのSQL操作をコミットまたはロールバックした後でメイン・トランザクションを再開できます。
自律型トランザクションは、自律型スコープ内で実行されます。自律型スコープとは、プラグマ(コンパイラ・ディレクティブ)AUTONOMOUS_TRANSACTIONでマークされたルーチンです。このプラグマは、PL/SQLコンパイラに対して、ルーチンを自律型(非依存)としてマークするように指示します。ここで意味するルーチンには、次のものが含まれます。
図2-3に、メイン・ルーチン(MT)と自律型ルーチン(AT)との間の制御フローを示します。図からわかるように、自律型ルーチンでは、制御がメイン・ルーチンに戻る前に複数のトランザクション(AT1およびAT2)をコミットできます。
自律型ルーチンの実行可能セクションに入ると、メイン・ルーチンが停止します。自律型ルーチンを終了すると、メイン・ルーチンが再開します。
COMMITおよびROLLBACKによって、アクティブな自律型トランザクションは終了しますが、自律型ルーチンは終了しません。図2-3に示すとおり、1つのトランザクションが終了すると、次のSQL文が別のトランザクションを開始します。
自律型トランザクションの特長をさらにいくつか示します。
図2-4に、自律型トランザクションが従う実行順序の例を示します。
この項の2つの例では、自律型トランザクションの使用方法をいくつか説明します。
例に示すとおり、自律型トランザクションおよびメイン・トランザクションを使用する場合は、4種類の結果が考えられます。表2-7にこの結果を示します。表からわかるように、自律型トランザクションの結果とメイン・トランザクションの結果との間に依存性はありません。
| 自律型トランザクション | メイン・トランザクション |
|---|---|
|
コミット |
コミット |
|
コミット |
ロールバック |
|
ロールバック |
コミット |
|
ロールバック |
ロールバック |
図2-5に示すように、この例では、顧客が購入注文を入力します。購買契約が成立しなくても、その顧客の情報(名前、住所、電話番号など)は顧客情報表にコミットされます。
この例では、顧客は口座から払戻しを実行しようとします。この処理で、メイン・トランザクションは2つの自律型トランザクション・スコープ(ATスコープ1およびATスコープ2)のいずれかをコールします。
次の図には、このトランザクションで考えられる使用例を3つ示します。
払戻しに十分な預金残高があり、銀行が払戻しに応じます。これについては、図2-6に示してあります。
払戻しに十分な預金残高はありませんが、この顧客には貸越し保護があります。したがって、銀行は払戻しに応じます。これについては、図2-7に示してあります。
払戻しに十分な預金残高はなく、この顧客には貸越し保護もありません。したがって、銀行は払戻しを差し止めます。これについては、図2-8に示してあります。
自律型トランザクションを定義するには、プラグマ(コンパイラ・ディレクティブ)AUTONOMOUS_TRANSACTIONを使用します。このプラグマは、PL/SQLコンパイラに対して、プロシージャ、ファンクションまたはPL/SQLブロックを自律型(非依存)としてマークするように指示します。
このプラグマは、プロシージャ、ファンクションまたはPL/SQLブロックの宣言セクション内のどこにでも作成できます。ただし、コードを読みやすくするために、プラグマはセクションの一番上に作成するようにします。構文は次のとおりです。
PRAGMA AUTONOMOUS_TRANSACTION;
次の例では、パッケージ・ファンクションを自律型としてマークします。
CREATE OR REPLACE PACKAGE Banking AS FUNCTION Balance (Acct_id INTEGER) RETURN REAL; -- add 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 --add appropriate code END; -- add additional functions and packages... END Banking;
自律型トランザクションには、次の制限があることに注意してください。
CREATE OR REPLACE PACKAGE Banking AS PRAGMA AUTONOMOUS_TRANSACTION; -- illegal FUNCTION Balance (Acct_id INTEGER) RETURN REAL; END Banking;
PIPE ROW文を実行することはできません。PIPE ROW文を実行する前に、自律型トランザクションをクローズする必要があります。通常これは、PIPE ROW文を実行する前に自律型トランザクションをコミットまたはロールバックするという方法で行われます。 長時間にわたって実行されるトランザクションが領域不足エラー状態によって中断されたときに、アプリケーションによって問題が発生した文を一時停止し、領域問題が修正された後でその文を再開することが可能です。この機能を、再開可能記憶域割当てといいます。この機能によって、時間がかかるロールバックを回避できます。また、操作を小さく分割したり、処理過程を追跡するコードを作成する必要がなくなります。
問合せ、DML操作および特定のDDL操作は、領域不足エラーが発生した場合、すべて再開可能です。この機能は、操作がSQL文によって直接実行されているか、または、ストアド・プロシージャ、無名PL/SQLブロック、SQL*Loader、OCIStmtExecute()などのOCIコール内で実行されている場合に適用されます。
操作は、次のようなエラーの後で再開可能です。
前述の方法では処理できない記憶域エラーもあります。ディクショナリ管理された表領域では、ロールバック・セグメントの上限に達するか、または索引または表の作成中にエクステントの最大数に達した場合、操作を再開できません。前述の方法にローカル管理表領域および自動UNDO管理を組み合せて使用してください。
操作が一時停止された場合、アプリケーションは通常のエラー・コードを受信しません。かわりに、AFTER SUSPENDイベントを検出しDBMS_RESUMABLEパッケージのファンクションをコールして問題についての情報を取得するトリガーをコーディングして、ロギングまたは通知を実行します。このパッケージを使用すると、次のことが可能です。
DBMS_RESUMABLE.SPACE_ERROR_INFOファンクションを使用して、エラー・メッセージを解析できます。このファンクションの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。
SET_TIMEOUTプロシージャを使用して新しいタイムアウト値を設定できます。
トリガーの本体内では、通知を実行できます。たとえば、オペレータにメール・メッセージを送り、領域問題について警告できます。
また、DBAが、DBA_RESUMABLE、USER_RESUMABLEおよびV$_SESSION_WAITデータ・ディクショナリ・ビューを使用して、一時停止された文を定期的に確認することもできます。
領域状態が(通常DBAによって)修正されると、一時停止された文は自動的に実行を再開します。タイムアウト周期が終了するまでに領域状態が修正されなかった場合は、その操作によってSERVERERROR例外が発生します。
トリガー自体の中で領域不足エラーが発生する可能性を減らすには、トリガーを自律型トランザクションとして宣言し、トリガーがSYSTEM表領域内のロールバック・セグメントを使用するようにする必要があります。一時停止された文が保持するロックによってトリガーにデッドロック状態が発生した場合、そのトリガーは強制終了され、アプリケーションは一時停止が発生しなかった場合の本来のエラー状態を受信します。トリガーによって領域不足状態が発生した場合、そのトリガーおよび一時停止された文はロールバックされます。トリガー内の例外ハンドラを介してロールバックを回避し、文が再開されるまで待つこともできます。
次に示すトリガーは、データベース内の適用可能な記憶域エラーを処理します。いくつかのエラーでは、このトリガーは文を強制終了し、エラーに関する警告をメール・メッセージによってDBAに通知します。他の一時的なエラーでは、8時間以内に記憶域の問題が解決していることを想定して、8時間後に文を再開するように指定しています。
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 sys.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 := 'Subject: 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;
|
![]() Copyright © 2006 Oracle Corporation. All Rights Reserved. |
|