既存のソリューションが機能しない可能性のある分散データストアが必要です。これらを実行するコンピューターは、リソースが非常に制限されます(例:64〜128 MB RAM)。さらに、楽しい練習として。
私は RAFTアルゴリズム の簡単な実装を書いていますが、データストア(これは単にキー/値のコレクションになります)は、クライアントがいつでもノードを更新する可能性があります。クラスタ全体で一貫性を保つ必要があります。
私は、クライアントから更新されるたびにハッシュを計算するノードを用意することを考えていました。その後、新旧のハッシュでクラスターの他のメンバーからコンセンサス/確認を取得し、大多数から取得されたらコミットします。 2つのメンバーが同時に更新されたときに、順序正しく更新されますか?これを実装する上での考えは?
あなたの質問に対する私の理解は、任意の時点Tで、任意のキーに関連付けられたその値についてそれらのノードのいずれにも尋ねることができ、同じ答えを得ることができるということです。
同様に、任意のサーバー上で任意のキーを更新でき、そのキーの別の(または同じノード)の次の読み取り時に、New値を参照します(他の誰もそれを更新していないと仮定)。
さらに悪いことに、他のキーの値や存在に応じて、プログラムが実際に多数のキーを原子的に更新(追加/削除/変更)する機能を必要とする場合があります。これはさらに困難であり、単一のスレッドを備えた単一のコンピュータでそれを正しく行う方法について、膨大な原稿が書かれています。
重要なのは、RAFTは上記を単独で保証するものではありません。せいぜいそれは、複数のマシンが1つのバージョンの履歴に同意するようにする方法です。
ダーティリード、ミッシングライト、データメイヘム
コンセンサスが得られたシナリオを想像して、書き込みが成功したと報告します。すぐに値を読み戻そうとします。今度は別のノードからそれを読み返すと、古い値で応答します。そのノードがまだ同期していない理由。
2つのプログラムが2つの別々のノードに値を書き込む別のシナリオ。両方のノードが競合し、過半数が書き込みに同意するようにします。 1つは失敗します。 1つのプログラムが書き込みを再試行し、他のプログラムは別のキーに書き込みます。ただし、本物の歴史は1つしかないため、どちらも過半数を獲得するために競争します。これは、1つのノード(およびそのプログラム)を更新ループから効果的にロックすることができます。
さらに別のシナリオ、同じキーのリーダーと2人のライター。作家は彼らの価値を書きました。リーダーはキーを読み取りますが、リーダーにはどの値が表示されますか。古い値ですか、それとも2つの新しい値の1つですか?状況によって異なりますが、現在はレースです。質問されるノードごとに異なる答えがある場合があります。
業界ソリューション
真剣にこれらは克服するべき単純な問題ではありません。多数のビッグネームの分散データベースでは、特に複製されたマスターを使用しない限り、これらのシナリオが発生することが明示されています。書き込みはマスターでシリアルにのみ実行でき、レプリカは既知の履歴バージョンのみを提供します。読者は古いデータを読んでいることを認識しています。また、書き込みでは、書き込みを有効にするために変更してはならないデータを具体的に示します。
他の解決策があると言われる前に...はい、存在します。正しく実装するのは簡単ですか?いいえ。大きな名前でも誤解します、頻繁に。
トレードオフ
RAFTは保持してください。一貫した状態にするための良いアイデアです。また、適切に実装するのもかなり簡単で、回復と縮退の特性が優れています。
今最も重要なことは何ですか?
全員が同じページにいることが重要な場合は、他のすべてのノードから見られたとマシンが認識している値のみを提供します。ログが新しい値を知っている場合でも。
代わりに、読み取りが1回のみ一貫している必要がある場合は、最新の多数決済みのログエントリよりも新しい時点をユーザーに選択してもらいます。それ以降またはそれ以前の回答のみを提供します。
ダーティリードが問題ない場合は、常に最新かつ最高のものを提供してください。
書き込みを失わないことが重要な場合は、選択された単一のノードを介してすべての書き込みを集めます。同様に、トランザクションはこの書き込みノードで処理する必要があります。
トランザクションが計算の点でかなり重い場合、読み取りと書き込みの数。次に、すべての読み取りが一貫している時間を固定し、読み取った内容と境界を記録します(つまり、キーが存在しない、値が存在しないかnullでした)。そのログ+選択されたトランザクションホストへの書き込みを出荷します(またはその選択されたホストになります)。ライターの仕事は、世界がまだ問題ないように見えることを確認し、何か変更があった場合にトランザクション全体が拒否される場合に書き込みを実行することです。
書き込みが失われても問題ない場合は、コミットされたことを確認する必要さえありません。ラフトログを更新し、コンセンサスが正しい履歴を決定できるようにします。書き込みの成功を追跡することが重要だった場合、ラフトログエントリが多数派によって承認された場合は成功し、ラフトログエントリが別の履歴のために破棄された場合は失敗します。
代わりに、これをKey/Value
ストアにしようとせず、代わりにKey/Time/Writer/Value
ストアにして、クライアントに大騒ぎをさせてください。負担を他の人に移すことほど簡単なことはありません-それは単にニースではありません。
クライアントの視点
クライアントコードは、この群のノードがさまざまな理由で不整合になる可能性があることを認識する必要があります。これは、読み取り/書き込みの観点から、各ノードの品質についてある程度の感覚を意味します。それらのほとんどは、古いクエリに正確に答えることができますが、最新のものにアクセスできるのはごくわずかであり、おそらくさらに少ない数の執筆者に相談する必要があります。
単純な指標は、アクセス可能な各ノードで半定期的にチェックインし、最後の100ログエントリのハッシュを要求することです。それらを比較してください。ほとんどのノードが同じハッシュを持つ場合、それらはほとんど一致しており、ノードは安定しています。 1つのノードに共通点がない場合は、非同期になっています。多くのノードのバランスが崩れている場合は問題があります。信頼しないでください。
テスト
このシステム設定を複数のノードでテストする必要があります。このシステムを信頼性の高いデータストアとして信頼できるようにするには、トランザクションの保証、ダーティリード、書き込みの欠落(例を挙げると)を確認しながら、低速接続、分割ネットワーク、および回復に対処する必要があります。
詳細
データベースのアーキテクチャとエンジンの設計に関する乾いた教科書を真剣に見つけてください。他の人たちは80年以上にわたってこれらの問題に取り組んできました。知識を見つけてください。解決策がすぐにはわからなくても、少なくとも問題は何かがわかります。
通常、完全に分離されたほとんどの「まったく分散されていない」データベースを用意する方がはるかに簡単です。
キー/値ストアに対してこれを行うには、キーを使用してハッシュ値を計算してから、「server_number = hash%number_of_servers」を実行します。この場合、コンセンサスを完全に無視できます(たとえば、データの現在のバージョンは常に特定のサーバー上にあり、他の場所には決してありません)。
サーバーの数が変化している場合、サーバーは煩雑になります(ただし、これに対応するために適応を追加できます)。クライアントがサーバートポロジを認識していない場合は、 "プレサーバー"を追加して、要求を転送します(遅延コストが増加します)。パフォーマンス上の理由からフォールトトレランスまたは並列処理が必要な場合(たとえば、多くのクライアントが同時に多くのサーバーから同じデータを要求する場合)、この方法はすぐに悪夢になります。
CAP定理 を調べていない場合は、ここから始めてください。あなたが提案する解決策は、いずれかのノードが他の大多数と通信できない場合、更新を処理できないことを意味します。あなたがそれで大丈夫なら、それは大丈夫でなければなりません。なぜデザインで整然とした更新が重要なのかを詳しく説明するのは良い考えでしょう。