RDBの isolation level についてまとめ。

読み取り一貫性の確保とは。
トランザクションの最初で取得した値が最後まで変わらないこと。

一番単純には select for update を発行してやればこの読み取り一貫性は確保できることになる。(行追加、削除についてはダメ)


で、SQL92の仕様では、アイソレーションレベル(トランザクション隔離性のレベル)に応じて、4つのレベルが規定されている。下に行くほどアイソレーションレベルは高くなる。


・read_uncommited
・read_commited
・repeatable_read
・serializable


で、読み取り一貫性が保たれないパターンは以下の3つ。

・ダーティーリード
トランザクションAが行を更新すると、(コミットしてなくても)即座にBにそれが見える。


・ノンリピータブルリード
トランザクションBで最初に行を取得した後、トランザクションAが行を更新し、コミット。その後、トランザクションBで行を取得すると、トランザクションAで更新した内容が取得される。(つまり最初に取得した値とは違う)


・ファントムリード
トランザクションBで最初に行を取得した後、トランザクションAが行を追加、もしくは削除し、コミット。その後トランザクションBで再度行を取得すると、トランザクションAで追加、削除した内容が追加されていたり、削除されていたりする。(最初に取得した行数と異なる)

で、

・ダーティーリードを防ぐには?
read_commited 以上にアイソレーションレベルを設定。このレベルでは他のトランザクションで更新がかかっていたら、そのトランザクションがコミットするまでselectの結果を返さない。(即座にエラーとして返すか、ロック開放待ちをするかは設定次第。コミットされたら、コミット後の内容を返す)

・ノンリピータブルリードを防ぐには?
repeatable_read 以上にアイソレーションレベルを設定。このレベルでは select かけるだけで読み込み対象の行にロックがかかる。(他のトランザクションに更新をさせないことで、リピータブルリードを確保する)

・ファントムリードを防ぐには?
serializable にアイソレーションレベルを設定。このレベルではselectかけるだけで読み込み対象のテーブルにロックがかかる。(他のトランザクションに追加も削除もさせないことで一貫性を確保する)

現場では read_commited か、read_uncommited が使われるケースが多いと思う。ダーティーリードで良い処理に対して、read_commited で読み込みすると同時実行性は落ちることになるので、対象処理を吟味した上でアイソレーションレベルを決定する必要がある。


そんなに同時アクセスの無いシステムであれば、read_commited 一本槍でも構わないのだけど、高アクセスのシステムであればこれはボトルネックの原因になってしまう。(トランザクションを短くすることで軽減はできるが)