分散トランザクションに挑戦しよう!

〜 第二回 2 フェーズコミットのしくみ〜

株式会社 オージス総研
UML ソリューション部
永田 博靖
Nagata_Hiroyasu@ogis-ri.co.jp

目次

1. はじめに
2. 分散トランザクションを実現する 2 フェーズコミット
3. トランザクションマネージャ
4. 2 フェーズコミットは本当に原子性を保証する?
5. X/Open の分散トランザクション処理参照モデル
6. 次回は・・・
7. 参考資料

1. はじめに

前回は、トランザクションが持つべき ACID 特性とトランザクション処理の概要について説明しました。第二回目では、分散トランザクションを実現する 2 フェーズコミットについて取り上げます。トランザクションという言葉を聞いて (?_?) になっている方は、まず第一回目の記事を見てください。トランザクションの ACID 特性と JDBC のトランザクション機能について知っている方は、第二回目から読み始めて頂いても問題ありません。 ちなみに、トランザクションの ACID 特性とは、原子性、一貫性、独立性、耐久性の 4 つを表す用語でしたね。

2. 分散トランザクションを実現する 2 フェーズコミット

分散トランザクションは、一つのトランザクションが複数のデータベースに対してアクセスするトランザクションを意味します。分散トランザクションをサポートしない JDBC のようなトランザクション機能では、複数のデータベースに対してアクセスする場合、図 1 のように原子性を損なう問題がありました。つまり、データベース A に対してコミットが成功し、データベース B に対してコミットが失敗した場合、データベース B に対してロールバックを実行したとしても、データベース A に対する変更までもロールバックできないという事態が発生してしまうということです。これが、JDBC のトランザクション機能で原子性を保証できない原因でした。

JDBC トランザクションでは原子性が失われる
図 1JDBC トランザクションでは原子性が失われる

では、複数のデータベースに対してアクセスする場合、どのようにすれば原子性を保証できるのでしょうか。これを解決するのが、図 2 に示す 2 フェーズコミットと呼ばれる方法です。

複数のデータベースに対して原子性を保証する解決策
図 2 複数のデータベースに対して原子性を保証する解決策

2 フェーズコミットでは、図 2 のようにトランザクションのコミット処理を 2 段階のフェーズにわけることによって原子性を保証します。ちなみに図 2 の UML 表記は、厳密ではありません。どのようなメッセージが交換されるのかについてのみ注目してください。

第 1 フェーズでは、まず、各データベースに対してコミットできる状態であるかどうかを確認するための準備 ( 図 2 の prepare ) の指示を送ります。これを受けた各データベースは、コミットできる状態かどうかをアプリケーションに伝えます。この処理を「投票する」と呼びます。コミットができる状態であれば、コミット予定の内容を確定させた後、アプリケーションに対して「準備完了」( 図 2 の ok ) を投票します。もし、データベースがコミットの準備を完了できない場合は、アプリケーションに対して「拒否」を投票します。第 1 フェーズは、アプリケーションがすべてのデータベースから投票を受信するまでの段階を表します。

第 2 フェーズでは、第 1 フェーズの各データベースの投票結果をもとに、すべてのデータベースに対してコミットかロールバックかの決定を行います。すべてのデータベースから「準備完了」の投票を受け取った場合のみ、各データベースに対してコミットの指示を送ります。この指示によって、該当のトランザクションが実際にコミットされます。しかし、データベースから 1 つでも「拒否」の投票を受け取れば、すべてのデータベースに対してロールバックの指示を送ることになります。また、指定時間内にデータベースから投票結果を受け取れなかった場合にも、すべてのデータベースに対してロールバックの指示が送られます。この指示によって、該当のトランザクション全体が差し戻されます。第 2 フェーズは、すべてのデータベースがコミットかロールバックかを実行する段階を表します。

このように、2 フェーズコミットは、コミットのための準備段階とコミット処理を行う実行段階を分けることによって、トランザクションの原子性を保証します。そして、データベースは 2 フェーズコミットと連携することによってトランザクションの ACID 特性を保証します。

3. トランザクションマネージャ

さて、アプリケーションが、分散トランザクションの原子性を保証するために、図 2 のような 2 フェーズコミットを制御するのは少々面倒です。また、トランザクションが単一のデータベースを利用するのか、複数のデータベースを利用するのかによってトランザクションの制御を変更するのもシステムの構成に依存し、メンテナンス性に欠けます。この問題を解決するには、アプリケーションからトランザクションの制御に関する機能を抜き出し、その処理を別のコンポーネントに任せればよいですね。このトランザクション制御を行うコンポーネントが、トランザクションマネージャと呼ばれるものです。

トランザクションマネージャを導入すると、トランザクションは 図 3 のように処理されます。

トランザクションマネージャの導入
図 3 トランザクションマネージャの導入

トランザクションマネージャの導入によって、アプリケーションプログラムから 2 フェーズコミットにおける処理が隠蔽されました。また、分散トランザクションを単一のデータベースにおけるトランザクション処理と同様な手順でコミットできるようにもなっています。

分散トランザクション機能をサポートする環境は、トランザクションマネージャのコンポーネントを提供します。トランザクションマネージャが存在するおかげで、分散トランザクションの開始から終了までの処理を単一のデータベースにおけるトランザクション処理と同様な手順で実行できるようになります。CORBA や J2EE、Web サービスにも、トランザクションマネージャに該当するコンポーネントが存在し、これによって分散トランザクションが実現されています。

4. 2 フェーズコミットは本当に原子性を保証する?

トランザクションマネージャによって、アプリケーションプログラムは 2 フェーズコミットを意識する必要はなくなりましたが、2 フェーズコミットについてもう少し詳しく見ておきましょう。

2 フェーズコミットは、分散トランザクションの処理において、起こりうるほとんどの障害をうまく取り扱えるように設計されています。ここでほとんどの障害という言い回しを使ったことに注意してください。実は、2 フェーズコミットには、原子性を破る可能性のある状況が存在するのです。

データベースは、トランザクションマネージャに対して投票を行なう前なら一方的にいつでもロールバックを行うことができます。しかし、データベースが「準備完了」を投票した後は、トランザクションマネージャの決定を含むメッセージを受け取るまでコミットもロールバックも行なえません。この区間を「不明」と呼びます (図 4)。

2 フェーズコミットの不明区間
図 4 2 フェーズコミットの不明区間

「不明」は 2 フェーズコミットの悪い性質です。データベースが「不明」である間にトランザクションマネージャに障害が発生すると、データベースはブロックされることになります。つまり、データベースは、コミットもロールバックもできないことになり、トランザクションがアクセスしていたデータに対してデータベースがロックを保持したままになります。また、データベースが「不明」のときに障害を起こして、続けて回復を行なうとき、トランザクションマネージャが停止している可能性もあります。この場合にも、データベースは、「不明」のままとなり、最後まで回復することができません。データベースはトランザクションをコミットすべきかロールバックすべきかわからないからです。

この不明区間で発生するブロックを回避する方法として、ヒューリスティックな決定があります。ヒューリスティックな決定とは、単にトランザクションの最終結果を発見的手法で推定することです。(ヒューリスティックとは、「経験則」という意味です)。推定結果は実際の結果と異なることもありますが、データベース内で独自にコミットかロールバックを実行することによって、データベースに対するロックを開放することができます。では、このヒューリスティックな決定が、その後、トランザクションに対してどのような結果を与えるか見てみましょう [1]

トランザクションのヒューリスティックな結果
図 5 トランザクションのヒューリスティックな結果

図 5 のような状況でトランザクションマネージャに障害が発生したとします。この場合、データベースはトランザクションマネージャからの決定の指示を受け取ることができないので、ロックを開放するためにヒューリスティックな決定を行います。しばらくして、トランザクションマネージャが復旧しました。トランザクションマネージャは、データベースとコンタクトを取って、2 フェーズコミットの結果に基づいて、トランザクションをコミットするかロールバックするかの指示をデータベースに送ります。データベースのヒューリスティックな決定が分散トランザクションの結果の成り行きと一致する場合は、解決すべき問題はなく、万事通常どおり進行します。しかし、データベースのヒューリスティックな決定がトランザクションマネージャの決定と食い違う場合には、データベースは、2 つの符号が一致しないことをトランザクションマネージャに知らせることになります。つまり、これは、その該当のトランザクションの原子性が破られたかもしれないということです。

符号が一致しないという結果は、トランザクションマネージャから、コミットを指示したアプリケーションプログラムにエラーとして通知されます。この場合、アプリケーション側でデータベースの一貫性を元に戻すような対処をするか、エラーをシステム管理者に報告してそれを解決するために人間が介入するか、といった手段を取ることになります。

さて、2 フェーズコミットのこのような欠点を知って 2 フェーズコミットが使えないと思った方がおられるかもしれません。しかし、2 フェーズコミットのブロック問題を解決するプロトコルは存在せず、どのようなプロトコルでも、複数のデータベースに対してアクセスするトランザクションを原子的にコミットするなら、以下の問題を抱えていることが証明されています [2]

なお、この不明区間以外のさまざまな障害に対しては、データベースは、トランザクションマネージャと連携して、適切にコミット、または、ロールバックを実行して回復できるようになっています。実際にどのように回復するのかを詳しく知りたい方は、[2] を見てください。

5. X/Open の分散トランザクション処理参照モデル

分散トランザクションを実現するために必要なトランザクションマネージャとデータベース間で行なわれる 2 フェーズコミットにのみ注目して説明してきたわけですが、いくつかの異なったデータベースを利用する場合、そのインタフェースが統一されていないとうまく連携がとれません。

X/Open の分散トランザクション処理 (以下 DTP と略す) 参照モデルでは、分散トランザクション処理に関係するインタフェースを標準化しています( 図 6 )。DTP 参照モデルでは、トランザクション処理システムを、トランザクションマネージャ、リソースマネージャ、通信リソースマネージャのコンポーネントに分割し、これらのコンポーネント間のインタフェースを定めています。

ちなみに X/Open は、UNIX を基盤としたシステムの標準化を行なっている業界団体です。X/Open と OSF (こちらはオープンシステムと分散コンピューティング技術の普及を促進する業界団体 ) が合併して1996 年 2 月から The Open Group となっています。現在、X/Open は The Open Group の一部という位置付けです。

X/Open の DTP モデル
図 6 X/Open の DTP 参照モデル

図 6 には、さまざまな用語が登場しています。まず、これらの用語について説明します。

リソースマネージャ
( RM:Resource Manager )
RM は、永続的に資源を管理するどんなコンポーネントも含まれます。データベース、ファイルシステム、メッセージキューイングシステム、他のトランザクション内からアクセス可能な共有オブジェクトなどが該当します。
トランザクションマネージャ
( TM:Transaction Manager )
TM は 2 フェーズコミットを動かす、回復可能なプロセスです。TM は、アプリケーションプログラムのための基本的なトランザクションに関する操作(開始、コミット、ロールバック)を処理します。TM は 1 つ以上の RM が含まれるときに、原子性を保証するためにトランザクションの軌跡を保持する帳簿係です。
通信リソースマネージャ
( CRM:Communication Resource Manager )
CRM は、DTP システムに利用できる通信システムの API を提供します。DTP 参照モデルでは、3 つの異なる API が採用されています。TxRPC ( Transarc モデルを基盤とするもの )、XATMI ( TUXEDO の API とプロトコルを基盤とするもの )、CPI-C ( CICS の API と LU 6.2 プロトコルを基盤とするもの ) の 3 つです。
XA インタフェース
XA は、TM と RM の間の API を規定しています。TM や RM が分散トランザクションに参加するためには、両方とも XA の API を実装していなければなりません。
TX インタフェース
TX は、アプリケーションプログラムがトランザクションを開始したり、終了したり、また、その状態について問い合わせたりするための API を規定しています。
XA+ インタフェース
XA+ は XA インタフェースを TM と CRM 間に拡張したインタフェースです。これによって CRM は、従属ノードが分散トランザクションに参加したことを TM に通知することができます。

この DTP 参照モデルの中で重要なのが、TM と RM と XA の 3 つの用語とその関係です(図 7)。上記 2 フェーズコミットのところで説明しましたが、TM と RM という用語を使ってトランザクション全体の動作を整理しておきます。

X/Open の DTP モデル
図 7 TM と RM と XA の関係

アプリケーションプログラムは、TM を使用して、トランザクションの開始や終了を行います。RM はアプリケーションプログラムにインタフェースを提供し、そのトランザクションを構成する検索や更新といったオペレーションの実行を許します。RM がアプリケーションプログラムに提供するインタフェースは、TM を介さないでアクセスするときと同一のインタフェースです。つまり、RM がデータベースであるならば、アプリケーションプログラムは SQL を使って RM にアクセスすることになります。アプリケーションプログラムがトランザクションを終了すると、TM はそれぞれの RM とやり取りし、そのトランザクションの原子性を保証してトランザクションを終了します。このとき、TM は、その該当のトランザクションが一つの RM にアクセスしていたか、複数の RM にアクセスしていたかを認識し、その状況に応じて最適なコミットプロトコル( 1 フェーズコミット、または、2 フェーズコミット )を選択します。

さて、本記事の最後で X/Open の DTP 参照モデルについて取り上げましたが、これは次回以降で説明する CORBA や J2EE のトランザクション機能においても TM と RM 間に用いられる XA インタフェースが利用されるからです。CORBA や J2EE のトランザクション機能を利用した場合、TM がアプリケーションプログラムに対して提供するインタフェース( X/Open の DTP 参照モデルでは TX インタフェース)はそれぞれ異なりますが、TM と RM との連携については XA インタフェースが利用されています。これは、RM と TM の連携部分についてはアプリケーションプログラムが関与することではないので、アーキテクチャが異なっても利用できることを示しています。

6. 次回は・・・

これで、ようやく本連載記事のメインである CORBA、J2EE、Web サービスの分散トランザクション処理について説明できる準備が整いました。というよりも、この記事を書いている間にいろいろ調査する必要があり、そのための時間が必要だったわけでもありますが。。。さて、次回ですが、CORBA のトランザクション機能について説明したいと思います。CORBA のトランザクション機能からは、実装方法も交えて説明する予定です。

7. 参考資料

  1. 『 CORBA, Encina によるエンタープライズ・トランザクション』 イアン・ゴートン /著,飯塚 正樹, 下田 みどり /訳,HP コンサルティング /監修,ピアソン・エデュケーション.
  2. 『トランザクション処理システム入門』 フィリップ・A・バーンスタイン,エリック・ニューカマー /著,大磯 和広,小野沢 博文,木下 聡,仲山 恭央,早瀬 勝 /訳,日経 BP 社.

© 2003 OGIS-RI Co., Ltd.
Prev Index
Prev. Index