細粒度ロックのメリットと問題

ロックする単位をデータベース全体ではなく、分割して口座ごとのような細粒度にしてやれば、プロセサAは口座1をロックして専用使用し、プロセサBは口座2をロックして専用使用するという風に並列処理ができ、両方が同じ口座にアクセスする場合だけが排他制御でどちらかが待たされるということになる。

しかし、このように口座ごとに細粒度のロックを行うと、プロセサAが口座1から口座2への振り込みを処理し、プロセサBが口座2から口座1への振り込みを処理する場合、プロセサAが口座1をロックしプロセサ2が口座2をロックすると、プロセサAは振込先の口座2のロックを得られないし、プロセサBは口座1のロックを得られないので、デッドロックに陥ってしまう。

このような2つのプロセサと2つのロック変数の場合は比較的単純であるが、より多くのプロセサとロックが絡み合ってくると、複雑なデッドロックやライブロックが発生する。また、このような現象はタイミング依存であり、同じことをやってもロックの獲得のタイミングがちょっと違うと発生せず、再現してデバグするのも容易ではない。また、デッドロックなどが起こった場合の解消手段もプログラムに組み込んでおく必要があり、マルチプロセサのプログラムが難しいという原因になっている。

トランザクションメモリによる問題の解決

この問題の解決法として提案されているのが、トランザクションメモリ(トランザクショナルメモリともいう)という手法である。トランザクションメモリでは、前の例の口座の残高を読み、引出額を引いて、残高を更新して書き戻すという、別のプロセサの残高アクセスを排除して実行する必要がある一連の処理をトランザクションと呼ぶ。

そして、図3に示すように、トランザクションの中で読み込むデータの集合をRead Set、書き込むデータの集合をWrite Setと呼ぶ。トランザクションの開始から終了までの間に他のプロセサBがトランザクションAのRead SetとWrite Setのメモリアドレスに書き込みを行わなければ、これらのトランザクションには干渉が無いので、それぞれのプロセサで並列に実行しても問題は生じない。

図3 Read SetやWrite Setに他のプロセサが書き込むと干渉が起こる

一方、トランザクションの終了までに、そのRead SetやWrite Setに対して他のプロセサが書き込みを行った場合は、入力データが書きかえられていたり、出力データを上書きしたりする可能性があり、そのトランザクションは正しく実行できない。ハードウェアでトランザクションメモリをサポートする場合、このようなケースをハードウェアが検出して、そのトランザクションを中止(Abort)する。そして、ソフトウェアは中止されたトランザクションをやり直す。

このようにすれば、細粒度ロックを使わなくても、口座番号が独立で干渉のない大分部のケースでは複数のプロセサで並列に処理し、同じ口座番号をアクセスするケースはハードウェアが検出して処理を中止し、排他的に順番に実行するということができる。

つまり、トランザクションメモリを使うと、プログラミングの難しい細粒度ロックを使わなくとも、大部分のケースでは複数のプロセサで並列処理ができ、干渉のあるケースだけを容易にやり直すことができるようになる。このため、トランザクションメモリはマルチプロセサ時代のプログラム作成を容易にする切り札の1つと見られている。