コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB

分離レベル

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

分離レベル

このレッスンで分かること

  • 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 件 ← 増えてる

新規行(ファントム)が出現するのでファントムリードです。

diagram (will load when visible)

各 DB のデフォルト

DBデフォルト分離レベル
PostgreSQLRead Committed
MySQL InnoDBRepeatable Read
OracleRead Committed
SQL ServerRead Committed
TiDBRepeatable 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 つができることを確認しましょう。

  1. 分離レベル の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. 分離レベル とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン