トランザクション分離レベル
まず、四つのトランザクション分離レベルから整理していきます。
これは、この記事に辿り着いた方は、すでに知っている方も多いと思うので、読みとばしていただいて大丈夫です。
今回は、トランザクションT1の直後にトランザクションT2が起ったとします。
この時に、トランザクションT1はデータ集合V1に対して処理(読み取り or 変更)をし、トランザクションT2は重複した部分集合を持つデータ集合V2に対して処理(読み取り or 変更)を及ぼすとします。
- Read Uncommitted
- 一番緩やかな分離レベルです。トランザクション中の変更はコミットする前に他のトランザクションから参照できます。 dirty readという問題が発生する可能性があります。
- dirty read とは、T1でコミットしていない変更をT2で読み取ってしまう問題です。
- Read Committed
- トランザクション中の変更はコミットするまで他のトランザクションから参照できません。しかし、同じデータに対して複数のトランザクションが並行して実行されると、non-repeatable readという問題が発生する可能性があります。
- non-repeatable readとは、T1の一つのトランザクション内で複数回データ集合V1を読み取る時に、トランザクションT2のコミットによってV2内のレコードの更新が先に完了すると、T1の同一トランザクション内でV1が更新されてしまうという問題です。
- つまり、トランザクション内でのレコードの一貫性に問題が生じることがありますよ。というものです。
- Repeatable Read
- トランザクション開始時に読み取ったデータは、そのトランザクション内で一貫性が保証されます。しかし、phantom readという問題が発生する可能性があります。
- phantom readとは、T1の一つのトランザクション内で複数回データ集合V1を読み取る時に、トランザクションT2のコミットによってV2内のレコードの追加や削除が先に完了すると、T1の同一トランザクション内でV1が更新されてしまうという問題です。
- つまり、トランザクション内でのテーブルの一貫性に問題が生じることがありますよ。というものです。(READ COMMITEDで起きる可能性のある、レコードの一貫性については保証される)
- Serializable
- 最もstrictな分離レベルです。トランザクションは完全に直列に実行されるかのように動作します。コンカレンシーが著しく低下しますが、あらゆる問題が回避できます。
Snapshot Isolation とは何か
Snapshot Isolationとは、そのトランザクションの開始時のスナップショットの上で一連のコミットが実現するように見えるものだ。
トランザクション分離レベルで言うならば、REPEATABLE READとSERIALIZABLEの間に相当する。
というのも、あるトランザクションに含まれる読み取り操作はそれがトランザクション内の最初の方で実行されようが最後の方で実行されようが常に同じ結果を得ることになるので、REPEATABLE READで起きえる、phantom read の問題は起きない。ですので、REPEATABLE READとは異なる。
ただし、以下のような問題が起きる。
- T1 と T2 はそれぞれ独自のスナップショットの上で動作するので、T1とT2が競合する変更を行なってしまった場合に、整合性が取れなくなってしまうという問題
- MVCCを用いて実装されている場合、コミット時に他のトランザクションにおける変更がなかったかどうかを確認し、競合があれば、コミット自体をAbortすることで対処することが多い。
- ただし、T1とT2のそのままの順序でコミットするとは限らず、先にコミットが完了したトランザクションが成功してしまうことが起こり得る。
つまり、完全にトランザクションがSERIALIZABLE
であるということは保証されないのが、Snapshot Isolationである。
一般に、Snapshot Isolationは、MVCCを用いて実装される
MVCC
MVCCといっても、実装の具体的なものはいろいろある。
MVTO,MV2PL,2V2PLなど、MVCCの実装はさまざまであるが、多くが、Snapshot Isolationの分離レベル実現を行うためのアルゴリズムだ。
SERIALIZABLEを実現するには、最も悲観的なアプローチを取る必要があり、現実的なところでは、Snapshot Isolationが最も一般的に使われる選択肢だ。