トランザクション分離レベル
同時実行されるトランザクション間のデータの見え方を制御するデータベースの設定
データベーストランザクション
トランザクション分離レベルとは
トランザクション分離レベルは、同時実行されるトランザクション間でデータがどのように見えるかを制御する設定である。分離レベルが高いほどデータの一貫性は保たれるが、パフォーマンスは低下する。
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 > 20 → 10件
TX2: INSERT (age=25); COMMIT
TX1: SELECT COUNT(*) WHERE age > 20 → 11件 (行が増えた)
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 (ロック競合を最小化) |
基礎から学ぶなら関連書籍が手がかりになる。