トランザクション分離レベルについて認識している事を復習をかねてまとめる。
web上で寄せ集めた知識なので、厳密に合ってるのかどうかの保証はない。ざっくり。
トランザクション分離レベルとは
DBは複数の処理を同時に実行することが求められるので並列で処理を行う必要がある。
並列性を上げれば単位時間辺りに処理できる数は増えるが、データが不整合を起こす可能性が増える。
並列性を下げれば単位時間辺りに処理できる数は減るが、データが不整合を起こす可能性が減る。
この並列性の高い低いをトランザクション分離レベルで調整するイメージ。
トランザクション分離レベルには以下のものがある。
- Read uncommitted
- Read committed
- Repeatable read
- Serializable
起こしうる不整合の種類
並列性の度合いによってデータが起こす不整合には以下のようなものがある(他にもあるがとりあえず)
- Dirty read
- Non-repeatable read
- Phantom read
Dirty read
トランザクションAがまだコミットしていない変更が、トランザクションBから読み取れてしまう。
これを許容できるシステムとは、どんなものがあるのかよくわからない。
Non-repeatable read
トランザクションAが1度読み取った内容を再度読み取ると、1回目と異なる内容が読み取れてしまう。(1回目と2回目の間にトランザクションBが変更をコミットした)
Phantom read
トランザクションAが1度データを範囲で読み取った後、再度同じ条件で読み取ると、1回目には無かったデータが含まれている。(1回目と2回目の間にトランザクションBが範囲内に新規レコードを追加コミットした)
トランザクション分離レベルと起こりうる不整合
基本的には以下の感じ。
ただし、製品によって異なる場合がある。
分離レベル | Dirty read | Non-repeatable read | Phantom read |
---|---|---|---|
Read uncommitted | ○ | ○ | ○ |
Read committed | ✗ | ○ | ○ |
Repeatable read | ✗ | ✗ | ○ |
Serializable | ✗ | ✗ | ✗ |
○: 発生する, ✗: 発生しない
楽観的ロックと悲観的ロック
(製品によって異なるかも)
Repeatable readやSerializableによる並列実行制御は楽観的ロックによって実現されている。
楽観的ロックとは「基本衝突しないだろう」で処理し、もし衝突したら例外にするようなやり方で、ロックによる待ちが発生しない。
待ちが無いので高い処理速度が期待できるが、不整合を起こす処理は例外によって終了させられてしまう。
たから「Phantom read起こしたくないからSerializableに指定すればいいや」って単純にやると、ユーザー視点で見るとエラーが発生してフォームの入力しなおし、みたいな事が起こりうる。
try-catchなどで数回程度の再実行処理を入れておくべき。
もしくはきっちり待たせたい場合はトランザクション分離レベルとは無関係に悲観的ロック(for updateなど)を使うようにする。
(mySQLのギャップロックに気をつける)