💭

トランザクション分離レベルについてまとめてみた

2023/09/28に公開
  • 話すこと
    • トランザクション分離レベルの概要とそれぞれの特徴
  • トランザクション分離レベルとは
    • 複数のトランザクションが同時に動いている時に他トランザクションの変更をどのタイミングで反映させるか定めたもの
      • 例えば他トランザクションの変更はコミットされたらすぐ反映するのかそれとも全く反映させないのか。など
  • トランザクション分離レベルの種類
    • READ UNCOMMITED
      • 概要
        • 他トランザクションで行われた変更が未コミットでも反映される
      • デメリット
        • 他トランザクションで行われた変更が結局ロールバックされた場合は存在しない値を読み込んでいたこととになる
        • そのコミットされていない値を元に何か処理を行うと不整合のもとになりそう
        • dirty read という
      • 参考フロー
    • READ COMMITED
      • 概要
        • 他トランザクションでコミットされた内容が反映される
        • 前述した dirty read は防ぐことができる
      • メリット
        • 他トランザクションでコミットされた内容しか読み込みが出来ない
        • 不正な値に対して処理を行うということはできなくなる
      • デメリット
        • 同じクエリを2回読み込む間に他トランザクションで変更がされればそれが反映されるため一つのトランザクション内での読み取りの結果が変わる可能性もある
        • non repeatable read という
      • 参考フロー
    • REPEATABLE READ
      • 概要
        • 他トランザクションで変更された内容は例えコミット後でも反映されない
        • 前述した non repeatable read が発生しない
        • トランザクション内での最初の読み取りでスナップショットを取得して以後そのスナップショットから情報を取得し続けていることで可能にしている
        Consistent reads within the same transaction read the snapshot established by the first read
        
      • メリット
        • 基本的にトランザクション内での読み取りに一貫性ができる
          • 他トランザクションで変更が起きても基本的には反映されない
      • デメリット
        • 他トランザクションの変更を反映しないと述べたが、ロックを獲得して読み取り( SELECT ... FOR UPDATE )を行う場合はスナップショットからではなく最新のレコードを読み取るため、 non repeatable read と同様に他トランザクションの変更が見えてしまう
        This means that if you issue several plain (nonlocking) SELECT statements within the same transaction, these SELECT statements are consistent also with respect to each other.
        
        「SELECT ~ FOR UPDATE を用いた locking read」は、一貫性読み取り とは違い、スナップショットが確立されていたとしても、他のトランザクションによる更新の影響を受ける(ファントムリード)
        
        • 同じレコードが複数トランザクションで変更された場合一番最後の変更のみが反映される
          • 何故かというと REPEATABLE READ は他のトランザクションでの変更を反映させないため全てのトランザクションが独立して変更を行うため
          • これが READ COMMITED の場合は他トランザクションのコミット後のデータが参照可能なため複数トランザクションでの変更が順番に反映される可能性が高い
            • 例を挙げると値が10のカラム1に対してT1で+1、T2でも+2している場合 READ COMMITED の場合はT1で1加算された値に対してさらにT2で2を足して結果13になるが、 REPEATABLE READ は最後の結果が反映されるため2しか増えず結果は12となる
    • SERIALIZABLE
      • 概要
        • 暗黙的に読み取りステートメントでは SELECT ... FOR SHARE となり共有ロックが取得されるので他のトランザクションが書き込みを行うことはできない
      • メリット
        • 完全な読み取りの一貫性を実現
      • デメリット
        • 他のトランザクションが書き込みを行うことができないので全体的なスピードに影響が出るかも
  • どれがベストなのか
    • それぞれにメリット・デメリットがあるので一概にどれがベストとは言えない
      • 例えばもうとにかくトランザクション内での読み取りに一貫性を持たせたいのであれば SERIALIZABLE だし、トランザクション内でデータが変わっても読み込めてもいい(or他のトランザクションで変更がコミットされたらすぐ反映させたい)ようなユースケースであれば、 READ COMMITED がむしろ適している
        • ただ READ UNCOMMITED はコミットされていない変更が見えてしまうので辞めた方がいいかも
    • 個人的な意見としてはどれを使うかより使っている分離レベルの特性を把握することを意識した方がいいと思った
      • それにより不自然な挙動が起きたとしても原因を特定するのに役に立つから
  • その他
    • 各トランザクションがお互いに干渉しないようにするには分離レベルを変える以外にもロックを獲得して読み込みを行うのも一つの手
  • 参考資料

Discussion