データベースの学習や実務でよく出てくる「分離レベル」という概念、正確に理解できていますか?
「READ COMMITTEDとREPEATABLE READってどう違うの?」「MySQLのデフォルトはどれ?」「SERIALIZABLEにするとどうなるの?」といった疑問を持つ方も多いでしょう。
本記事では、トランザクションの分離レベルの種類・違い・MySQLでの設定方法・各レベルで発生しうる問題まで、わかりやすく丁寧に解説していきます。
トランザクションの分離レベルとは?基本的な意味と必要性
それではまず、分離レベルの基本的な意味と、なぜ必要なのかについて解説していきます。
分離レベル(Isolation Level)とは、複数のトランザクションが同時に実行される際に、それぞれのトランザクションがどの程度互いの処理から「隔離(分離)」されるかを定義した設定です。
分離レベルは、ACID特性の「I(Isolation:独立性)」をどの程度保証するかを制御するものです。
分離レベルが高いほどデータの正確性・整合性は向上しますが、その分ロックが増えて並行処理性能(スループット)が低下するトレードオフがあります。
つまり、分離レベルの選択は「データの正確性」と「処理パフォーマンス」のバランスをどこに置くかという設計判断です。
分離レベルが低いと起こりうる3つの問題
ダーティリード(Dirty Read):まだコミットされていない別トランザクションの変更を読み取ってしまう問題です。
ノンリピータブルリード(Non-Repeatable Read):同じトランザクション内で同じデータを2回読み取ると、別のトランザクションの更新により値が変わっている問題です。
ファントムリード(Phantom Read):同じ条件で2回SELECTすると、別のトランザクションのINSERTにより結果セットに「幻の行」が出現する問題です。
これら3つの問題をどの程度防ぐかによって、4段階の分離レベルが定義されています。
分離レベルの4段階の概要
| 分離レベル | ダーティリード | ノンリピータブルリード | ファントムリード |
|---|---|---|---|
| READ UNCOMMITTED | 発生する | 発生する | 発生する |
| READ COMMITTED | 防ぐ | 発生する | 発生する |
| REPEATABLE READ | 防ぐ | 防ぐ | 発生する(※InnoDBは防ぐ) |
| SERIALIZABLE | 防ぐ | 防ぐ | 防ぐ |
SQLの標準規格(ANSI SQL-92)でこの4段階が定義されており、MySQLをはじめとする主要なRDBMSがこれをサポートしています。
MySQLのデフォルト分離レベル
MySQLのInnoDBエンジンのデフォルト分離レベルはREPEATABLE READです。
これはANSI SQL標準のREPEATABLE READに加え、InnoDBのMVCC(多版型同時実行制御)によってファントムリードも防ぐ実装になっているため、SERIALIZABLEほどのロックコストなしに高い整合性が得られる実用的な選択です。
PostgreSQLのデフォルトはREAD COMMITTEDで、MySQLとは異なります。
READ UNCOMMITTEDとREAD COMMITTEDの違い
続いては、最も低い2つの分離レベルについて詳しく確認していきます。
READ UNCOMMITTED(読み取り未コミット)
READ UNCOMMITTEDは、最も低い分離レベルで、他のトランザクションがまだコミットしていないデータでも読み取ることができます。
「ダーティリード」が発生する唯一の分離レベルです。
【ダーティリードの例】
トランザクションAが在庫を100→50に更新(まだコミットしていない)
トランザクションBがREAD UNCOMMITTEDで在庫を読む → 50を取得
トランザクションAがロールバック → 在庫は100に戻る
→ トランザクションBは存在しなかった「50」という値を使って処理を進めてしまった
ダーティリードは「幻のデータ」を読み取ってしまう非常に危険な問題です。
READ UNCOMMITTEDは実務ではほとんど使用されず、データの正確性が不要な統計処理や監視目的での概算値取得などの極限られた用途に留まります。
READ COMMITTED(コミット済み読み取り)
READ COMMITTEDは、コミット済みのデータのみ読み取ることができる分離レベルです。
ダーティリードを防ぎますが、ノンリピータブルリードは発生します。
【ノンリピータブルリードの例】
トランザクションBが在庫を最初に読む → 100
トランザクションAが在庫を100→50に更新してコミット
トランザクションBが同じ在庫を再度読む → 50(値が変わった!)
→ 同じトランザクション内で同じデータを読んだのに結果が変わってしまう
READ COMMITTEDはOracleおよびPostgreSQLのデフォルト分離レベルです。
多くのWebアプリケーションで許容できるレベルの整合性と、十分な並行処理性能を両立できるため、実務での使用頻度が高い分離レベルです。
REPEATABLE READとSERIALIZABLEの違い
続いては、高い分離レベルの2段階について確認していきます。
REPEATABLE READ(反復可能読み取り)
REPEATABLE READは、同じトランザクション内で同じデータを何度読み取っても、常に同じ値が返ることを保証する分離レベルです。
ノンリピータブルリードを防ぎますが、標準ではファントムリードが発生しえます。
ただし前述の通り、MySQLのInnoDBはMVCCによってREPEATABLE READでもファントムリードを防いでいます。
【REPEATABLE READでのスナップショット読み取り(MySQL InnoDB)】
トランザクション開始時点のデータスナップショットが固定される
→ 他のトランザクションがデータを変更・追加・削除してコミットしても、このトランザクションには影響しない
→ 一貫した読み取りが保証される
MySQL InnoDBのREPEATABLE READは、スナップショット読み取りとギャップロックの組み合わせにより、実質的にSERIALIZABLEに近い整合性を低コストで実現しています。
SERIALIZABLE(直列化可能)
SERIALIZABLEは、最も厳格な分離レベルで、すべてのトランザクションが直列(一本のキューで順番に)実行されたように動作することを保証します。
ファントムリードを含むすべての並行処理問題を防ぎます。
ただし、すべての読み取りにも共有ロックが課されるため、並行処理性能は最も低くなります。
| 分離レベル | 性能 | 整合性 | 主な用途 |
|---|---|---|---|
| READ UNCOMMITTED | 最高 | 最低 | 概算値取得・ほぼ使わない |
| READ COMMITTED | 高い | 標準 | 一般的なWebアプリ(Oracle/PostgreSQLデフォルト) |
| REPEATABLE READ | 中程度 | 高い | MySQLデフォルト・多くのビジネスアプリ |
| SERIALIZABLE | 最低 | 最高 | 金融・医療など厳格な整合性が必要なシステム |
SERIALIZABLEは金融機関・医療システム・航空予約など、データの正確性が最優先で、多少の性能低下を許容できるシステムで採用されることが多いでしょう。
MySQLで分離レベルを確認・設定する方法
続いては、MySQLで分離レベルを実際に確認・変更する方法について確認していきます。
現在の分離レベルを確認する
【MySQL 8.0での分離レベル確認コマンド】
SELECT @@GLOBAL.transaction_isolation; ← グローバル設定を確認
SELECT @@SESSION.transaction_isolation; ← セッション設定を確認
SHOW VARIABLES LIKE ‘transaction_isolation’; ← 別の確認方法
デフォルトでは「REPEATABLE-READ」と表示されます。
分離レベルを変更する方法
【分離レベルの変更コマンド】
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; ← 全セッションに適用
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE; ← 現在のセッションのみ
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ; ← 次のトランザクションのみ
本番環境でグローバル設定を変更する際は、既存のトランザクションへの影響を慎重に評価してから実施することが重要です。
my.cnfの設定ファイルでtransaction-isolation = READ-COMMITTEDのように記述することで、サーバー起動時のデフォルト分離レベルを変更することもできます。
分離レベルの選択指針
実務でどの分離レベルを選ぶべきかについての指針をまとめます。
一般的なWebアプリケーションでは、MySQLを使うならREPEATABLE READ(デフォルト)、PostgreSQLを使うならREAD COMMITTED(デフォルト)で多くのケースは問題ありません。
金融取引・在庫管理など厳格な整合性が必要な処理にはSERIALIZABLEを検討し、高スループットが最優先で多少の不整合を許容できる統計処理にはREAD COMMITTEDが適しています。
分離レベルはシステム全体に一律に適用するのではなく、処理の重要度に応じてトランザクションごとに設定を変える方法も有効です。
まとめ
本記事では、トランザクションの分離レベルの意味・4種類の違い・各レベルで防げる問題・MySQLでの設定方法について詳しく解説しました。
分離レベルはREAD UNCOMMITTED・READ COMMITTED・REPEATABLE READ・SERIALIZABLEの4段階があり、レベルが上がるほどデータの整合性は高まりますが、パフォーマンスは低下します。
MySQLのデフォルトはREPEATABLE READで、InnoDBのMVCCによってファントムリードも防いでいます。
分離レベルの適切な選択が、データの整合性と処理性能のバランスを最適化するための重要な設計判断です。
本記事を参考に、分離レベルへの理解を深め、システム要件に最適な設定を選べるエンジニアを目指してください。