トランザクションについて整理する

published_at: 2021-06-13

概要

DB周りを再度学び直していて、トランザクションについて学びがあったのでまとめる。

トランザクションとは?

複数のデータ更新処理を1つの単位にまとめたもの。 COMMITするとトランザクション確定させる。 ROLLBACKするとトランザクションを取り消す。

同時実行制御について

普段アプリケーションを通じて、自分以外の多くの人が検索 / 更新 / 削除など同時並行でトランザクションが実行されている。 この時、トランザクションの分離性(Isolation)を保つにはどうしたら良いのか?

  • トランザクションを直列に実行する
    • ユーザーAのトランザクションCOMMITごにユーザーBのトランザクションを開始する形式
      • メリット
        • トランザクション分離性を保てるので、結果整合性を担保
      • デメリット
        • スループットの低下が顕著に
    • => スループットより結果整合性の方に重きを置いている。
  • ロックを使って並列に実行する
    • 共有ロック / 排他ロックを使うことで、資源の占有 / 共有が可能になり、DBは並列に実行しながらある程度の分離性を担保できる

ロックの問題点

  • スラッシング
    • 並行して同じレコードに対してロックをかけようとするトランザクションが一定数超えると、1つのトランザクションが待機させられる頻度と時間が増え、スループットが低下すること。
  • デッドロック
    • 複数のトランザクションが複数の資源をロックする場合、互いに相手の資源解放を待つ状態となり永遠にトランザクションが終わらない状態が続いてしまうこと。

ロックの問題点を解消するには?

トランザクションの結果整合性を緩やかにしていく妥協策 => 4つのトランザクション分離レベルという妥協策

  • 非コミット読み取り(Read Uncommitted)
  • コミット済み読み取り(Read Committed)
  • 再読み込み可能読み取り(Repeatable Read)
  • 直列化可能(Serializable)

Serializableは結果整合性が強く担保されるが、それ以外は担保されないケースがある。 つまり、上3つを採用すると何かしらデメリットがある。 そのデメリットは下記の3つ。

  • ダーティリード
    • トランザクションBでコミットされていないデータをトランザクションAで読み取ってしまう問題
  • ファジーリード
    • トランザクションAでデータを複数回読み取っている途中で、トランザクションBでデータを更新してコミットした場合、トランザクションAで違う結果のデータを読み取ってしまう問題。
  • ファントムリード
    • トランザクションAで一定の範囲のレコードに対して処理を行っている途中で、トランザクションBでデータを追加・削除してコミットした場合、トランザクションAで幻影のようにデータが反映されるため、処理の結果が変わってしまう問題

分離レベル毎に生じる問題をまとめると下記の表になる。 テーブル

多くのDBMSがRead Committedを採用しているよう。

所感

DBMSは分離性をどう担保するかで苦労したんだろうな。