分散ロックの基本概念
分散ロックは、複数プロセス間での可視性と相互排他を実現するメカニズムです。主な目的は、複数インスタンスが共有リソースへのアクセスを調整し、プログラムの直列実行を保証することにあります。
分散ロックの必須条件
- 可視性: すべてのプロセスが同じ状態を認識可能
- 相互排他: 同時に一つのプロセスのみがロックを保持
- 高可用性: システム障害時も機能継続
- 高性能: ロック操作のオーバーヘッド最小化
- 安全性: 不正なロック解放の防止
実装方式比較
| 方式 | 特徴 |
|---|---|
| MySQL | 組み込みロック機構あり、性能面の課題 |
| Redis | SETNXコマンドによる効率的な実装 |
| ZooKeeper | 分散協調サービス向けの高度なロック管理 |
Redis分散ロックの実装戦略
基本操作
public interface DistributedLock {
boolean acquireLock(long timeout);
void releaseLock();
}
ロック取得の実装例
private final String LOCK_PREFIX = "lock:";
public boolean acquireLock(long timeoutSeconds) {
String lockId = generateLockIdentifier();
return Boolean.TRUE.equals(
redisTemplate.opsForValue().setIfAbsent(
LOCK_PREFIX + resourceName,
lockId,
timeoutSeconds,
TimeUnit.SECONDS
)
);
}
private String generateLockIdentifier() {
return UUID.randomUUID().toString().replace("-", "") +
":" +
Thread.currentThread().getId();
}
ロック解放の課題と解決策
誤解放問題に対処するため、所有者確認プロセスを導入:
public void releaseLock() {
String currentId = generateLockIdentifier();
String storedId = redisTemplate.opsForValue().get(LOCK_PREFIX + resourceName);
if (currentId.equals(storedId)) {
redisTemplate.delete(LOCK_PREFIX + resourceName);
}
}
原子性問題の解決
Luaスクリプトによる原子操作の実現:
-- unlock.lua
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
end
return 0
JavaからのLuaスクリプト実行
private static final RedisScript<Long> UNLOCK_SCRIPT;
static {
UNLOCK_SCRIPT = new DefaultRedisScript<>();
((DefaultRedisScript<Long>) UNLOCK_SCRIPT).setLocation(
new ClassPathResource("unlock.lua")
);
((DefaultRedisScript<Long>) UNLOCK_SCRIPT).setResultType(Long.class);
}
public void safeReleaseLock() {
redisTemplate.execute(
UNLOCK_SCRIPT,
Collections.singletonList(LOCK_PREFIX + resourceName),
generateLockIdentifier()
);
}
ビジネスロジックへの統合例
public TransactionResult processVoucher(Long voucherId) {
// バリデーションロジック
DistributedLock lock = new RedisDistributedLock("voucher:" + voucherId, redisTemplate);
if (!lock.acquireLock(1200)) {
return new TransactionResult("重複処理禁止");
}
try {
// トランザクション処理
return executeTransaction(voucherId);
} finally {
lock.safeReleaseLock();
}
}
完全性を担保する技術要素
- SETNXによる相互排他制御
- 有効期限設定によるデッドロック予防
- 所有者識別による安全な解放
- Luaスクリプトによる原子操作