DBのロックについて考える際にTransaction Isolation Levelsを確認することはよくあると思いますが、バックエンドを触ってない期間が長くなると細かい部分を忘れてしまうのでまとめてみました。
Transaction Isolation Levelsとは
トランザクションが同時に実施された時の一貫性、正確性を定義したもののことで、日本語だとトランザクション分離レベルと呼ばれます。
分離レベルは以下の4つが存在します。
- SERIALIZABLE
- REPEATABLE READ
- READ COMMITTED
- READ UNCOMMITTED
MySQL固有の概念ではなく他のRDBMS(PostgreSQL, Oracle等)にも存在する概念ですが、RDBMSによって挙動は同一ではないので注意が必要です。
当記事ではMySQL(InnoDB)での挙動をまとめていきます。
読み取り時の問題
分離レベルを理解する前に、データを読み取る際に発生し得る下記の代表的な問題について理解しておく必要があります。
Dirty Read
- 他トランザクションで未コミットの変更を取得できてしまうこと。
Fuzzy Read/Non-Repeatable Read
Phantom Read
Transaction Isolation Levels別の挙動
REPEATABLE READ
- InnoDBのデフォルトの分離レベル。
- 初回読み取り時にのみスナップショットを確立し、トランザクション内でSELECTする際にそのスナップショットからレコードを参照する。この読み取り方法は「consistent read」と呼ばれる。
- ただしlocking read(SELECT FOR SHARE, SELECT FOR UPDATE)時は最新のレコードを参照する。
- スナップショットを参照するため、Dirty Read、Fuzzy Read、Phantom Readが発生しない。
- 実装時、取得レコードが最新ではないことを考慮する必要がある。
READ COMMITTED
- 毎回読み取り時にスナップショットを確立し、トランザクション内でSELECTする際はスナップショットからレコードを参照する。
- コミットされていないレコードは参照しないため、Dirty Readが発生しない。
- スナップショットが毎回確立される為、Fuzzy Read、Phantom Readは発生する場合がある。
- 実装時、他トランザクションでのINSERT、UPDATE、DELETEの影響を受け得ることを考慮する必要がある。
READ UNCOMMITTED
- READ COMMITTEDと同様の挙動だが、加えて「まだCOMMITされていないレコード」も参照する。
- Dirty Read、Fuzzy Read、Phantom Readが発生する場合がある。
- 他トランザクションの変更による影響でデータの不整合を起こす可能性が高いが、パフォーマンスは高い。
- 一貫性の無いレコードが取得されることを許容できる要件なら使える。
SERIALIZABLE
- システム変数の
autocommit
の値によって挙動が異なる。- autocommitが0(disabled)のとき
- SELECTは暗黙的にSELECT FOR SHAREに変換して実行される。
- 共有ロックがかかることを考慮しての実装が必要になる。
- autocommitが1(enabled)のとき
- 各SELECT文は個別の独立したトランザクションでconsistent readされる。
- autocommitが0(disabled)のとき
- Dirty Read、Fuzzy Read、Phantom Readが発生しない。
- 他トランザクションのコミットを確実に取得したい場合に使える。ただしパフォーマンスが犠牲になる点は注意が必要。
MySQL :: MySQL 8.0 Reference Manual :: 5.1.8 Server System Variables
Transaction Isolation Levelsを変更する方法
システム変数から変える
システム変数のtransaction_isolation
にトランザクション分離レベルを指定できます。
my.cnfなどのオプションファイルに追記するのが良いと思います。
MySQL :: MySQL 8.0 Reference Manual :: 5.1.8 Server System Variables
MySQL :: MySQL 8.0 Reference Manual :: 4.2.2.2 Using Option Files
SQL文から変える
SET [GLOBAL | SESSION] TRANSACTION transaction_characteristic [, transaction_characteristic] ...
transaction_characteristicにトランザクション分離レベルを指定します。
MySQL :: MySQL 8.0 Reference Manual :: 13.3.7 SET TRANSACTION Statement
あとがき
Transaction Isolation Levelsはどれがおススメという話ではなく、それぞれの特性を理解した上で要件によって適切に採択するのが良さそうです。
参考
MySQL :: MySQL 8.0 Reference Manual :: 15.7.2.1 Transaction Isolation Levels