この章の内容は、次のとおりです。
分散トランザクションは1つ以上の文からなり、それらが個別に、またはグループとして、分散データベースの複数ノードのデータを更新します。たとえば、図34-1に示すデータベース構成を考えます。
scott
によって実行される次の分散トランザクションは、ローカルのsales
データベース、リモートのhq
データベース、およびリモートのmaint
データベースを更新します。
UPDATE scott.dept@hq.us.example.com SET loc = 'REDWOOD SHORES' WHERE deptno = 10; UPDATE scott.emp SET deptno = 11 WHERE deptno = 10; UPDATE scott.bldg@maint.us.example.com SET room = 1225 WHERE room = 1163; COMMIT;
注意: トランザクションのすべての文が1つのリモート・ノードのみを参照している場合、そのトランザクションは分散トランザクションではなくリモート・トランザクションです。 |
分散トランザクションでは、次の2種類の操作が許可されます。
分散トランザクションでサポートされているデータ操作言語(DML)およびデータ定義言語(DDL)操作は、次のとおりです。
CREATE
TABLE
AS
SELECT
DELETE
INSERT
(デフォルトおよびダイレクト・ロード)
UPDATE
LOCK
TABLE
SELECT
SELECT
FOR
UPDATE
DML文およびDDL文はパラレルに実行でき、ダイレクト・ロードINSERT
文はシリアルに実行できます。ただし、次の制限に注意してください。
リモート操作はすべてSELECT
文である必要があります。
これらの文は、別の分散トランザクション内の句であってはいけません。
INSERT、UPDATE
またはDELETE
文のtable_expression_clause
で参照される表がリモートの場合、実行はパラレルではなくシリアルになります。
パラレルDML/DDLまたはダイレクト・ロードINSERT
の発行後にリモート操作を実行することはできません。
XAまたはOCIを使用してトランザクションを開始した場合、そのトランザクションはシリアルに実行されます。
パラレル操作の実行元であるトランザクションで、ループバック操作を実行することはできません。たとえば、実際はローカル・オブジェクトのシノニムであるリモート・オブジェクトを参照することはできません。
トランザクションでSELECT
以外の分散操作を実行する場合、DMLはパラレル化されません。
分散トランザクションで文が発行されると、データベースはトランザクションに参加しているすべてのノードのセッション・ツリーを定義します。セッション・ツリーとは、セッション間の関係とセッションのロールを表す階層モデルです。セッション・ツリーの例を図34-2に示します。
分散トランザクションのセッション・ツリーに参加しているすべてのノードは、次に示すロールを1つ以上持ちます。
ロール | 説明 |
---|---|
クライアント | 異なるノードに属するデータベース内の情報を参照するノード。 |
データベース・サーバー | 別のノードからの情報の要求を受け取るノード。 |
グローバル・コーディネータ | 分散トランザクションの実行元ノード。 |
ローカル・コーディネータ | 他のノードのデータを強制的に参照して、自身のトランザクション部分を完了するノード。 |
コミット・ポイント・サイト | グローバル・コーディネータの指示に従ってトランザクションをコミットまたはロールバックするノード。 |
分散トランザクションのノードが果たすロールは、次の条件によって決まります。
トランザクションがローカルとリモートのどちらであるか。
ノードのコミット・ポイント強度(「コミット・ポイント・サイト」を参照)。
要求されたすべてのデータがノードで使用可能か、またはトランザクションを完了するために他のノードを参照する必要があるか。
ノードが読取り専用かどうか。
情報を別のノードのデータベースから参照するとき、ノードはクライアントとして機能します。参照先のノードはデータベース・サーバーです。図34-2のノードsales
は、warehouse
データベースおよびfinance
データベースが稼働しているノードのクライアントです。
データベース・サーバーは、クライアントがデータを要求する要求先データベースが稼働しているノードです。
図34-2では、sales
ノードのアプリケーションは、warehouse
ノードおよびfinance
ノードのデータにアクセスする分散トランザクションを開始します。したがって、sales.example.com
はクライアント・ノードのロールを持ち、warehouse
およびfinance
はどちらもデータベース・サーバーのロールを持ちます。この例では、アプリケーションでsales
データベース内のデータも変更されているため、salesはデータベース・サーバーおよび
クライアントです。
分散トランザクションにおいて自身のトランザクション部分を完了するために別のノードのデータを参照する必要があるノードは、ローカル・コーディネータと呼ばれます。図34-2で、sales
は、直接参照しているノードwarehouse
およびfinance
を調整しているため、ローカル・コーディネータです。ノードsales
もトランザクションに関係しているすべてのノードを調整しているため、グローバル・コーディネータになります。
ローカル・コーディネータは、自身が直接やり取りするノードの間で次のようにトランザクションを調整する役目を果たします。
これらのノード間でトランザクションのステータス情報を受け渡します。
これらのノードに問合せを渡します。
これらのノードから問合せを受け取り、他のノードに渡します。
問合せの結果を開始元のノードに返します。
分散トランザクションの実行元であるノードのことを、グローバル・コーディネータと呼びます。分散トランザクションを発行するデータベース・アプリケーションは、グローバル・コーディネータとして機能しているノードに直接接続します。たとえば、図34-2では、ノードsales
で発行されたトランザクションは、データベース・サーバーwarehouse
およびfinance
の情報を参照します。したがって、sales.example.com
は、この分散トランザクションのグローバル・コーディネータになります。
グローバル・コーディネータは、セッション・ツリーの親またはルートになります。グローバル・コーディネータは、分散トランザクション処理中に次の操作を実行します。
分散トランザクションのすべてのSQL文やリモート・プロシージャ・コールなどを参照先のノードに直接送り、それによってセッション・ツリーを構成します。
コミット・ポイント・サイト以外のすべての直接参照先ノードに対して、トランザクションの準備をするように指示します。
すべてのノードの準備が正常に完了した場合に、トランザクションのグローバル・コミットを開始するようにコミット・ポイント・サイトに対して指示します。
ノードから異常終了の応答があった場合に、トランザクションのグローバル・ロールバックを開始するようにすべてのノードに対して指示します。
コミット・ポイント・サイトの役割は、グローバル・コーディネータの指示に従ってコミットまたはロールバックの操作を開始することです。システム管理者は、すべてのノードにコミット・ポイント強度を割り当てることで、セッション・ツリー内のノードの1つをコミット・ポイント・サイトとして必ず指定します。コミット・ポイント・サイトには、最も重要なデータを格納するノードを選択してください。
図34-3は、sales
がコミット・ポイント・サイトとして機能している分散システムの例です。
コミット・ポイント・サイトは、分散トランザクションに関係する他のすべてのノードと次の点で区別されます。
コミット・ポイント・サイトが準備完了状態に入ることはありません。そのため、コミット・ポイント・サイトに最も重要なデータが格納されている場合は、たとえ障害が起きたとしても、データがインダウトのままになることはありません。障害が発生すると、障害を起こしたノードは準備完了状態のままになり、インダウト・トランザクションが解決されるまで必要なロックが保持されます。
コミット・ポイント・サイトは、トランザクションに関係している他のノードよりも先にコミットします。実際は、コミット・ポイント・サイトでの分散トランザクションの結果によって、すべてのノードでのトランザクションがコミットされるかロールバックされるかが決まり、他のノードはコミット・ポイント・サイトの指示に従います。グローバル・コーディネータは、すべてのノードでコミット・ポイント・サイトと同様にトランザクションが完了することを保証します。
分散トランザクションは、コミット・ポイント以外のすべてのサイトで準備が完了した後、コミットされたとみなされますが、実際には、トランザクションはコミット・ポイント・サイトで先にコミットされています。コミット・ポイント・サイトのREDOログは、このノードで分散トランザクションがコミットされるとただちに更新されます。
コミット・ポイント・ログにはコミットの記録があります。そのため、たとえ参加中のノードの一部がまだ準備完了状態であり、それらのノードで実際にトランザクションがコミットされていない場合であっても、トランザクションはコミットされたとみなされます。同様に、コミット・ポイント・サイトでコミットが記録されていない場合は、分散トランザクションはコミットされていないとみなされます。
データベース・サーバーには、必ずコミット・ポイント強度を割り当てる必要があります。データベース・サーバーが分散トランザクション内で参照される場合、そのコミット・ポイント強度の値によって、2フェーズ・コミットにおける役割が決まります。具体的には、このコミット・ポイント強度によって、どのノードが分散トランザクションのコミット・ポイント・サイトになり、他のすべてのノードより前にコミットするかが決まります。 この値を指定するには、初期化パラメータCOMMIT_POINT_STRENGTH
を使用します。ここでは、データベースがコミット・ポイント・サイトを決定する仕組みについて説明します。
準備フェーズの冒頭で決定されるコミット・ポイント・サイトは、トランザクションに参加しているノードの中からのみ選択されます。次に示す一連のイベントが発生します。
データベースは、グローバル・コーディネータが直接参照しているノードの中で、最も高いコミット・ポイント強度を持つノードをコミット・ポイント・サイトとして選択します。
最初に選択されたノードは、このトランザクションの情報を取得する必要のあるノードの中で、自身よりも高いコミット・ポイント強度を持っているノードがないかを判断します。
トランザクションで直接参照しているノードの中で最も高いコミット・ポイント強度を持つノードか、またはそのノードのサーバーの中でより高いコミット・ポイント強度を持つもののどちらかが、コミット・ポイント・サイトになります。
最終的なコミット・ポイント・サイトが決定した後、グローバル・コーディネータは、トランザクションに参加しているすべてのノードに準備応答を送ります。
図34-4は、各ノードのコミット・ポイント強度(カッコ内の値)を示したセッション・ツリーの例です。また、コミット・ポイント・サイトとして選択されたノードも示しています。
コミット・ポイント・サイトを決定するときは、次の条件が適用されます。
読取り専用ノードは、コミット・ポイント・サイトにはなれません。
グローバル・コーディネータが直接参照している複数のノードが同じコミット・ポイント強度を持っている場合、データベースはそれらのうちの1つをコミット・ポイント・サイトとして指定します。
分散トランザクションがロールバックで終了する場合は、準備フェーズとコミット・フェーズは不要です。したがって、データベースはコミット・ポイント・サイトを決定しません。そのかわりに、グローバル・コーディネータはROLLBACK
文をすべてのノードに送り、分散トランザクションの処理を終了します。
図34-4のように、コミット・ポイント・サイトとグローバル・コーディネータがセッション・ツリーの異なるノードになることもあります。各ノードのコミット・ポイント強度は、最初に接続が確立されたときにコーディネータに渡されます。コーディネータは、2フェーズ・コミット時のコミット・ポイント・サイトを効率的に選択するために、自身が直接やり取りしている各ノードのコミット・ポイント強度を保持しています。そのため、コミットが発生するたびにコーディネータとノード間でコミット・ポイント強度を交換する必要がありません。
関連項目:
|
ローカル・データベースのトランザクションとは異なり、分散トランザクションには複数のデータベースでのデータの変更が伴います。そのため、データベースは、トランザクションの変更のコミットまたはロールバックを自己完結単位として調整する必要があり、分散トランザクションの処理はより複雑になります。言い換えれば、トランザクション全体がコミットするか、またはトランザクション全体がロールバックするかのどちらかの結果になります。
データベースは、2フェーズ・コミット・メカニズムを使用することで、分散トランザクションにおけるデータの整合性を保証します。準備フェーズでは、トランザクション内の開始ノードが他の参加ノードに対して、トランザクションをコミットまたはロールバックすることを確約するように要求します。コミット・フェーズでは、開始ノードがすべての参加ノードに対して、トランザクションをコミットするように要求します。この結果が達成できない場合、すべてのノードはロールバックするように要求されます。
分散トランザクションに参加しているすべてのノードは、必ず同じ動作を実行します。つまり、ノードすべてがトランザクションをコミットするか、またはノードすべてがトランザクションをロールバックします。データベースは、分散トランザクションのコミットまたはロールバックを自動的に制御および監視しており、2フェーズ・コミット・メカニズムを使用してグローバル・データベース(トランザクションに参加しているデータベースの集まり)の整合性を維持します。このメカニズムは完全に透過的であり、ユーザーやアプリケーション開発者の側でプログラミングを行う必要はまったくありません。
コミット・メカニズムは、次の各フェーズで構成されています。データベースは、ユーザーが分散トランザクションをコミットすると、必ずこれらのフェーズを自動的に実行します。
フェーズ | 説明 |
---|---|
準備フェーズ | グローバル・コーディネータと呼ばれる開始ノードは、コミット・ポイント・サイト以外の参加ノードに対して、たとえ障害が起きた場合でもトランザクションをコミットまたはロールバックすることを確約するように要求します。準備ができないノードがある場合、トランザクションはロールバックされます。 |
コミット・フェーズ | すべての参加ノードが準備完了の応答をコーディネータに伝えると、コーディネータは、コミット・ポイント・サイトにコミットを要求します。コミット・ポイント・サイトのコミット後、コーディネータは、トランザクションをコミットするように他のすべてのノードに要求します。 |
情報消去フェーズ | グローバル・コーディネータは、トランザクションに関する情報を消去します。 |
この項の内容は次のとおりです。
準備フェーズは、分散トランザクションのコミットにおける1番目のフェーズです。このフェーズでは、データベースがトランザクションを実際にコミットまたはロールバックすることはありません。ここでは、分散トランザクションで参照されているすべてのノード(「コミット・ポイント・サイト」で説明されているコミット・ポイント・サイトを除く)がコミットを準備するように指示されます。ノードは、準備を完了するために次の処理を実行します。
REDOログの情報を記録し、それ以降障害が発生してもトランザクションをコミットまたはロールバックできるようにします。
変更済の表に分散ロックを設定し、読取りを防ぎます。
各ノードは、コミットの準備が完了したという応答をグローバル・コーディネータに伝えることによって、その後トランザクションをコミットまたはロールバックすることを確約しますが、トランザクションをコミットまたはロールバックするという決定を各ノードが一方的に下すわけではありません。この確約の意味は、この時点でインスタンス障害が発生した場合、ノードはオンライン・ログのREDOレコードを使用してデータベースをリカバリし、準備フェーズに戻すことができるということです。
注意: ノードの準備完了後に発行した問合せは、すべてのフェーズが完了するまで、関連するロック済データにアクセスできません。この時間は、障害が発生しないかぎり問題にはなりません(「インダウト・トランザクションの処理方法の決定」を参照)。 |
準備を指示されたノードは、次の方法で応答できます。
応答 | 意味 |
---|---|
Prepared | ノードのデータの変更が分散トランザクション内の文によって完了しており、ノードの準備が正常に完了しています。 |
読取り専用 | ノードで変更されるデータがない(問合せのみ)か、または変更できないので、準備は不要です。 |
異常終了 | ノードが正常に準備できません。 |
ノードの準備が正常に完了すると、ノードは準備完了メッセージを発行します。このメッセージは、ノードの変更レコードがオンライン・ログに格納されており、ノードでコミットまたはロールバックのどちらかを実行できる準備が整っていることを示します。また、このメッセージによって、トランザクションに対して保持されているロックが障害発生時にも残ることが保証されます。
ノードが準備を要求されたときに、データベースにアクセスするSQL文がノードのデータを変更しない場合、ノードは読取り専用メッセージで応答します。このメッセージは、ノードがコミット・フェーズに参加しないことを示します。
分散トランザクションの全部または一部が読取り専用になるケースとして、次の3つがあります。
分散トランザクションが読取り専用に設定されている場合、そのトランザクションでUNDOセグメントは使用されません。多数のユーザーがデータベースに接続していて、ユーザーのトランザクションがREAD ONLYに設定されていない
場合は、それらのトランザクションが問合せを実行するのみであっても、UNDO領域が割り当てられます。
これらの処理は、分散トランザクションに関係している他のノードに伝播します。これにより、他のノードはトランザクションをロールバックできるので、グローバル・データベースのデータの整合性が保証されます。この応答によって、「トランザクションに関係しているすべてのノードが同じ物理時間でトランザクションをすべてコミットするか、またはすべてロールバックする」という分散トランザクションの基本原則が守られます。
準備フェーズを完了するために、コミット・ポイント・サイトを除く各ノードは次の手順を実行します。
ノードは、自身の子(以降参照する各ノード)に対して、コミットの準備をするように要求します。
ノードは、トランザクションによって自分自身のデータまたは子のデータが変更されるかどうかをチェックします。データが変更されない場合、ノードは残りの手順を省略し、読取り専用応答を返します(「読取り専用応答」を参照)。
データが変更される場合、ノードはトランザクションのコミットに必要なリソースを割り当てます。
ノードは、トランザクションによる変更に対応するREDOレコードをREDOログに保存します。
ノードは、トランザクションに対して保持されているロックが障害発生時にも残ることを保証します。
ノードは、準備レスポンスを開始ノードに伝えます(「準備レスポンス」を参照)。あるいは、自身またはその子のいずれかが準備の試行に失敗した場合は、異常終了応答を伝えます(「異常終了時のエラー」を参照)。
これらの処理によって、ノードが後で自身のトランザクションをコミットまたはロールバックできることが保証されます。この後、準備完了ノードは、グローバル・コーディネータからCOMMIT
またはROLLBACK
要求を受け取るまで待機します。
各ノードの準備が完了した後、分散トランザクションはインダウトと呼ばれる状態になります(「インダウト・トランザクション」を参照)。すべての変更がコミットまたはロールバックされるまで、インダウト状態のままです。
コミット・フェーズは、分散トランザクションのコミットにおける2番目のフェーズです。このフェーズになる前に、分散トランザクションで参照されている、コミット・ポイント・サイト以外のすべてのノードが準備完了していること、つまり、トランザクションのコミットに必要なリソースを確保していることが保証されます。
グローバル・コーディネータは、コミット・ポイント・サイトにコミットを指示します。
コミット・ポイント・サイトは、コミットを実行します。
コミット・ポイント・サイトは、コミットが完了したことをグローバル・コーディネータに伝えます。
グローバル・コーディネータおよびローカル・コーディネータは、すべてのノードに対してトランザクションをコミットするように指示するメッセージを送ります。
各ノードで、データベースは分散トランザクションのローカル部分をコミットし、ロックを解放します。
各ノードで、データベースは、トランザクションのコミットが完了したことを示す追加のREDOエントリをローカルREDOログに記録します。
各参加ノードは、自身のコミットが完了したことをグローバル・コーディネータに伝えます。
コミット・フェーズが完了するときは、分散システムの全ノードのデータについて一貫性が保たれています。
コミットされた各トランザクションには、そのトランザクション内部のSQL文によって行われた変更を一意に識別するためのSCNが対応付けられます。SCNは、データベースのコミット済バージョンを一意に識別する内部的なタイムスタンプの役割を持ちます。
分散システムでは、次の処理がすべて発生したときに、通信中のノードのSCNが調整されます。
1つ以上のデータベース・リンクによって表されるパスを使用した接続の確立
分散SQL文の実行
分散トランザクションのコミット
特に、分散システムのノード間でSCNが調整されることにより、文とトランザクションの両方のレベルでグローバルな読込み一貫性が保証されるという利点があります。必要であれば、グローバルな時間ベースのリカバリを実行することもできます。
準備フェーズ時に、データベースは、トランザクションに関係しているすべてのノードで最も高いSCNを判断します。次に、コミット・ポイント・サイトにおいて最も高いSCNでトランザクションをコミットします。さらに、すべての準備完了ノードに対して、コミットの指示とともにコミットSCNを送ります。
2フェーズ・コミット・メカニズムは、すべてのノードがコミットされるかまたはロールバックを一斉に実行することを保証します。システムやネットワークのエラーのために、3つのフェーズのいずれかが失敗した場合はどうなるのでしょうか。この場合、トランザクションはインダウトになります。
分散トランザクションは、次の要因によってインダウトになる可能性があります。
Oracle Databaseソフトウェアを実行しているサーバー・システムがクラッシュした。
分散処理に関係している複数のOracle Database間のネットワーク接続が切断された。
未処理のソフトウェア・エラーが発生した。
システム、ネットワークまたはソフトウェアの問題が解決されると、RECOプロセスによってインダウト・トランザクションが自動的に解決されます。RECOがトランザクションを解決できるまで、データは読取りおよび書込みの両方についてロックされます。読取りをブロックするのは、データベースが問合せに対してどのバージョンのデータを表示すればよいかを判断できないためです。
この項の内容は次のとおりです。
データベースでは、多くの場合、インダウト・トランザクションは自動的に解決されます。たとえば、次の使用例においてlocal
とremote
の2つのノードがあるとします。ローカル・ノードはコミット・ポイント・サイトです。ユーザーscott
は、local
に接続してlocal
とremote
を更新する分散トランザクションを実行し、コミットします。
図34-5は、分散トランザクションの準備フェーズ中に障害が発生したときの一連のイベントを示しています。
次の手順が発生します。
ユーザーSCOTT
はLocal
に接続して分散トランザクションを実行します。
グローバル・コーディネータ(この例ではコミット・ポイント・サイトを兼務)は、コミット・ポイント・サイト以外のすべてのデータベースに対して、コミットまたはロールバックの指示があったときにその動作を実行することを確約するように要求します。
remote
データベースが、local
に準備応答を発行する前にクラッシュします。
トランザクションは、リモート・サイトのリストア時に、RECOプロセスによって各データベースで最終的にロールバックされます。
図34-6は、分散トランザクションのコミット・フェーズ中に障害が発生したときの一連のイベントを示しています。
次の手順が発生します。
ユーザーScott
はlocal
に接続して分散トランザクションを実行します。
グローバル・コーディネータ(この場合はコミット・ポイント・サイトを兼務)は、コミット・ポイント・サイト以外のすべてのデータベースに対して、コミットまたはロールバックの指示があったときにその動作を実行することを確約するように要求します。
コミット・ポイント・サイトは、コミットの確約を示す準備完了メッセージをremote
から受け取ります。
コミット・ポイント・サイトはトランザクションをローカルにコミットし、それからコミット・メッセージをremote
に送ってコミットを要求します。
remote
データベースはコミット・メッセージを受け取りましたが、ネットワーク障害のために応答できません。
トランザクションは、ネットワークがリストアされた後、RECOプロセスによってリモート・データベースで最終的にコミットされます。
次の場合のみ、インダウト・トランザクションを手動で解決する必要があります。
インダウト・トランザクションが重要なデータまたはUNDOセグメントをロックしている場合。
システム、ネットワークまたはソフトウェアの障害の原因をすぐに修復できない場合。
インダウト・トランザクションの解決が複雑になる場合があります。その場合は、次の手順を実行する必要があります。
インダウト・トランザクションのトランザクション識別番号を特定します。
DBA_2PC_PENDING
ビューおよびDBA_2PC_NEIGHBORS
ビューを問い合せ、トランザクションに関係しているデータベースのコミットが完了しているかどうかを確認します。
必要であれば、COMMIT FORCE
文を使用してコミットを強制実行するか、またはROLLBACK FORCE
文を使用してロールバックを強制実行します。
SCNは、コミット済バージョンのデータベースの内部的なタイムスタンプです。Oracle Databaseサーバーは、SCNクロック値を使用することでトランザクションの一貫性を保証します。たとえば、ユーザーがトランザクションをコミットするとき、データベースはそのコミットのSCNをREDOログに記録します。
データベースは、SCNを使用して、異なるデータベース間での分散トランザクションを調整します。たとえば、データベースは、次のときにSCNを使用します。
アプリケーションがデータベース・リンクを使用して接続を確立するとき。
分散トランザクションが、それに関係しているすべてのデータベースの中で最も高いグローバルSCNでコミットされるとき。
コミット・グローバルSCNが、トランザクションに関係しているすべてのデータベースに送られるとき。
SCNは、トランザクションが失敗した場合も含め、トランザクションの同期化されたコミット・タイムスタンプとして機能するため、分散トランザクションにとって非常に重要な存在です。トランザクションがインダウトになった場合、管理者はこのSCNを使用して、グローバル・データベースへの変更を調整できます。トランザクション・コミットのグローバルSCNは、分散リカバリの実行時など、後でトランザクションの識別に使用することもできます。
この使用例では、ある会社がsales.example.com
およびwarehouse.example.com
という異なるOracle Databaseサーバーを持っています。ユーザーが売上レコードをsales
データベースに挿入すると、対応付けられたレコードがwarehouse
データベースで更新されます。
セッション・ツリーの定義。
コミット・ポイント・サイトがどのように決定されるか。
準備メッセージがいつ送られるか。
トランザクションがいつ実際にコミットされるか。
トランザクションに関するどのような情報がローカルに格納されるか。
営業部門で、営業担当がSQL*Plusを使用して売上注文を入力し、コミットします。アプリケーションは次のようなSQL文を発行し、sales
データベースに注文を入力して、warehouse
データベースのinventory表を更新します。
CONNECT scott@sales.example.com ...; INSERT INTO orders ...; UPDATE inventory@warehouse.example.com ...; INSERT INTO orders ...; UPDATE inventory@warehouse.example.com ...; COMMIT;
これらのSQL文は単一の分散トランザクションの一部であり、発行されるすべてのSQL文がユニットとして成功または失敗することが保証されています。文をユニットとして扱うことにより、注文が設定されていても、注文を反映するように在庫が更新されていないという事態を防ぐことができます。実際は、トランザクションによってグローバル・データベースのデータの一貫性が保証されています。
トランザクションの各SQL文が実行されると、図34-7のようにセッション・ツリーが定義されます。
トランザクションの次の点に注意してください。
トランザクションを開始するのは、sales
データベースで実行されている注文入力アプリケーションです。したがって、分散トランザクションのグローバル・コーディネータは、sales.example.com
になります。
注文入力アプリケーションは、新しい売上レコードをsales
データベースに挿入し、warehouseデータベースのinventory表を更新します。したがって、ノードsales.example.com
とwarehouse.example.com
はどちらもデータベース・サーバーになります。
sales.example.com
はinventory表を更新するため、warehouse.example.com
のクライアントになります。
この段階では、この分散トランザクションのセッション・ツリーの定義が完了します。ツリー内の各ノードは、必要なデータ・ロックを獲得して、ローカル・データを参照するSQL文を実行します。これらのロックは、SQL文の実行が完了した後も、2フェーズ・コミットが完了するまで保持されます。
データベースは、COMMIT
文の直後にコミット・ポイント・サイトを判別します。図34-8
のように、グローバル・コーディネータのsales.example.comがコミット・ポイント・サイトとして判別されます。
準備段階では、次の手順が実行されます。
データベースがコミット・ポイント・サイトを判別した後、グローバル・コーディネータは、コミット・ポイント・サイトを除くセッション・ツリーの直接参照先のノードすべてに準備メッセージを送ります。この例では、準備を要求されるノードはwarehouse.example.com
のみです。
ノードwarehouse.example.com
は準備を試みます。トランザクション内のローカルに独立した部分をコミットし、自身のローカルREDOログにコミット情報を記録できることをノードが保証できれば、そのノードは正常に準備できます。この例では、sales.example.com
がコミット・ポイント・サイトなので、warehouse.example.com
のみが準備メッセージを受け取ります。
ノードwarehouse.example.com
は、準備完了メッセージでsales.example.com
に応答します。
各ノードが準備を終えると、準備を要求した側のノードに応答メッセージが送り返されます。応答に応じて、次のいずれかの動作が発生します。
準備を要求されたノードのいずれかが、グローバル・コーディネータに対して異常終了メッセージで応答した場合、グローバル・コーディネータはすべてのノードに対してトランザクションのロールバックを指示し、操作が完了します。
準備を要求されたすべてのノードがグローバル・コーディネータに対して準備完了メッセージまたは読取り専用メッセージで応答した場合、つまり、正常に準備できた場合は、グローバル・コーディネータがコミット・ポイント・サイトに対してトランザクションのコミットを要求します。
コミット・ポイント・サイトによるトランザクションのコミットでは、次の手順が実行されます。
ノードsales.example.com
は、warehouse.example.com
の準備が完了しているという確認を受け取り、トランザクションをコミットするようにコミット・ポイント・サイトに指示します。
この時点で、コミット・ポイント・サイトはトランザクションをローカルにコミットし、この操作を自身のローカルREDOログに記録します。
warehouse.example.com
がまだコミットを完了していない場合でも、このトランザクションの結果は事前に決まっています。言い換えれば、指定したノードのコミット機能が遅延したとしても、トランザクションはすべてのノードでコミットされます。
この段階では、次の手順が実行されます。
コミット・ポイント・サイトは、トランザクションがコミットされたことをグローバル・コーディネータに伝えます。この例ではコミット・ポイント・サイトとグローバル・コーディネータが同一のノードであるため、操作は必要ありません。コミット・ポイント・サイトは、トランザクションがコミットされたことを自身のオンライン・ログに記録しているため、この事実を把握しています。
グローバル・コーディネータは、分散トランザクションに関係している他のすべてのノードでトランザクションがコミットされたことを確認します。
トランザクション内のすべてのノードによるトランザクションのコミットでは、次の手順が実行されます。
コミット・ポイント・サイトでのコミットがグローバル・コーディネータに通知された後、グローバル・コーディネータは、直接参照している他のすべてのノードに対してコミットを指示します。
コミットの指示を受けたローカル・コーディネータは、次に自身のサーバーに対してコミットを指示し、以下同様にコミットの指示が伝播されます。
グローバル・コーディネータを含む各ノードは、トランザクションをコミットし、該当するREDOログ・エントリをローカルに記録します。各ノードのコミットが完了すると、そのトランザクションのためにローカルに保持されていたリソース・ロックが解放されます。
図34-10では、コミット・ポイント・サイトとグローバル・コーディネータを兼務しているsales.example.com
が、トランザクションのローカルのコミットをすでに完了しています。この時点でsales
はwarehouse.example.com
に対してトランザクションのコミットを指示します。
トランザクションのコミットの完了は、次の手順で実行されます。
すべての参照先ノードとグローバル・コーディネータがトランザクションのコミットを完了した後、グローバル・コーディネータはこれをコミット・ポイント・サイトに通知します。
このメッセージを待っていたコミット・ポイント・サイトは、この分散トランザクションに関するステータス情報を消去します。
コミット・ポイント・サイトは、完了したことをグローバル・コーディネータに伝えます。つまり、コミット・ポイント・サイトは、分散トランザクションのコミットに関する情報を保持しません。2フェーズ・コミットに関係しているすべてのノードでトランザクションのコミットが正常に完了すると、それらのノードで今後ノード自身のステータスを判断する必要はないため、このような処理が許可されます。
グローバル・コーディネータは、トランザクション自体に関する情報を消去することでトランザクションを完了します。
COMMIT
フェーズの完了後、分散トランザクションそのものが完了します。これまで説明した手順は、1秒以内に自動的に実行されます。