はじめに
DB について調べていたら、リード現象というものがあることを知りました。リード現象にはいくつかの種類があるようなので、それについて調べてみました。将来の自分のためにもまとめておきます。
ダーティーリード
ダーティーリードとは、あるトランザクションによって変更されたまだコミットされていないデータを他のトランザクションが読み取ってしまう現象のことです。
実際に動かして確認してみます。
DBの初期状態は以下のような状態です。
今回はid = 10のレコードを更新して確認してみるます。

まずはトランザクションAでクエリを発行して、状態を確認してみます。

次にトランザクションBでid = 10のレコードに更新をかけます。その後に、select文で確認してみると当然ですが変更されたことが確認できます。

これをcommitする前に、トランザクションAで再度select文で確認してみましょう。

するとなんと、まだcommitする前なのにも関わらず変更が確認できてしまいます。
これがダーティーリードです。
ファジーリード(ノンリピータブルリード)
ファジーリード(ノンリピータブルリード)とは、あるトランザクションによって変更された(コミットされた)データを他のトランザクションが参照できてしまう現象のことです。
これも実際の動かして確認してみます。(DBの初期状態はダーティーリードと一緒です。)
まずはトランザクションAでid = 10のレコードを確認してみます。

次にトランザクションBでid = 10のレコードを更新してコミットします。
select文で確認してみると、当然変更は反映されていることが確認できます。

次にトランザクションAから確認してみるとどうでしょうか。

まだrollbackを実行していないのにトランザクションBで反映した変更が確認できてしまいます。
これがファジーリードです。
ファントムリード
ファントムリードとは、あるトランザションによって新規作成、または削除されたデータが他のトランザクションによって参照できてしまう現象のことです。
これも実際に動かして確認してみます。
まずはトランザクションAでusersテーブルの状態を確認しておきます。

次にトランザクションBで新しいレコードをinsertしてみます。
commitをして、select文で新規作成の反映を確認してみます。

そして、トランザクションAから再度select文を実行してみます。

先ほどトランザクションBで新規作成したレコードがトランザクションAからも確認できます。
これがファントムリードです。
トランザクション分離レベル
それぞれの現象はトランザクション分離レベルによって制御することができます。
以下が対照表です。
分離レベル | ダーティリード | ファジーリード | ファントムリード | パフォーマンス |
READ UNCOMMITTED | 発生する | 発生する | 発生する | ⭐️⭐️⭐️⭐️(最速 / ロックなし) |
READ COMMITTED | 発生しない | 発生する | 発生する | ⭐️⭐️⭐️(早い / 読み取りの整合性のためのロック) |
REPEATABLE READ | 発生しない | 発生しない | 発生する | ⭐️⭐️(中程度 / 行単位のロックで更新をブロック) |
SERIALIZABLE | 発生しない | 発生しない | 発生しない | ⭐️(遅い / テーブルロックレベルの厳しい制御) |
レベルを下げるデメリット
表をご覧いただければわかると思うのですが、各現象が起こらないようにするとパフォーマンスに影響が出てしまいます。
まとめ
この記事ではDBにおける各リード現象をまとめました。
どのような用途で使用されいているのか、ビジネス要件などでどのトランザクション分離レベルを設定するのかは変わってくると思いますが、適切なものを選べるようになりたいです。