分散ロック

分散システムで複数のプロセスが同じリソースに同時アクセスすることを防ぐ排他制御

分散システム排他制御

分散ロックとは

分散ロック (Distributed Lock) は、分散システムで複数のプロセスが同じリソースに同時アクセスすることを防ぐ排他制御の仕組みである。単一プロセスの mutex と異なり、ネットワーク越しのロックが必要。

なぜ必要か

❌ ロックなし:
  Lambda A: 在庫を読む (残り 1)
  Lambda B: 在庫を読む (残り 1)
  Lambda A: 在庫を 0 に更新
  Lambda B: 在庫を 0 に更新
  → 2 つの注文が成立 (在庫は 1 つしかない)

✅ ロックあり:
  Lambda A: ロック取得 → 在庫を読む → 更新 → ロック解放
  Lambda B: ロック取得を待つ → 在庫 0 → 注文拒否

DynamoDB での分散ロック

// ロックの取得
async function acquireLock(lockId: string, owner: string, ttlMs: number) {
  const now = Date.now();
  await db.put({
    TableName: 'locks',
    Item: { lockId, owner, expiresAt: now + ttlMs },
    ConditionExpression: 'attribute_not_exists(lockId) OR expiresAt < :now',
    ExpressionAttributeValues: { ':now': now },
  });
}

// ロックの解放
async function releaseLock(lockId: string, owner: string) {
  await db.delete({
    TableName: 'locks',
    Key: { lockId },
    ConditionExpression: '#owner = :owner',
    ExpressionAttributeNames: { '#owner': 'owner' },
    ExpressionAttributeValues: { ':owner': owner },
  });
}

分散ロックの実装方法

方法 ツール 特徴
条件付き書き込み DynamoDB サーバーレスに最適
SET NX Redis (ElastiCache) 高速、TTL 付き
Redlock Redis (複数ノード) 高可用性
ZooKeeper Apache ZooKeeper 強い整合性

ロックの注意点

問題 対策
デッドロック TTL を設定し、期限切れで自動解放
ロック保持者の障害 TTL で自動解放
クロック問題 フェンシングトークンを使用
パフォーマンス ロックの粒度を細かくする

フェンシングトークン

1. ロック取得時にトークン (単調増加する番号) を発行
2. リソースへの書き込み時にトークンを添付
3. リソース側で古いトークンの書き込みを拒否
→ 期限切れのロックで古い書き込みが成功するのを防ぐ

サーバーレスでの代替手段

Lambda ではロックの代わりに冪等性で対処するのが推奨。DynamoDB の条件付き書き込みによる楽観ロック (バージョン番号)、SQS FIFO によるメッセージの順序保証 + 重複排除、Step Functions によるワークフローでの排他制御が代替手段だ。

分散ロックの理解を深めるには関連書籍が参考になる。

関連用語