発展

トランザクション分離レベルと 3 つの読み取り異常

定義

トランザクション分離レベル (isolation level) とは、並行実行されるトランザクションが互いの未確定・確定済みの変更をどこまで観測しうるかを規定する設定であり、SQL 標準では許容する読み取り異常の種類によって READ UNCOMMITTED / READ COMMITTED / REPEATABLE READ / SERIALIZABLE の 4 段階が定義されている。

分離レベルとは — 「他人の途中経過がどこまで見えるか」の設定

データベースは複数のトランザクションを同時に走らせる。このとき、他人がまだ書き換えている途中のデータが自分にどこまで見えるかを決めるのが分離レベルである。

完全に見えなくすれば安全だが、その分だけ待ち時間が増えて遅くなる。 逆にすべて見せれば速いが、存在しなかった値を読んでしまう。分離レベルは、この安全性と性能のトレードオフをどこで切るかを選ぶダイヤルで、 SQL 標準では 4 段階が定義されている。

4 段階 — 緩い順に、見える範囲が狭くなる

まず 4 段階それぞれが何なのかを、見え方だけで押さえておく。 上に行くほど緩く、下に行くほど厳しい。

分離レベルひとことで何が見えるか
READ UNCOMMITTED書きかけまで見える他のトランザクションがまだ確定していない、書きかけの変更まで見える。最も制限が緩く、取り消される予定の値まで読んでしまう。
READ COMMITTED確定したものだけ見える確定 (COMMIT) された変更だけが見える。ただし見えるのは「読んだその瞬間」の最新状態なので、同じトランザクションの中でも読むたびに結果が変わりうる。
REPEATABLE READ読み始めた時点を見続ける自分が読み始めた時点の状態を、トランザクションが終わるまで見続ける。途中で他人が確定させても自分の視界は変わらない。
SERIALIZABLE順番に実行したのと同じ並行して走っていても、トランザクションを 1 つずつ順番に実行した場合と同じ結果になることを保証する。最も厳しく、最もコストが高い。

仕組みから見ると「読み取りロックをどれだけ長く持つか」の違い

この見え方の差は、どこから生まれるのか。 ロックで実装した場合の古典的なモデルで見ると、4 段階の正体がはっきりする。

分離レベル読み取りロック書き込みロック結果として
READ UNCOMMITTED取らない終了まで保持書き込み中の行もそのまま読めるので、確定していない値が見える。
READ COMMITTED取るが、読んだ直後に解放終了まで保持読む瞬間だけは他人の書き込みと衝突しない。ただし解放後は他人が更新できるので、次に読むと値が変わる。
REPEATABLE READ読んだ行に対して終了まで保持終了まで保持一度読んだ行は誰にも変更されない。ただしロックを掛けられるのは既にある行だけなので、新しく挿入される行は止められない。
SERIALIZABLE行に加えて、検索条件の範囲にも掛ける (述語 / ギャップロック)終了まで保持「条件に合う行が後から増える」ことまで止まる。その代わり待ちと競合が最も多い。

見るべきなのは、書き込みロックの列が 4 段階すべてで同じだということ。 書き込みは常にトランザクション終了まで排他される。 つまり4 段階の違いは、読み取りロックをどれだけ長く持つかだけに現れる。 分離レベルが「読み取り側の設定」と言われるのはこのためである。

※ これはロックで実装した場合のモデルで、標準がロックの掛け方を 規定しているわけではない。主要な DBMS の通常の読み取りはロックを取らない方式で動いている (後述)。どの行を明示的にロックするかを自分で制御する話は同時実行制御で扱う。

緩いほど待たされず速いが、その分だけ「本来読むべきでないもの」が見えてしまう。 では具体的に何が見えると困るのか。それを整理したのが、次の 3 つの読み取り異常である。

3 つの読み取り異常

  • ダーティリード (dirty read)まだ COMMIT されていない、後で取り消される値を読んでしまう。
  • ノンリピータブルリード (non-repeatable read)同じ行を 2 回読んだのに、間に他人が COMMIT したせいで値が変わる。
  • ファントムリード (phantom read)同じ条件で 2 回検索したのに、間に他人が INSERT したせいで行数が変わる。

文章で読むと似て見えるが、実際に起きている順序を見ると別物だと分かる。 下のシミュレーターで、2 つのトランザクション (T1 = 自分 / T2 = 他人) の操作を 1 ステップずつ進めてみてほしい。

分離レベル別 ステップ実行
異常
分離レベル
0 / 5

「次のステップ」を押すと、T1 と T2 の操作が 1 つずつ進む。 分離レベルを切り替えると、同じ操作列のまま T1 の見え方だけが変わる

T1 から見えているもの

まだ読んでいない

確定済みのデータ
iditemqty
1ノート5
2消しゴム8
ダーティリード (dirty read)まだ COMMIT されていない、後で取り消される値を読んでしまう。 SQL 標準の定義では READ UNCOMMITTED で起こりうる。 実際の DBMS はこれより強く防ぐことがある (本文の「標準と実装は違う」を参照)。

操作列は変えずに分離レベルだけを切り替えられるようにしてある。 同じ順序で同じ SQL を実行しているのに、READ UNCOMMITTED では見えていたものがREAD COMMITTED では見えなくなる。これが分離レベルの働きそのものである。

4 段階 × 3 異常のマトリクス

SQL 標準の定義をまとめると次のようになる。「起きうる」は許容されているという意味で、 実装が実際に起こすとは限らない (次の節)。

分離レベルダーティリードノンリピータブルリードファントムリード
READ UNCOMMITTED起きうる起きうる起きうる
READ COMMITTED起きない起きうる起きうる
REPEATABLE READ起きない起きない起きうる
SERIALIZABLE起きない起きない起きない

表を上から下へ読むと、防げる異常が 1 つずつ増えていくのが分かる。 分離レベルが 4 段階なのは、3 つの異常を順に潰していった結果である。

ここでファントムリードだけが 1 段階遅れて防がれていることに気付く。 理由は前に見たロックの表にある。行ロックは既に存在する行にしか掛けられないので、REPEATABLE READ で読んだ行を全部ロックし続けても、まだ存在しない行は誰もロックできず INSERT を止められない。 条件に合う範囲そのものを押さえる別の道具 (述語ロック / ギャップロック) が要る。 3 つのうちファントムだけ防ぐのが遅れるのは、必要な道具が違うからである。

標準の定義と、実際の DBMS の挙動は違う

ここが分離レベルの解説でいちばん混乱しやすい。SQL 標準が定めているのは「そのレベルで最低限これは防ぐ」という下限であって、 実装がそれより強く防ぐことは違反ではない。

しかも手段も標準が決めているわけではない。前節はロックで説明したが、 PostgreSQL や InnoDB の通常の SELECT読み取りロックを取らない。行の複数バージョンを保持しておき、 自分に見えるべきバージョンを選んで返す方式 (MVCC) で同じ保証を実現している。 読み取りが書き込みを待たせないぶん速い。

  • PostgreSQL の REPEATABLE READ はファントムリードも防ぐ。 トランザクション開始時点のスナップショットを固定するため、 標準が許容しているファントムリードも実際には現れない
  • PostgreSQL には READ UNCOMMITTED が実質的に存在しない。 指定はできるが READ COMMITTED として扱われるため、ダーティリードは起こらない
  • MySQL (InnoDB) の既定は REPEATABLE READ。 PostgreSQL の既定 (READ COMMITTED) と違う。 さらに InnoDB はギャップロックによって範囲検索の隙間も押さえるため、ファントムリードを防ぐ

つまり「REPEATABLE READ ならファントムリードが起きる」は標準の話であって、 主要な DBMS の実挙動ではない。試験では標準の定義が問われ、 実務では使っている DBMS の挙動が効く。この 2 つを分けて覚えておくと混乱しない。

実務でどれを選ぶか

既定のまま (READ COMMITTED または REPEATABLE READ) で動かし、必要な処理だけ局所的に上げるのが基本方針になる。

  • 大半の処理: 既定のまま。読んで表示するだけの処理に強い分離は要らない
  • 読んだ値を根拠に書き込む処理 (残高チェックしてから引き落とす、 在庫を見てから確保する): ここだけ引き上げるか、SELECT ... FOR UPDATE で明示的に行をロックする
  • SERIALIZABLE を使うならリトライ処理を書く。 競合したトランザクションは待たされるのではなく失敗することがある。 「遅くなる」より「エラーが増える」ほうが実装上の影響は大きい

なお、分離レベルを上げても防げない事故もある。2 人が同じ行を読んで別々に書き戻し、 後の書き込みが先の書き込みを消してしまう更新消失 (Lost Update)は、分離レベルではなくロックの設計で対処する問題である。

よくある疑問

Q.分離レベルは実務では何を選べばいい?
A.多くのアプリケーションは既定の READ COMMITTED で足ります。残高や在庫のように「読んだ値を根拠に書き込む」処理だけ、その処理に限って REPEATABLE READ や SERIALIZABLE に上げるのが現実的です。全体を SERIALIZABLE にすると競合時のリトライとコストが跳ね上がります。
Q.ファントムリードとノンリピータブルリードの違いは?
A.ノンリピータブルリードは「同じ行の値が変わる」、ファントムリードは「条件に合う行の数が変わる」異常です。前者は UPDATE、後者は INSERT や DELETE によって起こります。行単位のロックでは後者を防げないのが本質的な違いです。
Q.PostgreSQL の REPEATABLE READ ならファントムリードは起きない?
A.起きません。PostgreSQL の REPEATABLE READ はスナップショットを固定するため、SQL 標準が許容しているファントムリードも実際には防ぎます。標準の定義は「そのレベルで許される最低ライン」であって、実装がそれより強く防ぐのは違反ではありません。
Q.MySQL の既定は何?
A.InnoDB の既定は REPEATABLE READ です。PostgreSQL の既定 (READ COMMITTED) と違うため、同じコードを両方で動かすと挙動が変わることがあります。移植時に最初に確認すべき設定の一つです。
Q.なぜファントムリードだけ行ロックで防げないのですか?
A.行ロックは既に存在する行にしか掛けられないためです。読んだ行をすべてロックし続けても、まだ存在しない行は誰もロックできないので INSERT を止められません。防ぐには「条件に合う範囲」そのものを押さえる述語ロックやギャップロックが必要になります。REPEATABLE READ と SERIALIZABLE の差はここにあります。
Q.SERIALIZABLE にすればすべて安全?
A.読み取り異常は防げますが、代わりに競合したトランザクションが失敗するようになります。SERIALIZABLE を使うならアプリケーション側にリトライ処理が必要です。「遅くなる」だけでなく「エラーが増える」ことを織り込んでください。

もっと学びたい方へ(おすすめ書籍)

おすすめ
達人に学ぶDB設計徹底指南書 第2版
ミック

テーブル設計と正規化、パフォーマンス考慮のインデックス設計まで実務レベルで学べる定番書。第2版ではクラウド対応も強化。

Amazon で見る →
おすすめ
楽々ERDレッスン (CodeZine BOOKS)
羽生章洋

ER 図をどう「使える設計」に落とすか、実務の判断まで踏み込んだ入門書。エンティティの切り出しから多対多の扱いまで具体例が豊富。

Amazon で見る →
達人に学ぶSQL徹底指南書 第2版
ミック

SQLの本質的な使い方と、インデックスが効くクエリの書き方を学べる。ウィンドウ関数など現代SQLも網羅。

Amazon で見る →
SQLアンチパターン 第2版
Bill Karwin

実務でやりがちなSQL・DB設計のアンチパターンとその回避策を体系的に学べる。

Amazon で見る →
[改訂3版]内部構造から学ぶPostgreSQL
勝俣智成 ほか

PostgreSQLの内部構造・ストレージ・インデックス機構を丁寧に解説。設計と運用計画の鉄則が学べる。

Amazon で見る →
情報処理教科書 データベーススペシャリスト 2025年版
三好康之

IPAデータベーススペシャリスト試験の総合対策書。インデックス関連は本サイトと合わせて学ぶと理解が深まる。

Amazon で見る →
理論から学ぶデータベース実践入門
奥野幹也

リレーショナルモデルの理論から、インデックス設計を含む実務で使えるSQLまで解説。

Amazon で見る →

本セクションはAmazonアソシエイトのリンクを含みます。

オンライン個別指導

もっと深くDBを学びたい方へ。

たいてっくが、SQL・データベース設計・パフォーマンスチューニング・IPAデータベーススペシャリスト対策まで、1対1で学習をサポートします。まずは無料相談から。

無料相談を予約する →
受講者の声 (DB・SQL コース)
menta レビュー原文を見る →
  • 教え方も上手で、お人柄も良いメンターです。DB周りの知識はもちろん、何より、しっかり教えてあげようという姿勢がとてもありがたかったです。データベース、SQLの学習を考えている方にはおススメです。
    H 様DB・SQL コース受講
  • 体系的に知識を教えてくださり、実際の業務でも大変役立っております。特に短い時間で効率よく知識の習得や、練習をできているのは期待以上でした。
    M 様DB・SQL コース受講
  • 大変充実したコンテンツでわかりやすいご説明をありがとうございました。基本的な質問にも丁寧にご説明いただき、また業務のご相談にも乗って頂き大変有意義な時間でした。
    K 様DB・SQL コース受講