Atom Login Admin

Above the clouds

スナップショット分離について

AppEngine Datastoreのトランザクション分離レベルは、
トランザクション内はSERIALIZABLEでトランザクション外部ではREAD COMMITTEDに近いということを紹介した。

トランザクション外部でのデータストアの分離レベルは READ_COMMITTED に最も近くなります。一方、トランザクション内部での分離レベルは SERIALIZABLE で、具体的には一種のスナップショット分離です。

スナップショット分離について

スナップショット分離についてWikipedia - Snapshot isolationに載っている内容を記載した。

スナップショット分離

データベースや、トランザクション処理(トランザクション管理)でスナップショット分離とは、
トランザクションで読み込まれるデータは一貫性の取れたデータベースのスナップショットで、トランザクションのコミットはスナップショットが作成されて以来からの他の並行した更新処理と競合を起こさない場合のみに成功する。

スナップショット分離はいくつかの主要なDBMSに採用されている。それはSQL Anywhere, InterBase, Firebird, Oracle, PostgreSQL,Microsoft SQL Server(2005以降)である。
これらを適用する主要な理由は、直列化可能性よりもパフォーマンスで優れているからで、いつもではないが、直列化可能性が回避するほとんどの並行処理の異常を回避することができる。スナップショット分離の実践としては、MVCC(Multi concurrency control)内で実装されているデータの世代を維持する為の仕組みである。MVCCはトランザクション内でいくつかの関連する最終バージョンを読み込み処理できるのとそれぞれのデータが書き込まれた時点で、新しい世代のデータを生成することにより、並行性とパフォーマンスを同じ方法で増進させている。スナップショット分離は、SQLの標準で禁止している異常を表していないまだSerializableでないとしてANSI SQL-92の標準定義の批判にも使われている。※1A Critique of ANSI SQL Isolation Levels - Microsoft Research
スナップショット分離はOracleやPostgreSQLのVer9.1よりserializableと呼ばれているので、これが、本物の直列化可能性と混乱する原因となっている。
これらの両方の議論がデータベースのシステムロジックで望まない異常とユーザーが明確に区別する為に気づくべきの決定になる。

定義

トランザクションがスナップショット分離の元で行われるのは、データベースの個々のスナップショットでの処理でトランザクションの開始時点に取られる。トランザクションの結末で、スナップショットが取られた時点から、外部のトランザクションによって、値の変更が行われていない場合のみコミットが成功する。書き込みで競合が起きた場合はトランザクションを失敗させる。Write skew異常では、二つのトランザクションで同じデータセットを読みこんでいて、互いに違いに並行で更新を行う。最終的にコミットを行うが、お互いに参照していたデータは他によって更新されている。SerializableのシステムではT1またはT2のどちらかが"first"を発生することがあり、他にも見えるので、このような異常は不可能である。反対にスナップショット分離ではWrite skew異常を許可している。
具体的な例で紹介すると、1人の人によって保持されているV1とV2という残高があるとする。銀行はV1とV2のどちらかが赤字になった場合に両方の総合金額が0以下にならないようにできる(V1+V2≥0)。両方の残高は$100とする。二つのトランザクションを並行で初期化し、T1はV1から$200引き落とし、T2はV2から$200引き落とす。
データベースが直列化トランザクションを保証していれば、単純にT1はV1から$200を差し引き、V1+V2が0以上である事を検証する。もし0以下だった場合は失敗する。T2も同様である。トランザクションは直列化する必要があるので、T1は最初に処理を行い、V1を-100$、V2は$100とし、T2を成功させないようにする(V1+V2-$200は-$200になってしまう。)または、T2が最初に処理した場合も同様にコミットを予防する必要がある。
スナップショット分離では、T1とT2はそれぞれのスナップショットで処理をしてしまう。それぞれ$200を口座から差し引く、そして、他の口座の値はスナップショットが取られた時点のものを使い、新しい総数が0になることを検証する。V1とV2は-$100でV1+V2=-200$でどちらの更新も競合し、コミットは成功する。
もしMultiversion concurrency controlで構築されていれば、並行処理について心配する事無く、スナップショット分離のトランザクションを処理する事ができるのと、更に重要なことには、トランザクションが最終的にコミットされるときに全ての読み込みを再検証する必要がないことである。
コミットする前に衝突を簡単に検索出来るようにするトランザクション中に更新したリストという唯一の情報を格納しておく必要がある。

直列化可能スナップショット分離

Write skew異常に起因する潜在的な矛盾の問題は、直列化可能性プロパティを適用するために、取引に(そうでなければ不必要な)更新を追加することで修正できる。
・Materialize the Conflict:両方のトランザクションが直接的は書き込み競合に従って更新する、特別な競合テーブルを追加する。
・Promotion:書き込み競合にしたがって、一つのトランザクションは読み込み専用の"update"ロケーションを持つ。(OracleのSELECT FOR UPDATEがそれに値する)

上記の例で例えると、それらの合計残高を一人一人をマッピングする、隠された制約が明示的に設定される競合テーブルを追加することにより競合を具体化することができる。上記例だと開始時点を$200が残高とし、それぞれのトランザクションはここから$200を差し引く、書き込み競合テーブルを作成することにより、並行で二つのトランザクションが成功することを防ぐ。
このアプローチはviolates the normal formである。
代わりに、Promotionは一つのトランザクションを書き込みの為に読み込む事が出来る。例えば、T2がV1=V1と設定できると、人口てきな書き込み競合をT1で作成することができる。そして並行で二つが成功することを防ぐことができる。この解決法は全てに適用できるわけではない。
一般的には従って、スナップショット分離はユーザーへ潜在的な落とし穴や可能な解決法に感謝する事がないであろうと思われる維持の非自明の制約のいくつかの問題を与える事になる。スナップショット分離の利点はパフォーマンスの良さである。
トランザクション間の追加の通信では、スナップショット分離が正常に許可している異常は完全直列化可能性の保証にスナップショット分離の実装を変更して関連するトランザクションのいずれかの操作を中止することによって阻止することができる。
この実装は、multiversion concurrency controlデータベースに適していて、PostgreSQL 9.1に採用されている。
これはSerializable Snapshot Isolation(SSI)で言及している。一貫性を使用する際、上記で必要な回避策は必要出なくなる。スナップショット分離の欠点はトランザクションの失敗が増加する事である。上記の回避策のスナップショット分離よりもパフォーマンスを良くできるも悪くするのもそのワークロードが関わっている。

歴史

スナップショット分離は、書き込みで衝突なしに実行する為に読み込みに許可することを並行で維持する複数バージョンのデータベースである、multiversion concurrency controlデータベースから生じた。
自然な定義と、分離レベルの実装を許可するようなシステムである。後でボーランドが所有するInterBaseは、はるか昔1984年にSIを提供しました。残念ながら、ANSI SQL-92標準はロックベースのデータベースの思想で書かれていて、故にMVCCのシステムに適用した場合、かなり曖昧である。Nerensonは1995年にSQL標準への批判の論文を書き、いまだ直列化トランザクションで比較した際の振る舞いであり、ANSI SQL-92標準では記述されていない標準の異常を出典していないということで、例としてスナップショット分離を引用した。