web-dev-qa-db-ja.com

分散システムで同じレコードの同時更新を処理する方法

以下のための最良の解決策を見つけ出そうとしています。どんな助けでも素晴らしいでしょう。

したがって、基本的には、キューでリッスンするサービス(水平方向にスケーリングできる)があります。受信したすべてのメッセージは、ジョブにディスパッチされ、同時に処理されます。

ジョブは(同じ順序で)次のようになります。

  1. メッセージのペイロードに基づいてデータを生成する
  2. Redisにデータをキャッシュする
  3. 別のサービスにデータを送信する

私の問題は、同じ論理レコード(同じテーブルレコードですがデータが変更されている)に対して別のメッセージが受信されたときです。
同じレコードIDの2つ以上のメッセージが処理されているシナリオで、最新バージョンのデータがRedisにキャッシュされ、次のサービスに送信されることを確認する必要があります。したがって、古いバージョンのペイロードを持つジョブが最新のものを上書きすることを回避します。

分散ロックメカニズムの使用を検討していますが、それが効率的かどうかはわかりません。特に、最新バージョンを次のサービスにできるだけ早く送信したい場合は特にそうです。
ジョブ全体をロックするのではなく、古いペイロードのジョブをキャンセルする方法があるのでしょうか? Redis pubsubを使用してサービス間で通信するか(スケーリング時)、またはより良い方法がありますか?

3
Alan

私があなたなら、次のコンポーネントを含むメッセージごとにメタレコードを作成します。

  • 論理レコードのキー
  • このメッセージのタイムスタンプ
  • ペイロード値

各プロセスに中間データストアにレコードを作成させ、永続データストアを無効にします。次に、ハウスキーピングプロセス(レフェリーと呼ぶ)が最新バージョンを決定できるようにし、最終的なデータストアに表示されるようにプロモートします。

この設定は書き込み用に最適化されています。

もちろん、この手法には拡張性の制限があります。特定のしきい値を下回ると、結果的に一貫性が得られます。

データを取得するときは、次のような優先取得システムからデータを引き出します。

  • 永続データストアが無効化されていない場合は、選択してください
  • データストアが無効になっている場合は、一時データストアからデータを選択し、タイムスタンプの降順で並べ替えます
1
A.Rashad

私が間違っている場合は私を修正してくださいが、次のデータフローがあるようです

Message Queue: InBox
Service1 : read message from InBox
           process InBox.Id
           save result to Redis
           send message to S1Out with S1Out.Id = Start.Id

Message Queue: S1Out
Service2 : read message from S1Out
           read data from Redis with S1Out.Id
           save data to DB

問題は、同じIDに対して2つのInBoxメッセージを取得できることと、redisデータが2番目に処理されたリクエストによって上書きされることです。サービス2の動作がおかしい。

S2がredisキャッシュ内のデータを参照するのではなく、Service 2が必要とするすべてのデータをS1Outメッセージで送信したいのです。 (大きなメッセージ)

これにより、問題と、キャッシュの障害/ボトルネックの中心点が排除されます。

しかしながら。別のアプローチは、キャ​​ッシュに保存された各アイテムの新しいIDを生成し、そのIDをメッセージに追加することです。これにより、サービス2はジョブの正しいデータを取得できます。

冗長なジョブがキューを流れるという2番目の問題は、共有リソースに到達するまでは対処が困難です。

私の例では、サービス2がデータをDBに保存します。この時点で、ジョブが冗長であったかどうかを確認できます。サービスは元のメッセージのタイムスタンプを知っており、データベースは処理されたメッセージのタイムスタンプを知っているので、サービスは比較してスキップしたり、古い作業から除外したりできます。

または、S1Outからの読み取り、メッセージのバッファーの保持、冗長な作業の削除または並べ替え、S1Out2への投稿のみを行う追加の「ルーターワーカー」を追加することもできます。しかし、これはもう1つの失敗の中心点になります。あたかもそれらが2つあるかのように、彼らはお互いのバッファを見ることができません。

私の推奨するアプローチは、すべてのメッセージを処理し、それらが実際の問題を引き起こす場合にのみ冗長な効果をドロップすることです。 (つまり、CPU使用率を最適化するだけではありません)

0
Ewan