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

同時実行されるトランザクション間のデータの見え方を制御するデータベースの設定

データベーストランザクション

トランザクション分離レベルとは

トランザクション分離レベルは、同時実行されるトランザクション間でデータがどのように見えるかを制御する設定である。分離レベルが高いほどデータの一貫性は保たれるが、パフォーマンスは低下する。

4 つの分離レベル

レベル ダーティリード ノンリピータブルリード ファントムリード
Read Uncommitted ✅ 発生 ✅ 発生 ✅ 発生
Read Committed ❌ 防止 ✅ 発生 ✅ 発生
Repeatable Read ❌ 防止 ❌ 防止 ✅ 発生
Serializable ❌ 防止 ❌ 防止 ❌ 防止

異常の種類

ダーティリード:
  TX1: UPDATE balance SET amount = 0  (未コミット)
  TX2: SELECT amount → 0 を読む
  TX1: ROLLBACK → 実際は元の値
  → TX2 は存在しないデータを読んだ

ノンリピータブルリード:
  TX1: SELECT amount → 1000
  TX2: UPDATE amount = 500; COMMIT
  TX1: SELECT amount → 500 (同じ TX 内で値が変わった)

ファントムリード:
  TX1: SELECT COUNT(*) WHERE age > 2010件
  TX2: INSERT (age=25); COMMIT
  TX1: SELECT COUNT(*) WHERE age > 2011件 (行が増えた)

RDS のデフォルト

DB デフォルト分離レベル
PostgreSQL Read Committed
MySQL (InnoDB) Repeatable Read
Aurora PostgreSQL Read Committed
Aurora MySQL Repeatable Read

DynamoDB のトランザクション

DynamoDB は SQL のトランザクション分離レベルとは異なるモデルを採用する。

await db.transactWrite({
  TransactItems: [
    { Update: { TableName: 'accounts', Key: { id: 'A' },
      UpdateExpression: 'SET balance = balance - :amount',
      ExpressionAttributeValues: { ':amount': 100 } } },
    { Update: { TableName: 'accounts', Key: { id: 'B' },
      UpdateExpression: 'SET balance = balance + :amount',
      ExpressionAttributeValues: { ':amount': 100 } } },
  ],
});
// 全操作が成功するか、全操作が失敗する (All or Nothing)

選択の指針

ケース 推奨
一般的な Web アプリ Read Committed
金融取引 Serializable or 楽観ロック
分析クエリ Read Committed (長時間ロックを避ける)
高スループット Read Committed (ロック競合を最小化)

基礎から学ぶなら関連書籍が手がかりになる。

関連用語