マイクロサービスにおけるトランザクション分離性(Isolation)の問題


マイクロサービスについて色々と探求しているうち、「分離性」について混乱してきました。そこで、自分なりに整理してみようと思います。

ACID特性における分離性(Isolation)とは

分離性とは、トランザクション実行中の途中経過が、別のトランザクションから観測できない、という性質です。

例えば、トランザクションAが
 ①田中さんの口座残高を3万円減らす
 ②山田さんの口座残高を3万円増やす
という処理をする場合、別のトランザクションBは「①だけが完了ている。②は未完了。」という時点のデータを参照できない、ということです。

これができてしまう現象はダーティーリードであり、これができてしまうとデータの整合性を保てません。

分離性が満たされている場合、あるトランザクションBは、以下の2つしか観測できません。
 ・別のトランザクションAが全く実行されていない状態
 ・別のトランザクションAが完全に実行された状態

誤った説明(注意:あくまで私見です)

一般的な説明としてよくあるのが「分離性とは、並列に実行した場合と逐次的に実行した場合とで、結果が同じになること」というものです。しかし、これは誤りかなと思っています。分離性とは本来上記の概念(途中経過を外側から観測できない)を指すのであり、分離性があるだけでは、並列実行と逐次実行とで異なる結果になる可能性があります。

例えば、トランザクションAの実行中に、別のトランザクションBがテーブルに1件のレコードを追加したとします。トランザクションAがトランザクションBの処理の前と後でテーブルの件数を取得すると、前と後ではテーブル件数が異なります(ファントムリード)。トランザクションAが、このテーブル件数をもとに何らかの処理をするとすれば、逐次的に実行した場合と結果は異なる可能性があります。

上記のケースでは、「並列に実行した場合と逐次的に実行した場合とで、結果が同じになる」ということは、どう頑張っても実現できません。

なお、 wikipedia「ACID (コンピュータ科学) 」によると、ACID特性の正式な定義は「ISO/IEC 10026-1:1992 Section 4」にあるとのこと。ここに分離性がどう定義されているのかが気になってきました。というのも、インターネット上を検索して出てくるACID特性の定義は「野良」なものばかりで原典をそのまま載せているものが無さそうだったからです。「ISO/IEC 10026-1:1992 Section 4」をインターネット上で閲覧することはできなかったので、図書館に行って読んでみようと思います。

「分離性」と「トランザクション分離レベル」との関係

トランザクション分離レベル 分離性に着目した説明
READ UNCOMMITED 分離性を満たさないレベル
READ COMMITED 分離性を満たすレベル
REPEATABLE READ 同一のデータを読み込む複数のトランザクションを逐次的に実行する。例えばトランザクションAがあるデータを読み込んだら、他のトランザクションBはトランザクションAが完了するまでそのデータを読み込めない。同時に実行しないので分離性はもはや関係ない。
SERIALIZABLE 同時に発生したトランザクションを、その内容に関わらず交通整理して本当に逐次的に実行するレベル。同時に実行しないので分離性はもはや関係ない。

マイクロサービスと分離性

マイクロサービスでは、上記の意味での分離性が満たされません。例えば、2つのマイクロサービスに跨って更新処理を行う場合、1つ目のサービスで更新処理が完了したが2つ目のサービスでの更新処理は未済という場合、1つ目のサービスの更新結果はすぐに他の処理に見えるようになってしまいます。

複数のサービスに跨って更新処理を行う場合、原子性(Atomicity)を心配されることが多いような気がしています。つまり、どこか一部のサービスが失敗した場合に、他のサービスをロールバックしなければならない、ということに注目が集まりやすいです。

その点ももちろん重要なのですが、分離性が満たされないことによって生じる悪影響をいかに扱うか、ということも同じくらい重要です。

マイクロサービスにおける分離性との付き合い方

あまり定説は無いような気がしますが、Sagaという手法が現実的なように感じています。これについては、また別の記事でご紹介できればと思います。