コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
分離レベル
分離レベル
このレッスンで分かること
- 4 つの標準分離レベル(Read Uncommitted / Read Committed / Repeatable Read / Serializable)
- 3 つの読み取り異常(ダーティリード・ノンリピータブルリード・ファントムリード)
- 各 DB のデフォルトと選び方
分離レベル とは
Read Uncommitted から Serializable まで 4 段階を比較する。本レッスンでは、分離レベル の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
なぜ分離レベルが複数あるか
完全な分離(Serializable)を実現すると、トランザクションが事実上 1 本ずつ直列実行されるため性能が極端に落ちます。多くのアプリは「ある程度の異常は許容してでも速くしたい」ため、分離を弱める選択肢が提供されます。
ANSI SQL は次の 4 段階を定義します。
| 分離レベル | ダーティ | ノンリピータブル | ファントム |
|---|---|---|---|
| Read Uncommitted | あり | あり | あり |
| Read Committed | なし | あり | あり |
| Repeatable Read | なし | なし | あり |
| Serializable | なし | なし | なし |
各セルは「その異常が起きうるか」を示します。Serializable のみ全てを防ぎます。
3 つの読み取り異常
ダーティリード (Dirty Read)
他のトランザクションが まだコミットしていない 変更を読んでしまう現象。
プレーンテキスト
T1: UPDATE accounts SET balance = 10000 WHERE id = 1; -- まだ COMMIT してない
T2: SELECT balance FROM accounts WHERE id = 1; -- 10000 が見える
T1: ROLLBACK; -- 巻き戻しT2 は存在しない値を読んでしまいました。これは最も危険な異常です。
ノンリピータブルリード (Non-repeatable Read)
同じトランザクション内で 同じ行を 2 回読むと値が違う 現象。
プレーンテキスト
T1: SELECT balance FROM accounts WHERE id = 1; -- 5000
T2: UPDATE accounts SET balance = 8000 WHERE id = 1; COMMIT;
T1: SELECT balance FROM accounts WHERE id = 1; -- 8000 ← 変わってるT1 は同じ条件で同じ値を期待しますが、別 TX のコミットで値が変わります。
ファントムリード (Phantom Read)
同じ条件で範囲検索すると 行数が違う 現象。
プレーンテキスト
T1: SELECT COUNT(*) FROM orders WHERE status = 'pending'; -- 5 件
T2: INSERT INTO orders (status) VALUES ('pending'); COMMIT;
T1: SELECT COUNT(*) FROM orders WHERE status = 'pending'; -- 6 件 ← 増えてる新規行(ファントム)が出現するのでファントムリードです。
各 DB のデフォルト
| DB | デフォルト分離レベル |
|---|---|
| PostgreSQL | Read Committed |
| MySQL InnoDB | Repeatable Read |
| Oracle | Read Committed |
| SQL Server | Read Committed |
| TiDB | Repeatable Read |
MySQL の Repeatable Read は他より強い(しかも MVCC のおかげでファントムもほぼ防げる)のが特徴です。
切り替え方
SQL クエリ
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
...
COMMIT;セッション全体の設定や、トランザクションごとの設定が可能です。
どう選ぶか
- Read Committed — 大半の Web アプリで十分。並列性が高く性能も良い
- Repeatable Read — レポート生成や集計で「途中で数字が変わらないでほしい」場合
- Serializable — 金融や予約システムで完全な整合性が必要な場合。性能犠牲を受け入れる
- Read Uncommitted — ほぼ使わない。読み取り専用のレプリカで「最新値は気にしない」場合だけ
困ったらデフォルトのまま使う。明示的に変えるのは「異常を許せない場面」だけ。
楽観ロックという別解
分離レベルを上げず、楽観ロック (Optimistic Locking) で衝突を検出する方法もあります。
SQL クエリ
SELECT version, balance FROM accounts WHERE id = 1; -- version=5, balance=5000
-- アプリで残高変更を計算
UPDATE accounts
SET balance = 6000, version = version + 1
WHERE id = 1 AND version = 5; -- 0 行更新なら他人が先に変えてるversion カラムで「読んだ時から変わってないか」を確認します。変わっていたら再試行。楽観ロックは並列性が高く、Web アプリでよく使われます。
やってみよう
- MySQL で
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTEDにして、別セッションでダーティリードを再現する Repeatable Readで同じトランザクション内のSELECTが同じ値を返すことを確認するSerializableでアプリの性能がどれくらい落ちるかベンチを取る
よくある質問
Q. このトピックは実務でどう役立ちますか?
A. DB のクエリ最適化、API 設計、データ構造の選択など、設計判断の根拠になります。表面的にライブラリを使うだけでなく「なぜそれが速いのか」を理解できると、性能問題を未然に防げます。コーディング面接でも頻出のテーマです。
Q. Read Committed と Repeatable Read はどちらをデフォルトにすべきですか?
A. 大半の Web アプリは Read Committed で十分です。PostgreSQL や Oracle のデフォルトでもあり、並列性が高く性能も良好です。集計レポートや「途中で数字が変わってほしくない」処理には Repeatable Read を選択します。MySQL / TiDB はデフォルトが Repeatable Read なので、そのまま使うのが安全です。
Q. ファントムリードとノンリピータブルリードの違いは何ですか?
A. ノンリピータブルリードは「同じ行の値が 2 回目に変わる」現象、ファントムリードは「同じ条件の範囲検索で行数が変わる(新しい行が出現する)」現象です。前者は既存行の UPDATE が原因、後者は INSERT / DELETE が原因という点で異なります。Repeatable Read はノンリピータブルリードを防ぎますが、ファントムリードは理論上防ぎません(MySQL InnoDB は MVCC でほぼ防いでいます)。
次のレッスン
次は MVCC(マルチバージョン同時実行制御) で、各 DB が読み取りをロックせずに一貫性を保つ仕組みを学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 分離レベル の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 分離レベル とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン