データベーススペシャリスト試験の勉強をしていて初めて出会った「トランザクション分離レベル(ISOLATION LEVEL)」について、内容を忘れないよう、学んだ点を整理しておこうと思います。
前提知識
共有ロック
他のトランザクションからの対象行の参照が可能なロック。変更は共有ロックの解放待ちとなる。
占有ロック
他のトランザクションからの対象行の参照・変更ともに不可となるロック。占有ロックが解放されるまで他のトランザクションは参照・変更を待つ必要がある。
トランザクション分離レベル(ISOLATION LEVEL)
複数のトランザクションが同時に実行される際に、互いにどのように影響を及ぼすかを制御するための設定のこと。
主に4つの分離レベルがあり、それぞれ異なる特徴を持っています。
READ UNCOMMITTED
名前の通り、他のトランザクションが未コミットのデータも読み取る方式。
- データ参照時は共有ロックや占有ロックを無視して読み取る
- データ変更時に占有ロックを掛け、トランザクション終了時に解放する
データ参照時に共有ロックや占有ロックを無視するため、他のトランザクションでまだコミットされていないデータを読み取れてしまう。
これをダーティリードという。
ダーティリード
他のトランザクションで更新された"コミット前"のデータを読み込んでしまうこと。
READ COMMITTED
名前の通り、他のトランザクションがコミットしたデータのみを読み取る方式。
- データ参照時に共有ロックを掛け、参照終了時に解放する
- データ変更時に占有ロックを掛け、トランザクション終了時に解放する
データ参照時に占有ロックを無視しないためダーティリードは起きない。
また、データ参照時にかける共有ロックは参照処理が終了したらすぐロックを解放するため、解放後は他のトランザクションでそのテーブルを更新可能となってしまう。
これにより、例えば、トランザクションAにおいて、とあるテーブルの同じ行を複数回参照する場合、1回目の読み取り処理と2回目の読み取り処理の間で、他のトランザクションBが同じ行に対し更新処理を行うと、1回目と2回目で読み取れるデータが異なる可能性がある。
これをノンリピータブルリードという。
ノンリピータブルリード
同じデータを2回リードしたときに値が異なる可能性のある読み方のこと。
1回目と2回目の間に別のトランザクションによりデータが変更され、不整合が発生する可能性がある。
REPEATABLE READ
名前の通り、繰り返し読み取っても不整合が発生しない方式。
- データ参照時に共有ロックを掛け、トランザクション終了時に解放する
- データ変更時に占有ロックを掛け、トランザクション終了時に解放する
共有ロックをトランザクション終了時に解放するため、ノンリピータブルリードは起きませんが、ファントムリードが発生する可能性があります。
ファントムリード
1回目と2回目のリードの間に、他のトランザクションによってデータが追加されてしまう読み方のこと。
1回目にはなかった幻=ファントムのデータが発生し不整合になる可能性がある。
ファントムリードはノンリピータブルリードと似ているが、ノンリピータブルリードは1回目と2回目で参照した同じ行に対してデータの不整合が発生するのに対し、ファントムリードは参照していない他の行にデータの不整合が発生することを指している。
データ参照時に参照する行に対して共有ロックをかけトランザクション終了時に解放するため、トランザクションを解放するまでは参照している行に対して他のトランザクションから更新されることはなくなりますが、その他の範囲への行についてはロックはかけていないため追加可能となっている。
SERIALIZABLE
名前の通り、トランザクションが完全に直列化されたかのように振る舞う方式。
- データ参照時に、クエリでアクセスする行やインデックスの範囲に対して共有ロックまたは意図的ロックを取得し、トランザクション終了時に解放する
- データ変更時は占有ロックを使用し、トランザクションが終了するまで解放しない
「REPEATABLE READ」までは、読み取る行単位に共有ロックをかける方式ですが、「SERIALIZABLE」ではアクセスする範囲に対してロックを取得する点が他と異なる。
ロックを掛ける範囲については詳しく調べられていないのですが、イメージとしては表ロックに近いレベルでのロックを取得するんだと思う。
まとめ
4つの分離レベルについて整理しましたが、最後にまとめると表のようになる。
ISOLATION LEVEL | ダーティリード | ノンリピータブルリード | ファントムリード |
---|---|---|---|
READ UNCOMMITTED | ○ | ○ | ○ |
READ COMMITTED | - | ○ | ○ |
REPEATABLE READ | - | - | ○ |
SERIALIZABLE | - | - | - |
これを見ると、データベースの設定は分離レベルが一番高い「SERIALIZABLE」にすればいいじゃん、と思ってしまいますが、分離レベルを上げれば上げるほど、ロックの解放待ちが発生しやすく、一概に分離レベルを上げればいいというものでもない。
一般的なWebシステムの分離レベルとしては「READ COMMITTED」を選択し、同じデータに対する読み取りの一貫性が必要な場合は「REPEATABLE READ」を設定するのがいいのかなと思う。
以上