1 基本概念

分散ロックは、複数の計算機やプロセスが同じ資源を同時に扱う場面で、特定の実行主体だけに排他的な利用権を与える仕組みである。主な目的は、更新衝突を避け、データ整合性を保ち、処理の重複を抑えることにある。単独の計算機内で使う排他制御と異なり、通信遅延や部分的障害を前提設計される点が特徴である。

1.1 分散ロックの定義

分散ロックとは、ネットワーク越しに共有される状態を用いて、ある資源へのアクセス権を一時的に独占させる手法である。多くの場合、ロックの所有者、取得時刻、有効期限などの情報を外部の共有基盤に記録し、他の利用主体が同じ資源を同時に使わないよう制御する。

1.2 必要とされる背景

分散環境では、同じ処理が複数回走ったり、複数の実行主体が同一データを更新したりすることが珍しくない。そのため、排他的な制御機構がないと、結果が不定になったり、後続処理が矛盾した状態を前提に動いたりする。分散ロックは、こうした不整合を抑えるための基本的な手段として用いられる。

1.2.1 同時更新の競合

複数のワーカーが同じレコードや設定値を書き換えると、最後に書いた内容だけが残る場合がある。このとき、先に行われた処理の結果が失われる可能性がある。分散ロックは、更新前に排他的な権利を確保することで、競合の発生を減らす。

1.2.2 重複処理の防止

スケジューラの再実行やメッセージの再配送により、同じ処理が重ねて実行されることがある。請求作成、通知送信、集計更新のような処理では、重複は実害につながりやすい。ロックは、同一対象に対する並列実行を抑え、二重処理の回避に役立つ。

1.3 単一計算機のロックとの違い

単一計算機のロックは、同じメモリ空間やカーネル機構の範囲で完結し、障害の形も比較的限定される。一方、分散ロックでは、通信断やノード停止、時刻のばらつきなど、外部要因を考慮しなければならない。また、取得できたとしても、その状態が他の参加者からも一貫して見えているとは限らないため、より慎重な設計が必要になる。

2 動作原理

分散ロックの基本は、共有基盤に「この資源は現在誰が使っているか」を表す状態を作り、その状態の競合を通じて排他を実現することにある。取得と解放の手順は単純に見えるが、失敗や遅延が混ざるため、実際には期限管理や再確認の仕組みが重要になる。

2.1 排他制御の考え方

排他制御では、ある時点で許可される利用主体を一つに限定する。別の主体が同じ資源を使いたい場合は、既存の所有者が手放すまで待機するか、失敗として扱う。分散ロックでは、この「一人だけが持てる」という条件を、共有ストア上の状態遷移で表現する。

2.2 ロック取得の流れ

ロック取得は、要求を出し、空き状態かどうかを確認し、成功したら所有権を記録する流れで進む。解放もまた、所有者であることを確認したうえで状態を消すか、無効化する。実装によっては、更新が途中で分断されないよう、原子的な操作が求められる。

2.2.1 取得要求

利用主体は、対象資源の識別子とともにロック取得を申し込む。共有基盤は、すでに別の所有者がいないかを調べる。空いていれば取得候補として扱い、占有中なら待機や失敗が返される。

2.2.2 成功判定

成功判定では、単に空いていたかどうかだけでなく、自分の要求が最後まで反映されたかを確認する。複数の要求が同時に到達した場合、判定の競合が起きうるため、比較と更新を一つの操作として扱うことが望ましい。

2.2.3 解放処理

解放処理では、所有者自身だけがロックを外せるようにするのが一般的である。識別子を照合し、正当な保持者であることが確認できた場合にのみ状態を消去する。これにより、他者による誤解除を防ぎやすくなる。

2.3 期限付きロック

期限付きロックは、取得後に一定時間だけ有効な権利を与える方式である。保持者が停止したり通信不能になったりしても、期限が切れれば自動的に利用可能状態へ戻せるため、資源の永久占有を避けやすい。

2.3.1 有効期限の設定

有効期限は、処理に必要な時間より少し長めに定めることが多い。短すぎると正当な処理が途中で失効し、長すぎると障害時の回復が遅れる。適切な値は、実行時間の分布再試行の方針によって変わる。

2.3.2 自動失効

自動失効は、保持者が明示的に解放しなくても、期限到来でロックが無効になる仕組みである。これにより、プロセス停止やネットワーク障害で解放命令が届かない場合でも、永久的な詰まりを避けやすい。ただし、処理がまだ継続している最中に失効すると、重複実行の危険が生じる。

3 実装方式

分散ロックは、単一の専用製品だけでなく、既存の基盤を流用して実現されることが多い。代表的には、共有データベース、分散キーバリューストア、メッセージ仲介、仲介サービスの四つの系統がある。いずれも、原子性、可視性、障害時の振る舞いをどう確保するかが要点となる。

3.1 共有データベースを用いる方式

共有データベースは、広く使われている永続的な保管場所であり、更新の一貫性を活かしてロック表現に利用できる。テーブルの行や一意制約を使って、資源の占有状態を表す方法が一般的である。

3.1.1 行ロック

ロック専用の行を用意し、その行に対する更新を占有の合図とする方式である。取得時に行を挿入または更新し、同じ行に対する衝突で競合を検出する。解放は行の削除や状態変更で行う。

3.1.2 一意制約の利用

一意制約を使う方式では、同じ資源識別子を重複登録できない性質を利用する。先に登録できた主体がロック保持者となり、後続は制約違反で失敗する。実装が簡潔である一方、失敗時の再試行設計が重要になる。

3.2 分散キーバリューストアを用いる方式

分散キーバリューストアは、キーと値の対応で状態を保持し、比較的高速な読み書きが可能である。設定値の保存や一時的な状態管理に向いており、ロック情報もキーとして表現できる。

3.2.1 取得と解放の原子性

取得と解放は、途中で他の処理に割り込まれない原子的操作として行う必要がある。たとえば、存在確認と書き込みを分けると、同時実行により二重取得が発生しうる。そのため、条件付き更新や比較交換の機能が重視される。

3.2.2 失効時間の管理

キーバリューストアでは、キーに期限を設定して自動削除させる設計がよく用いられる。これにより、保持者が応答不能になってもロックが残り続ける事態を抑えられる。もっとも、期限更新の遅延や時計差の影響には注意が必要である。

3.3 メッセージ仲介型の方式

メッセージ仲介型では、処理要求を順番に並べ、1件ずつ消費させることで実質的な直列化を行う。厳密にはロックそのものではないが、同じ資源に対する競合を減らす方法として利用される。

3.3.1 キューによる直列化

同一対象に対応するメッセージを一列に並べ、先頭から順に処理することで、並行更新を避ける。資源ごとにキューを分ける設計もある。これにより、排他制御を明示的なロックより自然に扱える場合がある。

3.3.2 ワーカー分散との関係

ワーカーを複数台に増やしても、同一キューの消費順を保てれば、処理の重複を抑えられる。ただし、ワーカーの障害時には未完了メッセージの再配送が起こるため、実際にはべき等な処理設計と組み合わせることが望ましい。

3.4 仲介サービスを用いる方式

仲介サービスは、参加者の管理や状態同期を担う専用の仕組みである。ロックやリーダー選出の機能を持つものもあり、参加者が共通の状態を監視しながら協調動作する。

3.4.1 一時ノードの活用

一時ノードは、作成した主体が消えると自動的に削除される項目である。これを使うと、保持者の消失を監視しやすく、放置された状態を長引かせにくい。ロックの所有情報や待機列の管理に利用される。

3.4.2 リーダー選出との連携

リーダー選出では、複数候補の中から代表者を一人決める。これは、実質的に全体の排他制御の一種として働くことがある。代表者が決まると、特定処理を一箇所に集約でき、ロックの運用を簡素化できる。

4 設計上の課題

分散ロックは便利だが、単純な実装では誤作動を招きやすい。特に、可用性と一貫性の優先順位、障害時の状態回復、期限の扱い、待ち行列の詰まりなどは、設計段階で十分に検討する必要がある。

4.1 可用性と一貫性の両立

強い一貫性を重視すると、ロックの状態は安定するが、障害時に取得できない時間が増えることがある。逆に、可用性を優先しすぎると、同時保持のような危険な状態を招きやすい。用途に応じて、どこまで厳密さを求めるかを定めることが重要である。

4.2 障害時の安全性

障害が起きた場合、ロックが残るのか、消えるのか、再取得できるのかを明確にしておく必要がある。安全性の観点では、障害後に二重実行や整合性破壊が起きないことが最優先となる。

4.2.1 ネットワーク分断

ネットワーク分断が起きると、あるノードはロック保持中だと思っていても、別のノードには失効したように見えることがある。こうした見え方の差は、同じ資源を複数者が同時に操作する危険を増やす。

4.2.2 プロセス停止

実行中のプロセスが停止すると、解放処理が行われないまま残る可能性がある。期限付き方式であれば、一定時間後に自動回復できるが、その間は他の主体が待たされる。停止検知と期限設定のバランスが重要になる。

4.2.3 再起動後の整合性

再起動したプロセスが、停止前のロック保持状態を誤って前提にすると、古い操作の続きを行ってしまうことがある。そのため、再起動後は所有権を再確認し、必要に応じて作業を最初からやり直す設計が求められる。

4.3 時計のずれと期限管理

期限付きロックでは、各ノードの時計が完全には一致しないことが問題になる。ある機器ではまだ有効でも、別の機器では期限切れと判断される場合がある。したがって、絶対時刻だけに頼らず、中央管理や猶予時間の導入が検討される。

4.4 解放漏れと重複取得

解放漏れがあると、資源が長く塞がれたままになる。一方、重複取得が起きると、複数の主体が同じ権限を持ったように見えてしまう。どちらも危険であり、所有者識別子、期限、再確認を組み合わせて対策することが多い。

4.5 デッドロックとライブロック

複数のロックを順に取る設計では、互いに相手の解放を待ち合って進まなくなることがある。これはデッドロックである。ライブロックは、進行しているように見えて実質的な完了に至らない状態を指し、再試行の繰り返しや衝突回避の失敗で生じやすい。

5 運用と実務上の注意

実運用では、理論上の正しさだけでなく、障害が起きたときにどれだけ扱いやすいかが重要である。再試行の回数、待機時間、監視指標、警告基準を整え、想定外の競合が起きても原因を追跡できるようにしておく必要がある。

5.1 再試行戦略

ロック取得に失敗したときは、すぐに再試行するのではなく、短い待機を挟むことが多い。固定間隔よりも、待ち時間を少しずつ変える方法が衝突を減らしやすい。再試行回数に上限を設けることで、無限ループも避けられる。

5.2 監視と可観測性

ロックの挙動を把握するには、取得成功率、待機の長さ、失敗理由などを記録する必要がある。監視が不十分だと、詰まりや性能低下が起きても発見が遅れる。運用上は、異常な集中や長時間保持を早期に見つけられることが望ましい。

5.2.1 ロック待ち時間の計測

待ち時間の計測は、競合の激しさを知る手がかりになる。長い待ちが頻発する場合、資源設計や処理分割を見直す余地がある。平均だけでなく、最大値や分布も確認すると実態をつかみやすい。

5.2.2 失敗回数の把握

失敗回数が増えているなら、ロック競合、基盤障害、設定不備のいずれかが疑われる。失敗の種類を分けて記録すれば、単なる混雑とシステム異常を区別しやすい。これは改善作業の優先順位づけにも役立つ。

5.3 タイムアウトの設定

タイムアウトは、待ち続けて全体が停滞するのを防ぐための基本設定である。短すぎると正常な処理まで失敗扱いになり、長すぎると障害時の回復が遅くなる。処理時間と障害回復時間の両面を考慮して決める必要がある。

5.4 競合の多い場面での最適化

アクセスが集中する場面では、単一のロック対象がボトルネックになりやすい。そこで、資源を細かく分ける、処理をまとめる、順序を固定するなどの工夫が有効になる。場合によっては、ロックを減らしてべき等性や後処理の補正で補う設計も選ばれる。

6 関連技術

分散ロックは、広い分散システム設計の中で、いくつかの関連技術と密接に結びついている。完全な代替ではなく、目的や保証の強さに応じて使い分けられる。

6.1 分散トランザクション

分散トランザクションは、複数の資源にまたがる更新を一まとまりとして扱う仕組みである。ロックより厳密な整合性を目指せる場合があるが、実装や運用は重くなりやすい。用途によっては、より軽量な分散ロックで十分なこともある。

6.2 リーダー選出

リーダー選出は、複数の参加者から代表者を一つ決める技術である。代表者が一括して処理を担当する形にすると、個別のロックを減らせる。分散ロックは、この代表者決定を支える手段としても使われる。

6.3 一意性保証

一意性保証は、同じ対象が重複しないことを確かめる考え方である。注文番号、ジョブID、イベントIDなどを重複させない設計は、ロックの負担を軽減する。厳密な排他が難しい場面でも、一意性の工夫で実害を抑えられる。

6.4 べき等性の設計

べき等性の設計では、同じ処理を複数回行っても結果が変わらないようにする。これにより、ロックに全面依存せず、再試行や再配送に強い構成を作りやすい。分散ロックと組み合わせると、誤作動に対する耐性が高まる。

7 応用例

分散ロックは、重複や衝突が問題になりやすい業務でよく使われる。完全な排他が必要な場面だけでなく、実行順序をある程度整えたい場合にも有効である。

7.1 定期処理の重複防止

定期実行ジョブが複数のインスタンスで同時に起動すると、同じ処理が二度走ることがある。ロックを使えば、ある時点で一つの実行主体だけに処理を任せられる。これにより、集計や通知の重複を抑えやすい。

7.2 共有ファイルの保護

共有ファイルを書き換える際、複数のプロセスが同時に編集すると内容が壊れる場合がある。ロックを通じて書き込み中の独占権を与えれば、他方は待機できる。結果として、保存内容の衝突を減らせる。

7.3 在庫更新の整合性確保

在庫数の減算や予約確定では、同時更新によって数量がずれると、実際の保有量と記録が一致しなくなる。分散ロックは、対象商品や在庫レコードごとに排他をかけ、矛盾した更新を防ぐ補助手段となる。

7.4 バッチ処理の直列化

大量データを扱うバッチでは、同じ対象を並列に処理すると競合が起こりやすい。ロックで順番を制御すると、処理負荷を抑えつつ、結果の再現性を高められる。特に、集計、移行、締め処理などで利用されることがある。