MySQL(InnoDB)のTransaction Isolation Levelsについてまとめる

目次
  1. Transaction Isolation Levelsとは
  2. 読み取り時の問題
  3. Dirty Read
  4. Fuzzy Read/Non-Repeatable Read
  5. Phantom Read
  6. Transaction Isolation Levels別の挙動
  7. REPEATABLE READ
  8. READ COMMITTED
  9. READ UNCOMMITTED
  10. SERIALIZABLE
  11. Transaction Isolation Levelsを変更する方法
  12. システム変数から変える
  13. SQL文から変える
  14. あとがき
  15. 参考

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

  • 同トランザクションで複数回レコード取得した際、他トランザクションのUPDATEの影響で取得結果が変わってしまうこと。(取得レコードの値が変わってしまうこと)

Phantom Read

  • 同トランザクションで複数回レコード取得した際、他トランザクションのINSERT・DELETEの影響で取得結果が変わってしまうこと。(レコードが増えたり減ったりしてしまうこと)

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される。
  • Dirty Read、Fuzzy Read、Phantom Readが発生しない。
  • 他トランザクションのコミットを確実に取得したい場合に使える。ただしパフォーマンスが犠牲になる点は注意が必要。

https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_autocommit

Transaction Isolation Levelsを変更する方法

システム変数から変える

システム変数のtransaction_isolationにトランザクション分離レベルを指定できます。 my.cnfなどのオプションファイルに追記するのが良いと思います。

https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_transaction_isolation

https://dev.mysql.com/doc/refman/8.0/en/option-files.html

SQL文から変える

SET [GLOBAL | SESSION] TRANSACTION
    transaction_characteristic [, transaction_characteristic] ...

transaction_characteristicにトランザクション分離レベルを指定します。

https://dev.mysql.com/doc/refman/8.0/en/set-transaction.html

あとがき

Transaction Isolation Levelsはどれがおススメという話ではなく、それぞれの特性を理解した上で要件によって適切に採択するのが良さそうです。

参考

https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html