

# 前置き

トランザクション分離レベルの設定方法ですが`Spring`ならメソッド毎に`@Transactional`をつければカジュアルに設定できます。`Rails`でも
`ApplicationRecord.transaction(isolation: :read_committed) do
end`で囲うなどカジュアルに設定できます。

[トランザクション分離レベル -
Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%88%86%E9%9B%A2%E3%83%AC%E3%83%99%E3%83%AB)には「並列トランザクションを並べた時に起こりうる正常ではない現象（phenomena）とanomalies」が記載れています。

# 質問

ネットにはよく `SERIALIZABLE`
同士の例を参考に、`ダーティリード`も`ファジーリード`も`ファントムリード`も起こらないと説明されていますが、`READ
UNCOMMITTED`を先にしているところに`SERIALIZABLE`のトランザクションがやってくるとどうなるのでしょうか？
よく考えて設計すればこんなことはしない気がしますが、あとからコードをカジュアルに足したときに書いてしまいかねない気もしているので、どうなるのか知りたいです。（とはいえ考える組み合わせが多すぎるので、都度しっかり考えてトランザクション分離レベルを設定することになるのでしょうか。
多いと言っても4*4=16通り？）

# 参考

[Rails でトランザクション分離レベルを設定する方法 - Hack Your
Design!](https://blog.toshimaru.net/rails-4-transaction-isolation/)  
[Springでトランザクション管理 -
Qiita](https://qiita.com/NagaokaKenichi/items/a279857cc2d22a35d0dd)


CC BY-SA 4.0
2020-05-28T23:44:58.520
67101
2021-06-09T08:13:33.533
2020-05-29T00:36:50.653
9008
9008
question
ruby-on-rails sql database spring
トランザクション分離レベルをソースコード内で任意に設定しまくるとどうなりますか？


この問題は、それぞれの DBMS においてどのように Concurrency を制御する設計になっているのか、に依存します。ひとまず自分の知っている
PostgreSQL と MySQL について回答します。

# MySQL について

MySQL は、更新系のクエリ(Insert/delete/update)と参照系のクエリ(select)において、論理的な作用対象が違います。 MySQL
は MVCC を採用していますが、おおむね、

  * select: MVCC で snapshot を取れるようにして、それに対して取得する。
  * 更新系: 上記の snapshot にかかわらず、その時の buffer pool の最新の値を更新しにかかる。更新されたテーブル(の箇所)は write lock される。

です。また、これを前提として、各 isolation level は、 select がどのように行われるか、の差異しかありません。

  * READ UNCOMMITTED: その時点のバッファプールの最新の値を取得する
  * READ COMMITTED: Tx 内のその文を実行する直前(Txの最初ではなく)の snapshot を読み取る
  * REPEATABLE READ: Tx 開始時点
  * SERIALIZABLE: すべての select を select for update に書き換えて実行。この意味で strict 2 phase commit と同じであり、 lock が取れなければ実行できない

# PostgreSQL について

snapshot isolation を基本に動作する。参照:
<https://ja.wikipedia.org/wiki/Snapshot_isolation>

端的に言うと、データ参照はデータベースのある時点の snapshot に対して動作し、データ更新系は早い者勝ちで、遅かった方が abort されます。

  * READ UNCOMMITTED: 存在しない。READ COMMITTED と同じ動作になる。
  * READ COMMITTED: 各文それぞれが単一の Tx であったような動き方をする。
  * REPEATABLE READ: Tx の最初のデータ操作・参照の直前を snapshot として、それに対してデータは取得される。
  * SERIALIZABLE: ^ に加えて、 serializable を実現するために、 serializable Tx 同士の、同一データに対する read-write 関係を監視して、それが循環すると serializable でなくなる可能性が高いのでどちらか一方を rollback 
    * ^ の理論をまとめた元論文: <https://dl.acm.org/doi/10.1145/1376616.1376690>

# まとめると

PostgreSQL や MySQL は、 MVCC / Snapshot Isolation を実現しており、 isolation level
が効いてくるのはどのようにデータを取得するのか、という点において。 MVCC において、データ取得は snapshot に対して実行されるため、
isolation level の組合せによる挙動の制御、というものはあまり存在せず、各 Tx が concurrent  
にデータを更新していく中でどの時点の snapshot を参照するのか、という違いが主。

ただし、 serializable は snapshot isolation のみでは実現できないため (write skew / read only
skew)、

  * MySQL では read でも lock を取得し、
  * PostgreSQL では serializable 同士の rw の依存関係の監視を行う。


CC BY-SA 4.0
2021-06-09T08:13:33.533
77427
2021-06-09T08:13:33.533
754
67101
answer