トランザクションが「終わる」のはいつか
ところで、前回で私は、「『障害発生前に終了しているトランザクションの変更は救う』というのが耐久性(D)の定義だ」と述べましたが、このときの「終了している」とはどういう意味なのでしょう。言い換えると、トランザクションは、いったいいつ「終わる」のでしょう。
COMMIT/ROLLBACKには時間がかかる
直観的に考えれば、それは「COMMIT(またはROLLBACK)が実行されたとき」です。そして、ここにおいて私たちとDBMSとの間に認識のズレはありません。障害発生前にCOMMITまでたどり着いたトランザクションは、障害後にも救われます。そんなの当たり前のことではないか、と思うかもしれませんが、DBMSの内部処理という観点から見ると、ここにはクリアすべき問題があるのです。
もしトランザクションが、物理的にデータファイルへすべての変更を反映し終わったタイミングをもって「終わり」と判断されるならば、特に問題はありません。データは堅固なディスクに保存されているため、たとえDBMSに障害が発生しても、再起動すればトランザクションが変更したあとのデータが生き残っています(仮にディスクに物理的な障害が起きた場合でも、冗長化などの対策がとられていればデータは救われます)。
しかし、本連載の第1回で見たように、実際には多くのDBMSは、データファイルへ変更を反映する前に、トランザクションの終了をユーザに通知しています。そうすると、COMMITが発行されたあとで、かつ、データファイルへ変更が反映される前に障害が発生した場合、トランザクションのデータは失われてしまうことにならないでしょうか? もしそうなら、ACIDの「D」を満たさないことになる深刻な事態です。
WALでひとまずログファイルに書き込む
現実には、それは起こりません。この損失を防止するためにDBMSが備えているしくみが、一般にWAL(write-ahead log)と呼ばれている方法です。日本語ではログ先行書き込みと訳します。名前のとおり、COMMIT時にデータファイルにすべての変更を反映する代わりに、ログファイル(「ジャーナルファイル」とも呼びます)に当該トランザクションで行われた操作の記録を書き出す方法です。これは、データファイルに変更を書き込むよりずっと書き込み量が少ないので、短時間で終わります[3]。余談ですが、WAL はWindows のNTFS(NT File System)やLinuxといったファイルシステムでも用いられていて、ジャーナリング(journaling)と呼ばれています。
DBMSは、COMMIT時にはいったんログファイルに操作ログだけ残しておいて、データファイルへの変更反映は後でゆっくりやっているのです(このデータファイルへの反映処理をチェックポイントと呼びます)。実装によっては、ログファイルに操作コマンドだけでなく、変更前・変更後のデータを直接保持するようなしくみを持つ場合もありますが、それでもデータファイルに比べればログファイルのサイズはずっと小さなものになります(図2)。
WALによって、COMMITしてから変更がデータファイルに書き込まれるまでに障害が発生した場合でも、ログファイルさえ残っていれば、DBMSの再起動時にそのログを参照して、もう一度自動的にトランザクションを再実行すれば変更を正しく反映することが可能になるのです。この再実行処理を、ロールフォワードと呼びます。ちょうどロールバックの反対語です。もちろん、こんな面倒なことをせず、COMMIT時にいきなりデータファイルを更新してしまえばずっとしくみは簡単になるのですが、それだと巨大なデータファイルの更新コストが大きいため、COMMIT時にユーザを長く待たせることになってしまうのです。ここでも支配者はトレードオフの原則です[4]。
障害発生時のトランザクション状態と復旧時の動作
具体的に、図3のような5つのトランザクションのケースを考えてみましょう。
今、時刻Tmで不運にもDBMSが障害に見舞われシステムがクラッシュしました。幸い物理障害ではなかったため、DBMSの再起動は無事行うことができました。さて、T1~5の結果はどのようになるでしょうか。
まず、簡単にわかるところとしては、T1はまったく何の問題もなく結果が保証されます。T1は、チェックポイントでデータファイルに同期済みのため、再起動時に何の処理も行う必要がありません。
時刻Tmでトランザクションが実行中だったT3とT5は残念な結果になります。再起動時にロールバックによって「なかったこと」とされてしまうのです。ログファイルが完全ではないためロールフォワードもできないので、ユーザが手動で再実行するしかありません。
注目すべきがT2とT4です。この2つは、障害前にトランザクションが終了しているため、結果は保証の対象となります。ただし、T1と違って、チェックポイントより後にもトランザクションが実行されているため、データファイルはまだ同期されていません。変更履歴が残っているのはログファイルだけですので、再起動時にロールフォワードが実施されます。障害があってDBMSを再起動するとき、いつもより余計に時間がかかることがありますが、それは裏で上述のようなロールバック/ロールフォワードが行われているからです。
このように、DBMSは常に処理のログを記録して残しておくことで、障害時においてもトランザクションの結果を保証できるわけです。このことを、コンピュータ科学者のJim Grayは「ログは何でも知っている」という印象的な言葉で表現しました。