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

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

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

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

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

4 つの分離レベル

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 のデフォルト

RDS のデフォルトを以下にまとめる。

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

DynamoDB のトランザクション

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

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 (ロック競合を最小化)

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

この記事は役に立ちましたか?

関連用語

関連する記事