復数のトランザクションが同時に実行されるとき、それらのトランザクションがどのように相互作用するかを定義するのが分離レベル。
本来は直列実行すれば並行で実行することの問題が解決できるが、サービスは複数人が同時に使うのが一般的なのでアプリケーション全体のパフォーマンスを考えるとそれは現実的じゃなさそう。(「誰かが実行中なのでお待ちください。」はやばい)
並行で実行されるときの問題
- 1つのトランザクションが別のトランザクションで変更されているデータを読み取ること。
- 2つ以上のトランザクションが同じデータを同時に変更すること。
トランザクション分離レベル
問題に対し、ある部分に関しては許容する。と段階的に定義しているものがトランザクション分離レベル。
- リードアンコミッティド(Read uncommitted)
- リードコミッティド(Read committed)
- リピータブルリード(Repeatable read)
- シリアライザブル(Serializable)
PostgreSQLのデフォルトのトランザクション分離レベルは「リードミコッティド」
PostgreSQLで発生しうること
リードコミッティドの場合、以下の3つが発生しうる。
- 反復不可能読み取り(Nonrepeatable Read)(ファジーリードと読んでいる記事もある fuzzy = 曖昧な、ぼんやりした)
- ファントムリード(Phantom Read)
- 直列化異常(Serialization Anomaly)
ダーティーリードは起きない。
反復不可能読み取り
一度読み取ったデータを他のトランザクションが変更することで、同じデータを読み取ったとしても異なる値が読み取られること。
ファントムリード
トランザクションが複数行のある集合を返す検索条件で問い合わせを行ったあと、別のトランザクションのコミットが行われることで、同じ検索条件で問い合わせを行ったとしても異なる行が返されること。
直列化異常
復数のトランザクションがコミットされたあとの結果が、トランザクションを直列化した場合の結果と異なること。(=直列で実行した場合は正常に値が反映されるが、並列で実行した場合は異なる値になること)
業務で遭遇したこと
PostgreSQLでは、トランザクション分離レベルをリードアンコミッティド(デフォルトのひとつ下で、もっとも下のレベル)にしたとしても、ファントムリードが発生しない。つまりほかトランザクションのコミット前のデータを読み取ることがない。
上記の挙動をするため、他トランザクションでコミットされるデータを読み取らず、自トランザクションでコミットしようとしたときに、Unique制約に引っかかったことがある。(自トランザクションのコミットは他トランザクションのあと)
トランザクション分離レベルと発生しうる問題を理解していると当然の挙動と理解できる。