Atom Login Admin

Above the clouds

Google App Engine Datastore #4 ~ Datastoreのトランザクション分離について ~

written on Sunday,March 03,2013

Datastoreのトランザクション分離について

トランザクション分離レベルについて

Wikipediaより

トランザクション分離レベル (-ぶんり-)または 分離レベル (英: Isolation) とは、データベース管理システム上での一括処理(トランザクション)が複数同時に行われた場合に、どれほどの一貫性、正確性で実行するかを4段階で定義したものである。隔離レベル 、 独立性レベルとも呼ばれる。トランザクションを定義づけるACID特性のうち,I(Isolation; 分離性, 独立性)に関する概念である。

これらは、複数の処理を並行で行っているときに生じる待ち状態を防ぐ為に「待ち時間をを減らすためどれだけデータの一貫性を犠牲にして良いか」を定めたものである。

分離レベルは以下の四種類が定義されている。

Wikipediaより

・SERIALIZABLE ( 直列化可能 )
複数の並行に動作するトランザクションそれぞれの結果が、いかなる場合でも、それらのトランザクションを時間的重なりなく逐次実行した場合と同じ結果となる.このような性質を直列化可能性(Serializability)と呼ぶ.SERIALIZABLEは最も強い分離レベルであり、最も安全にデータを操作できるが、相対的に性能は低い。ただし同じ結果とされる逐次実行の順はトランザクション処理のレベルでは保証されない。
・REPEATABLE READ ( 読み取り対象のデータを常に読み取る )
ひとつのトランザクションが実行中の間、読み取り対象のデータが途中で他のトランザクションによって変更される心配はない。同じトランザクション中では同じデータは何度読み取りしても毎回同じ値を読むことができる。ただし ファントム・リード(Phantom Read) と呼ばれる現象が発生する可能性がある。ファントム・リードでは、並行して動作する他のトランザクションが追加したり削除したデータが途中で見えてしまうため、処理の結果が変わってしまう。
・READ COMMITTED ( 確定した最新データを常に読み取る )
他のトランザクションによる更新については、常にコミット済みのデータのみを読み取る。 MVCC はREAD COMMITTEDを実現する実装の一つである。
ファントム・リード に加え、非再現リード(Non-Repeatable Read)と呼ばれる、同じトランザクション中でも同じデータを読み込むたびに値が変わってしまう現象が発生する可能性がある。
・READ UNCOMMITTED ( 確定していないデータまで読み取る )
他の処理によって行われている、書きかけのデータまで読み取る。PHANTOM 、 NON-REPEATABLE READ 、さらに ダーティ・リード(Dirty Read) と呼ばれる現象(不完全なデータや、計算途中のデータを読み取ってしまう動作)が発生する。トランザクションの並行動作によってデータを破壊する可能性は高いが、その分性能は高い。

このうちデータストアのトランザクションはSERIALIZABLEの分離レベルを満たしてる。
したがって、あるエンティティグループに対するトランザクションは1つ1つ直列に実行される。
一方、トランザクション外部ではREAD COMMITTEDとなる。

以前取り上げたデータストアへの書き込みのライフサイクルについて2つのフェーズ(コミットフェーズと適用フェーズ)で成り立っていることを説明したが、トランザクション内の更新では以下のプロセスをとっている。

コミットプロセス

Commitが成功した場合、適用されるのは保証するが、全ての適用が即座に全ての読み取りに現れるものではない。
トランザクションの適用には二つのマイルストーンにより構成されている。

トランザクションのマイルストーン

マイルストーンAはエンティティに対する変更が適用された地点
マイルストーンBはインデックスに対する変更が適用された地点

High Replication Datastore(HRD)では、コミットが返ってきた後数百ミリ秒後に換算に適用される事になる。
ただし、まだ完全にコミットが適用されていない場合でもそれらの処理は他の処理が適用された後に実行されるので、後続の読み込み書き込み、先祖クエリはコミットの結果が反映されたものが返却される。
しかし、複数のエンティティグループにまたがるクエリが実行する前に、未適用の変更があるかどうかを判断できないと失効しているか部分的に適用された結果を返すことがある。

マイルストーンAの後で、更新されたエンティティをそのキーで検索する要求を行うと、必ず更新後の最新のエンティティが返されるが、並行してGQLのWHERE句に更新前のエンティティではなく更新後のエンティティに対する抽出を行うようなクエリーが実行された場合は、抽出できない場合がある。したがって、マイルストーンBに到達した後に実行した場合にのみ結果のセットに含まれる。
つまり、少しの間だが、キーによるクエリでの抽出で結果セットに含まれない場合がある。
マイルストーンAとBの間における変更はクエリに反映することはできないが、クエリでどのエンティティーを結果に返すか、また、クエリで特定のエンティティーを返すことが決定されている場合は、最新のバージョンのエンティティーを返すことができる。
Master/Slaveデータストアにおけるトランザクショナルな先祖クエリやHRDの先祖クエリにあたるものでは、保留中の変更でもクエリの実行後に完全に適用されることを保証するもので、どちらも最新で一貫性のとれた結果を返す。

Bobの身長の変更の例のようにマイルストーンAとマイルストーンBの間でクエリを実行する場合とマイルストーンAの前、マイルストーンBの後で結果セットが変わる様子が示されている。
要するにマイルストーンAはエンティティーに対する変更がコミットされクエリの結果などに影響するコミットはマイルストーンBとなるということである。(よって、マイルストーンA後のキーが指定されているクエリは常に最新のエンティティが返ってくる事になる)

このようにトランザクションの分離レベルについて理解し、設計することが必要である。
もう一度整理するとトランザクション内部での分離レベルはSERIALIZABLEで、一方、トランザクション外部ではREAD_COMMITEDに近いということ。
次回はトランザクションを実際に利用するケースを理解したい。
(スナップショット分離についてはまとめて資料としてアップしておこうと思っている)

Comments

Add Comment

Login
This entry was tagged #GAE #Datastore