中学数学621382 views
小学算数1194618 views
雑学1472593 views
中学社会667106 views
MathPython491378 views
高校生物549842 views
中学理科1626207 views
高校倫理1433119 views
LaTeX957300 views
中学英語808712 views
Help
Tools

English

MySQL のトランザクション分離レベル

トランザクション分離レベルは、複数のトランザクションが同時に実行される際に、互いの変更がどの程度見えるかを制御する仕組みだ。分離レベルが高いほどデータの一貫性は保たれるが、並行性やパフォーマンスとのトレードオフが生じる。MySQL の InnoDB では 4 つの分離レベルが用意されており、要件に応じた使い分けが求められる。

4 つの分離レベル

SQL 標準では 4 段階の分離レベルが定義されている。InnoDB はこれらすべてをサポートしており、デフォルトは REPEATABLE READ だ。

分離レベル特徴
READ UNCOMMITTED他のトランザクションの未コミットデータが見える
READ COMMITTED他のトランザクションのコミット済みデータのみ見える
REPEATABLE READトランザクション開始時点のスナップショットを参照する
SERIALIZABLEすべての読み取りに暗黙の共有ロックがかかる

分離レベルが低いほど他のトランザクションの影響を受けやすく、高いほど安全だが制約も強くなる。この 4 つの違いを理解するには、それぞれで防げる「読み取り異常」を把握しておく必要がある。

読み取り異常の種類

トランザクション間で発生する読み取り異常には 3 つの代表的なパターンがある。

ダーティリード

他のトランザクションがまだコミットしていない変更を読み取ってしまう現象。その変更がロールバックされた場合、存在しないデータを読んだことになる。

ノンリピータブルリード

同一トランザクション内で同じ行を 2 回読み取った際に、別のトランザクションによる更新の影響で異なる値が返される現象。

ファントムリード

同一トランザクション内で同じ条件の範囲検索を 2 回実行した際に、別のトランザクションによる行の挿入・削除の影響で結果行数が変わる現象。

各分離レベルがどの読み取り異常を防ぐかを整理すると、選択の判断材料になる。

分離レベルダーティリードノンリピータブルリードファントムリード
READ UNCOMMITTED発生する発生する発生する
READ COMMITTED防止発生する発生する
REPEATABLE READ防止防止InnoDB では防止
SERIALIZABLE防止防止防止

InnoDB の REPEATABLE READ はネクストキーロックによってファントムリードも防止できるため、SQL 標準の定義よりも強い分離を提供している点が特筆すべきポイントだ。

READ UNCOMMITTED

最も低い分離レベルで、他のトランザクションの未コミットデータまで読み取ってしまう。

-- セッションの分離レベルを変更
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- トランザクション A
BEGIN;
UPDATE products SET price = 500 WHERE id = 1;
-- まだ COMMIT していない

-- トランザクション B(別セッション)
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
BEGIN;
SELECT price FROM products WHERE id = 1;
-- → 500 が見える(未コミットのデータ)
-- トランザクション A がロールバックすると、この 500 は幻のデータになる

実用上、このレベルを選択する場面はほとんどない。厳密さが不要な統計集計や、おおよその件数確認といったごく限定的な用途で使われる程度だ。

READ COMMITTED

コミットされたデータのみを読み取る分離レベルで、Oracle や PostgreSQL のデフォルトでもある。ダーティリードは防止されるが、同一トランザクション内で同じクエリを実行しても異なる結果が返る可能性がある。

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- トランザクション A
BEGIN;
SELECT price FROM products WHERE id = 1;  -- → 100

-- トランザクション B(別セッション)で更新してコミット
UPDATE products SET price = 200 WHERE id = 1;
COMMIT;

-- トランザクション A の続き
SELECT price FROM products WHERE id = 1;  -- → 200(変わっている)
COMMIT;

READ COMMITTED では、SELECT を実行するたびに最新のスナップショットが生成される。

REPEATABLE READ がトランザクション開始時の 1 つのスナップショットを使い続けるのに対し、READ COMMITTED は文ごとにスナップショットを更新するため、他のトランザクションのコミット済み変更が即座に反映される。

この分離レベルは、InnoDB のギャップロックが無効になるという特性を持つ。ギャップロックによるロック競合がパフォーマンスのボトルネックになっている場合に、READ COMMITTED に変更することで改善できるケースがある。ただし、ファントムリードが発生する可能性がある点は受け入れる必要がある。

REPEATABLE READ(デフォルト)

MySQL InnoDB のデフォルト分離レベルであり、トランザクション開始時点のスナップショットを一貫して参照する。同じクエリを何度実行しても、そのトランザクション内では同じ結果が返る。

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- トランザクション A
BEGIN;
SELECT price FROM products WHERE id = 1;  -- → 100

-- トランザクション B(別セッション)で更新してコミット
UPDATE products SET price = 200 WHERE id = 1;
COMMIT;

-- トランザクション A の続き
SELECT price FROM products WHERE id = 1;  -- → 100(変わらない)
COMMIT;

この一貫性は MVCC(Multi-Version Concurrency Control)によって実現されている。InnoDB は各行の過去のバージョンを UNDO ログに保持しており、トランザクションの開始時点より後に変更されたデータについては、過去のバージョンを参照する仕組みになっている。

MVCC の利点

読み取りにロックが不要なため、書き込みと読み取りが互いをブロックしない。高い並行性を実現する InnoDB の中核技術といえる。

MVCC の注意点

長時間実行されるトランザクションがあると、UNDO ログが肥大化してパフォーマンスに影響を及ぼす可能性がある。トランザクションは適切な長さに保つべきだ。

SERIALIZABLE

最も厳格な分離レベルで、すべての SELECT 文が暗黙的に SELECT ... FOR SHARE として実行される。読み取りにも共有ロックがかかるため、読み取り中の行に対する他のトランザクションからの更新はブロックされる。

SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- トランザクション A
BEGIN;
SELECT * FROM products WHERE id = 1;
-- この行に共有ロックがかかる

-- トランザクション B(別セッション)
BEGIN;
UPDATE products SET price = 200 WHERE id = 1;
-- → トランザクション A がコミットまたはロールバックするまで待機

データの完全な一貫性が保証されるが、ロック競合の増加によるパフォーマンス低下が避けられない。金融取引のように整合性が最優先される限定的な場面を除けば、通常は REPEATABLE READ で十分だ。

分離レベルの確認と変更

現在の分離レベルは以下のコマンドで確認できる。

-- 現在のセッションの分離レベル
SELECT @@transaction_isolation;

-- グローバル設定の確認
SELECT @@global.transaction_isolation;

-- セッション単位で変更
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- グローバルで変更(新規セッションに適用)
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- my.cnf で恒久的に設定
-- [mysqld]
-- transaction-isolation = READ-COMMITTED

セッション単位での変更は現在の接続にのみ影響し、グローバルでの変更は新しく接続したセッションから適用される。既存のセッションには影響しない点に注意が必要だ。

実務での選び方

多くのアプリケーションでは、デフォルトの REPEATABLE READ のまま運用して問題ない。InnoDB の MVCC とネクストキーロックの組み合わせにより、高い並行性と強い一貫性を両立できるためだ。

まずは REPEATABLE READ(デフォルト)で運用を開始する

ギャップロックによるロック競合が頻発する場合は READ COMMITTED への変更を検討する

変更時はアプリケーションの動作に影響がないかテスト環境で十分に検証する

分離レベルの変更はアプリケーション全体の振る舞いに影響を及ぼすため、安易に変更すべきではない。特に READ COMMITTED に変更する場合は、ステートメントベースのレプリケーションが使えなくなる制約があるほか、ファントムリードへの対策をアプリケーション側で講じる必要が出てくる。トレードオフを十分に理解したうえで判断することが重要だ。