Redisトランザクションの基本概念
Redisトランザクションは複数のコマンドを単一の操作として実行する仕組みです。すべてのコマンドは順序付けられ、他のコマンドによる割り込みが発生しないよう直列化して実行されます。
主要なトランザクションコマンド
| コマンド | 機能 |
|---|---|
| MULTI | トランザクション開始を宣言 |
| EXEC | トランザクション内の全コマンド実行 |
| DISCARD | トランザクション中断 |
| WATCH | キーの変更監視 |
| UNWATCH | 監視状態の解除 |
トランザクション実行フロー
- 開始: MULTIコマンドでトランザクション開始
- キュー登録: コマンドを実行キューに追加(即時実行されない)
- 実行: EXECコマンドでキュー内コマンドを一括実行
特性と制約
- 分離性: トランザクション実行中は外部コマンドの割り込み不可
- 原子性保証なし: 一部コマンド失敗時も後続処理は継続
- エラーハンドリング:
- EXEC前エラー: 全コマンドがキャンセル
- EXEC後エラー: エラーコマンドのみ失敗
楽観的ロックによる競合制御
WATCHコマンドの動作原理
WATCHで監視したキーがトランザクション実行前に変更されると、トランザクション全体が中止されます。これは楽観的ロックの実装であり、競合状態を検出します。
監視パターン実装例
WATCH account_balance current_balance = GET account_balance MULTI SET account_balance (current_balance - 100) EXEC
CAS(Compare-and-Swap)の概念
RedisのWATCHメカニズムはCAS操作に類似しており、以下の要素で構成されます:
- メモリ値(V)
- 期待値(A)
- 更新値(B)
V == Aの場合のみVをBに更新し、不一致時は操作を中止します。
ABA問題への対応
値がA→B→Aと変化した場合、標準のCASでは検出不能です。RedisではWATCHによるキー監視でこの問題を回避します。
JavaでのAtomicStampedReference実装
public boolean compareAndSet(
Object expectedRef,
Object newRef,
int expectedStamp,
int newStamp
) {
ReferencePair<Object> current = internalPair;
return expectedRef == current.reference &&
expectedStamp == current.stamp &&
((newRef == current.reference &&
newStamp == current.stamp) ||
updatePair(current, newRef, newStamp));
}
監視解除のタイミング
EXECまたはDISCARD実行後、すべてのWATCH監視が自動的に解除されます。明示的な解除にはUNWATCHを使用します。