アプリケーションが、並行トランザクション と呼ぶ複数の独立した接続を持っていると便利である場合がしばしばあります。トランザクションを使用すると、アプリケーションは一度に複数のデータベースに接続でき、また同じデータベースへの複数の独立した接続を確立することもできます。
マルチスレッドのデータベースのアクセスに説明されている文脈 API を組み込むと、アプリケーションで並行トランザクションを使用することができます。アプリケーション内で作成される文脈は、それぞれ他の文脈とは独立しています。ということは、他の文脈によって実行される COMMIT または ROLLBACK ステートメントなどの活動に影響されずに、新たに文脈を作成し、その文脈を使用してデータベースに接続し、データベースに SQL ステートメントを実行できるということです。
たとえば、ユーザーがあるデータベースに対して SQL ステートメントを実行し、同時に別のデータベースで実行している活動のログを保存するというアプリケーションを作成するとしましょう。ログは最新のものでなければなりませんから、ログを更新するたびに COMMIT ステートメントを発行することが必要ですが、ログに対して発行したコミットがユーザーの SQL ステートメントに影響することのないようにしたいと思います。このようなときにこそ、並行トランザクションを使います。アプリケーション内で、次の 2 つの文脈を作成します。 1 つの文脈はユーザーのデータベースに接続し、ユーザーの SQL すべてに対して使用するもので、別の文脈はログ・データベースに接続し、ログの更新に使用します。このように設計することにより、ログ・データベースへの変更をコミットしても、ユーザーの現在の作業単位には影響は及びません。
並行トランザクションのもう 1 つの利点は、ある接続でカーソルに対する処理がロールバックされても、他の接続のカーソルには何の影響もないということです。 1 つの接続でロールバックが行われた後も、他の接続では終了した処理とカーソル位置はそのまま保持されます。
並行トランザクションを使用するアプリケーションでは、単一接続を使用するアプリケーションを作成する場合には起こりえない問題にいくつか直面することがあります。並行トランザクションを用いたアプリケーションを作成する場合には、以下の注意が必要です。
アプリケーション内の各文脈には、データベース・オブジェクトに対するロックなど、それぞれ固有のデータベース資源があります。そのため、2 つの文脈が同じデータベース・オブジェクトにアクセスしようとすると、デッドロック状態になってしまう可能性があります。データベース・マネージャーはデッドロックを検出し、一方の文脈が SQLCODE -911 を受け取り、その文脈の作業単位がロールバックされます。
単一のスレッド内で複数の文脈を切り換えると、その文脈間に従属関係が作成されます。文脈にデータベースの従属関係もある場合は、デッドロックが起こる可能性があります。一部の従属関係はデータベース・マネージャーの管理範囲外にあるため、デッドロックが検出されず、アプリケーションが中断してしまうこともあります。
この種の問題の例として、次のアプリケーションを検討してみましょう。
context 1 UPDATE TAB1 SET COL = :new_val context 2 SELECT * FROM TAB1 COMMIT context 1 COMMIT
最初の文脈 (context 1) が UPDATE ステートメントを正常に実行したとします。 UPDATE は、TAB1 のすべての行をロックします。次に、文脈 2 (context 2) は TAB1 のすべての行を選択しようとします。 2 つの文脈は独立しているので、文脈 2 は文脈 1 がロックを保持する間待機します。しかし、文脈 1 は文脈 2 が実行を終了するまでロックを解放できません。こうして、アプリケーションはデッドロック状態になりますが、データベース・マネージャーは文脈 1 が文脈 2 を待機していることを知らないため、どちらか一方の文脈を強制的にロールバックすることはしません。そのため、アプリケーションは延期状態になってしまいます。
データベース・マネージャーは文脈間のデッドロックを検出できないため、デッドロックしないように (または少なくともデッドロックを回避できるように) アプリケーションを設計してコーディングしなければなりません。上の例では、いくつかの方法でデッドロックを回避することができます。
文脈 2 に切り換える前に文脈 1 がコミットを実行するように、コードを変更します。
同じ文脈から更新と選択の両方が実行されるように、コードを変更します。
これではデッドロックを防ぐことはできませんが、実行を再開することはできます。文脈 2 は、要求されたロックを獲得できないため、結局はロールバックされます。文脈 2 がロールバックされれば、文脈 1 は実行を継続でき (これによってロックは解除され)、文脈 2 は処理を再試行します。
デッドロックを回避する技法を上記の例を参考にして示しましたが、この方法は並行トランザクションを使用するすべてのアプリケーションに適用できます。