データベーストランザクションの分離レベルと一貫性問題

分離レベルの基本概念

データベーストランザクションには4つの分離レベルが存在し、整合性と並行性のバランスを調整します。レベルは低い順に次の通りです:

  • Read Uncommitted(未コミット読み取り)
  • Read Committed(コミット済み読み取り)
  • Repeatable Read(反復可能読み取り)
  • Serializable(直列化可能)
並行処理では次の整合性問題が発生する可能性があります:
  • ダーティリード: 未コミットの変更を別トランザクションが読み取る
  • 非再現読み取り: 同一トランザクション内の複数回の読み取り結果が不一致
  • ファントムリード: 範囲クエリで新規レコードが突然出現する現象

Read Uncommitted(未コミット読み取り)

未コミットの変更を他トランザクションが参照可能です。例:銀行口座振込処理中、送金元が$10,000減額(未コミット)の状態で、別ユーザーが残高を確認すると一時的な不一致が発生。この現象がダーティリードです。整合性保証が最も低く、実用は稀です。

Read Committed(コミット済み読み取り)

コミット済みデータのみ読み取ります。例:ECサイトの在庫管理で、商品Aの在庫数10をユーザーが確認。同時に管理者が在庫を8に更新しコミット。ユーザーが再度確認すると在庫数が変化。ダーティリードは防止できますが、非再現読み取りが発生します。SQL ServerやOracleのデフォルトレベルです。

SELECT stock_count FROM products WHERE product_id = 101 FOR UPDATE;

スナップショット機構によりロックなしで一貫性を提供しますが、更新操作との競合は解決できません。

Repeatable Read(反復可能読み取り)

トランザクション開始時のデータ状態を維持します。例:月末集計処理中、売上データの合計値を計算している間、新規売上記録の追加をブロック。これにより非再現読み取りを防止しますが、新規レコード挿入によるファントムリードは未解決です。MySQLのデフォルトレベルです。

Serializable(直列化可能)

トランザクションを完全直列処理します。例:航空券予約システムで座席確保処理を逐次実行。全ての整合性問題を解決しますが、パフォーマンス低下が顕著で実運用は限定的です。

MySQLにおけるファントムリード対策

発生条件:InnoDBストレージ・Repeatable Readレベル・現在読み取り操作時
原因:既存レコードロックでは新規挿入をブロック不可
解決手法

  • スナップショット読み取り:MVCC(多版同時実行制御)で整合性確保
  • 現在読み取り:ネクストキーロック(レコード+ギャップロック)で挿入防止

-- 現在読み取りのロック例
SELECT * FROM orders WHERE user_id = 2001 LOCK IN SHARE MODE;

操作タイプ比較

読み取り種別取得データロック方式使用例
スナップショットトランザクション開始時点ロックなし基本SELECT
現在最新コミット値明示的ロックSELECT FOR UPDATE

分離レベルと問題の対応関係

分離レベルダーティリード非再現読み取りファントムリード
Read Uncommitted発生発生発生
Read Committed防止発生発生
Repeatable Read防止防止発生
Serializable防止防止防止

タグ: データベース トランザクション 分離レベル MySQL InnoDB

6月21日 23:29 投稿