Posted at

[RDBMS][SQL]トランザクション分離レベルについて極力分かりやすく解説

More than 1 year has passed since last update.

技術系の記事色んなところで書いてたけど、ここにまとめることにした。昔書いてたやつは綺麗バッサリ消そうかと思ったんだけど、やたらView数が多いやつが何個かあったので気が向いた時に乗り換えしつつ(予定)今の知識で更新。


概要

以下の3つの不都合な読み込み現象がある。この意味に関しては後ほど解説。とりあえずはどれもRDBMSのACID特性のI(Isolation-隔離性)から外れたものと思ってくれればいい。


  • ダーティリード

  • ファジーリード(非再現リード,ノンリピータブルリード)

  • ファントムリード

で、本題のトランザクション分離レベルは4つのレベルがある。


  • READ UNCOMMITTED

  • READ COMMITTED

  • REPEATABLE READ

  • SERIALIZABLE

下に行くほど高レベルで上に行くほど低レベル。

高レベルになればなるほど、先ほどの不都合な読み込み現象が発生しなくなる。が、代わりにパフォーマンスが落ちる。

つまり、「パフォーマンスを上げるためにある程度の読み込み不都合を妥協するか、パフォーマンスを落としてもいいから不都合を発生しないようにするか」という設定のレベルのことを言う。


読み込みの不都合

先述した3種類の読み込み不都合に関して説明。

まずこれ知らないと分離レベルの話できない。


ダーティリード

別のトランザクションでコミットされてないデータが読み取れる現象。


  1. トランザクションAでレコードを①から②にUPDATE(未コミット)

  2. トランザクションBでレコードをSELECTする

  3. トランザクションAをロールバックする

  4. トランザクションBで取得したデータは②となっている。

一番低いレベルでないと発生しない。これを許容するシステムをまだ見たことがない・・・


ファジーリード(非再現リード,ノンリピータブルリード)

別のトランザクションで更新されたデータを読むことにより、一貫性がなくなる現象。


  1. トランザクションAでレコードをSELECTする。①となっている。

  2. トランザクションBでレコードを①から②にUPDATEし、COMMITする。

  3. トランザクションAで同じレコードを再度SELECTする。②となっている。

トランザクション分離レベル知る前はそういうもんじゃんとか思っていたんだけど、

2つのトランザクションが隔離されてないので使ってる側で気にしないといけない。

その時点で隔離性から離れてるんだよね。


ファントムリード

別のトランザクションで挿入されたデータが見えることにより、一貫性がなくなる現象。


  1. トランザクションAでレコードをSELECTする。該当レコードがない。

  2. トランザクションBでレコードをINSERTし、COMMITする。

  3. トランザクションAでレコードをSELECTする。2でINSERTとしたレコードが取得できる。

ファジーリードとよく似ているがINSERTとUPDATEという点が違う。

また、以下もファントムリードとなる。


  1. トランザクションAでレコードをCOUNTする。X件取得できた。

  2. トランザクションBでレコードをINSERTし、COMMITする。

  3. トランザクションAでレコードをCOUNTする。X+1件取得できた。

以上が読み込みにおける3種類の不都合の簡単な説明となる。


トランザクション分離レベルと読み込みの不都合の関係

簡潔に以下のような関係性となる。

ダーティリード
ファジーリード
ファントムリード

READ UNCOMMITTED
発生する
発生する
発生する

READ COMMITTED

発生する
発生する

REPEATABLE READ

発生する

SERIALIZABLE

こうしてみると隔離性の高いSERIALIZABLEをいつも選択がいいのではないかと思ってしまうが、隔離性の高さ故の問題もある。が、今回は極力分かりやすくというコンセプトなので説明では省略


幾つか補足


  • あくまで「発生しないことが保証されている」というもので、レベル低いからと言って発生するようになっているかと言えばそうとは限らない。その辺りの実装はRDBMSによって大きく違い、例えばMySQLではREPEATABLE READであってもファントムリードは発生しない。
    (が、代わりに別の問題があるのだけど今回はスルー)

  • 標準SQLで「発生しないことが保証されている」というものなので、発生しない為にどういう方法を取るかというのはRDBMS毎に違う。発生する状況になったらエラーになる、トランザクション完了を待つ等。

  • RDBMSによってデフォルトのトランザクション分離レベルが違う。対応している分離レベルの種類も違う。


デフォルトのトランザクション分離レベル


  • MySQL(InnoDB) … REPEATABLE READ

  • PostgreSQL … READ COMMITTED

  • Oracle … READ COMMITTED

  • SQL Server … READ COMMITTED


どうでもいい話

元記事書いたの4年前だった。