CAP 理論と MongoDB の一貫性と可用性に関するいくつかの考え

文章

  

  私がNoSqlに出会ったのは5~6年ほど前で、当時すでに話題になっていました。でも当時はmysqlしか覚えていなくて、Nosqlはまだ私にとっては目新しいもので、あまり使っていなかったけど、ただ知らなかっただけなんです。しかし、私が感銘を受けたのは、次のような写真でした(後でGoogleで調べたところ、その写真はここからのものであることがわかりました)。

  

    この図は、データベース (従来のリレーショナル データベースや NOSQL を含む) と CAP 理論の関係についてのものです。NoSql には実務経験がなく、深い理解もないため、CAP 理論については多少の理解があります。したがって、あるデータベースがなぜどの陣営に分かれているのかは不明です。

    私は仕事の後、MongoDB をよく使用するので、ある程度の理解はしています。この写真を少し前に見たので、MongoDB が本当に CP 陣営に属しているのか、またその理由を知りたいと思っています。この質問の本来の意図は、MongoDB の古典的な (公式に推奨されている) デプロイメント アーキテクチャがレプリカ セットを使用し、レプリカ セットが冗長性と自動フェイルオーバーを通じて高可用性 (可用性) を提供するためではないかと思われます。では、なぜ MongoDB は上の図で可用性を犠牲にするのでしょうか。 ? ? そして、MongoDB の公式ドキュメントで「CAP」を検索しましたが、何も見つかりませんでした。そこで私はこの質問を自分で考えて、自分自身に答えを出したいと思いました。

  この記事では、まず CAP 理論とは何か、および CAP 理論に関するいくつかの記事を明確にしてから、MongoDB の一貫性と可用性の間の妥協点とトレードオフについて説明します。

CAP理論

CAP理論については、私はこの3つの言葉の意味しか知りませんし、その説明もネット上の記事から引用したものなので、正確ではないかもしれません。したがって、まず第一に、この理論の起源と正確な説明を見つけるために情報源に戻る必要があります。出発点としてはwikipedia が最適だと思います。そこからより正確な紹介を見ることができ、さらに重要なことに、CAP 理論の出典や開発と進化のプロセスなど、多くの有用なリンクを見ることができます。

  CAP 理論は、分散データ ストレージでは、整合性 (C、Consistency)、可用性 (A、Availability)、およびパーティション トレランス (P、Partition Tolerance) のうち最大 2 つだけを同時に満たすことができることを意味します。

  一貫性とは、各読み取り操作で、最後に書き込まれたデータを読み取ることができるか、エラーが発生するかのいずれかを意味します。

  可用性とは、各リクエストに対してタイムリーでエラーのない応答を取得できることを意味しますが、リクエストの結果が最新の書き込まれたデータに基づいているという保証はありません。

  パーティション フォールト トレランスとは、ノード間のネットワークの問題により、一部のメッセージがパケット間で発生したり遅延したりした場合でも、システム全体がサービスの提供 (一貫性または可用性の提供) を継続できることを意味します。

  一貫性と可用性は両方とも非常に広範な用語であり、異なるセマンティック環境における異なるものを指します。たとえば、Brewer は記事「cap-twelve-years-later-how-the-rules-have-changed」全員が同じ文脈にいることが明らかな場合にのみ、議論は有意義なものとなります。

    

  分散システムの場合、ネットワーク分断(ネットワーク分断)の状況は避けられず、ノード間のデータレプリケーションに遅延が発生する必要がありますが、整合性(すべての読み取り要求に対して最新の書き込みデータが読み取れる)を確保する必要がある場合には、つまり、可用性が犠牲になり、その逆も同様です。

  Wikipedia の説明によると、CAP 間の関係は 1998 年に始まりました。ブリューワーは 2000 年のPODC (分散コンピューティングの原理に関するシンポジウム

  

CAP理論の起源

 CAP 理論の創始者である Brewer 氏は、「  堅牢な分散システムに向けて」の中で、分散システムではコンピューティングは比較的簡単で、本当の難しさは状態の維持であると指摘しました。さらに、分散ストレージやデータ共有システムの場合、データの一貫性を保証することも困難です。従来のリレーショナル データベースでは、可用性よりも一貫性が優先されるため、トランザクションの ACID 機能が提案されています。多くの分散ストレージ システムでは、一貫性よりも可用性が重要であり、一貫性は BASE (基本的に利用可能、ソフト状態、結果的一貫性) によって保証されます。次の図は、ACID と BASE の違いを示しています。

  

  つまり、BASE は結果整合性を通じてサービスの可用性を確保しようとします。写真の最後の文「But I think it's a spectrum」に注目してください。これは、ACID BASE が程度の問題であり、両極端ではないことを意味します。

  

  2002 年に、Brewer の予想と一貫性があり、可用性があり、パーティション耐性のある Web サービスの実現可能性について、2 人の著者は非同期ネットワーク モデルを通じて CAP 予想を実証し、Brewer の予想を理論 (定理) にアップグレードしました。しかし、正直に言うと、私は記事をあまり読んでいませんでした。

  

  2009 年の記事brewers-cap-theoremで、著者は比較的簡単な証明を示しました。

  

  上図に示すように、2 つのノード N1 と N2 には同じデータ V が格納されており、現在の状態は V0 です。安全で信頼性の高い書き込みアルゴリズム A がノード N1 で実行され、同じ信頼性の高い読み取りアルゴリズム B がノード N2 で実行されます。つまり、N1 ノードが書き込み操作を担当し、N2 ノードが読み取り操作を担当します。N1 ノードによって書き込まれたデータも自動的に N2 に同期され、同期されたメッセージは M と呼ばれます。N1 と N2 の間にパーティションがある場合、メッセージ M が一定期間内に N2 に到達するという保証はありません。

  これらの問題をビジネスの観点から見ると

  

   α このトランザクションは、オペレーション α1 と α2 で構成されます。α1 はデータの書き込み、α2 はデータの読み取りです。単一点であれば、α1 が書き込んだデータを α2 が読み取れることを確保するのは簡単です。分散状況の場合、α2 の発生時間を制御できない限り、α1 によって書き込まれたデータを α2 が読み取れるという保証はありませんが、何らかの制御 (ブロッキング、データ集中化など) を行うとパーティションが破壊されます。耐障害性、またはユーザビリティの喪失。

  さらに、ここの記事では、多くの場合、一貫性よりも可用性の方が重要であると指摘しており、たとえば、Facebook や Google などの Web サイトの場合、短期間の利用不能は多大な損失をもたらします。

  

この記事brewers-cap-theorem-on-distributed-systems/ (  2010 年) では3 つの例を使用して CAP を説明しています、例 1: 単一の mysql、例 2: 2 つの mysql、ただし異なる mysql には異なるデータ サブセットが格納されます (シャーディングと同様)、例 3: 2 つの mysql (A に対する挿入操作) は、操作が完了したとみなされる前に、B に対して正常に実行される必要があります (レプリカ セットと同様)。著者は、example1 と example2 の両方で強整合性は保証できるが、可用性は保証できないと考えています。example3 では、パーティションが存在するため、一貫性と可用性の間のトレードオフが必要です。

  私の考えでは、CAP 理論は「分散ストレージ システム」を前提として議論するのが最善であると考えています。可用性とは、サービス全体の可用性を意味するのではなく、分散システム内の特定のサブノードの可用性を意味します。したがって、上記の例はあまり適切ではないように感じます。

CAP理論の開発

    2012 年、CAP 理論の発明者であるブリューワーは、CAP 理論に関する別の記事「CAP Twelve Years Later: How the "Rules" Have Changed」を執筆しました。『CAP Theory Twelve Years Review: The "Rules" Have Changed』の中国語訳もインターネット上にあり、翻訳も悪くありません。

  記事の要点は、CAP理論は3者が2者を選択する必要がないという意味ではないということだ。まず第一に、分散システムである限りパーティションが存在する可能性はありますが、パーティションが発生する可能性は非常に小さいため (そうでない場合はネットワークまたはハードウェアを最適化する必要があります)、CAP ではほとんどの場合、完璧な C と A が許可されます。パーティションが存在する場合のみ 期間中、CとAのバランスをとる必要があります。次に、一貫性と可用性はどちらも程度の問題であり、0 か 1 ではありません。可用性は 0% から 100% まで連続的に変化します。一貫性は多くのレベルに分割できます (たとえば、casandra では、一貫性レベルを設定できます)したがって、現代の CAP 実践の目標は、特定のアプリケーションの合理的な制限内でデータの一貫性と可用性の効果を最大化することです。

  記事はまた、パーティションは相対的な概念であり、あらかじめ定められた通信制限時間を超えた場合、つまりシステムが制限時間内にデータの整合性を達成できない場合、それはパーティションが発生したことを意味し、現在の操作を続行する必要があると指摘した。 CとAの間で選択してください。

  収益目標と契約規制に関しては、システムの可用性が主な目標であるため、システムの可用性を最適化するために、キャッシュまたは事後更新ログを定期的に使用しています。したがって、設計者が可用性を選択するのは、パーティションの終了後に壊れた不変性契約を復元する必要があるためです。

  実際には、ほとんどのグループは、データ センター内にパーティションは存在しない (単一の場所にある) ため、単一のデータ センター内で CA を選択できると考えています。CAP 理論が出現する前、システムはデフォルトでこの設計アイデアを採用していました。従来のデータベースも含まれます。

  パーティショニング中、ノードの独立した自己一貫性のあるサブセットは操作を実行し続けることができますが、グローバル スコープの不変制約に違反しないという保証はありません。データ シャーディングはこの例です。設計者は事前にデータを異なるパーティション ノードに分割します。パーティション化中は、単一のデータ シャードが動作し続ける可能性があります。逆に、パーティション化された状態が本質的に密接に関連している場合、または保存する必要があるグローバル不変制約がある場合は、よくてもパーティションの片側のみを操作でき、最悪の場合は操作をまったく実行できません。

  上記の抜粋の下の行の選択部分は、MongoDB のシャーディングの状況と非常によく似ています。MongoDB のシャード クラスター モードでは、通常の状況では、シャードは相互に通信する必要はありません。

  13 年の論文「better-explaining-cap-theorem」の中で、著者は「それは実際には A 対 C にすぎない!」と指摘しました。

  (1) 可用性は通常、異なるマシン間のデータ複製によって実現されます。

  (2) 整合性を確保するには、読み取り操作を許可する間に複数のノードを同時に更新する必要があります。

  (3) 一時的な分断、つまり拠点間の通信遅延が発生する可能性があり、この時は A と C のトレードオフが必要となります。ただし、トレードオフを考慮する必要があるのは、パーティショニングが発生する場合のみです。

  分散システムでは、ネットワークの分断が必ず発生するため、「実際には A 対 C だけです!」

MongoDB と CAP        

「シャードクラスターを段階的に作成して MongoDB を理解する」では、MongoDB の高パフォーマンス、高可用性、スケーラビリティ (水平スケーリング) などの特徴が紹介されています。自動フェイルオーバーを備えたレプリカ セット。MongoDB データベースの使用には、スタンドアロン、レプリカ セット、共有クラスターの 3 つのモードがあり、共有クラスターの構築プロセスについては、前回の記事で詳しく紹介しました。

  スタンドアロンは単一の mongod であり、アプリケーションはこの mongod に直接接続されていますが、この場合、パーティション耐性はまったくなく、強い一貫性が必要です。シャードクラスターの場合、各シャードをレプリカセットにすることも推奨されます。MongoDB のシャードは独立したデータ サブセットを維持するため、シャード間のパーティションはほとんど影響を与えません (チャンクの移行プロセス中に影響が残る可能性があります)。そのため、主に考慮すべき点は、シャード内のレプリカ セットのパーティションへの影響です。したがって、この記事では、MongoDB のレプリカ セットも対象として、MongoDB の一貫性と可用性について説明します。

  レプリカ セットには、書き込みリクエストと読み取りリクエストを受け入れるプライマリ ノードが 1 つだけあり、他のセカンダリ ノードは読み取りリクエストを受け入れます。これは単一書き込み、複数読み取りの状況であり、複数読み取り、複数書き込みの状況よりもはるかに単純です。後で説明する目的で、レプリカ セットは 3 つのベース ポイント (1 つのプライマリ、2 つのセカンダリ) で構成され、すべてのノードがデータを永続化する (データ保持) ことも想定されます。

  MongoDB の一貫性と可用性の間のトレードオフは、書き込み重視、読み取り重視、読み取り優先の 3 つの要素によって決まります。MongoDB3.2 版では read-concerns が導入されたため、以下では主に MongoDB3.2 版の状況を説明します。

書き込み上の懸念:

  書き込み懸念は、どのような状況で MongoDB が書き込み操作に対してクライアントに応答するかを示します。次の 3 つのフィールドが含まれます。

  { w: <値>、j: <ブール値>、wtimeout: <数値> }

  w: 書き込みリクエストが値 MongoDB インスタンスによって処理された後にクライアントに返されることを示します。範囲:

    1: デフォルト値。データがスタンドアロンまたはレプリカ セットのプライマリの MongoDB に書き込まれて返されることを示します。

    0: 書き込みを行わずに直接クライアントに返すため、パフォーマンスは高くなりますが、データが失われる可能性があります。ただし、 j: True とともに使用すると、データの耐久性 (耐久性) を高めることができます。

    >1: レプリカ セット環境でのみ有効です。値がレプリカ セット内のノード数より大きい場合、ブロッキングが発生する可能性があります。

    'majority': データがレプリカ セット内の大半のノードに書き込まれると、データはクライアントに返されます。この場合、通常は読み取りを考慮して使用されます。

    書き込み操作が w: "majority" クライアントに確認応答を返した後、クライアントは 「多数」の readConcernを使用してその書き込みの結果を読み取ることができます。

  j: 書き込みリクエストがジャーナルに書き込まれた後にクライアントに返されることを示します。デフォルトは False です。注意すべき点は 2 つあります。

    ジャーナリングを有効にしていない MongoDB インスタンスに j:True が使用されている場合、エラーが報告されます。

    MongoDB3.2 以降では、w>1 の場合、返す前にすべてのインスタンスをジャーナルに書き込む必要があります。

  wtimeout: 書き込みタイムアウト期間を示します。つまり、指定された時間 (数値) でクライアントに返せない場合 (w が 1 より大きい場合)、エラーが返されます。

    デフォルトは 0 で、このオプションを設定しないのと同じです。

  MongoDB3.4 では、writeConcernMajorityJournalDefaultが追加され、このようなオプションにより、組み合わせによって w、j が異なります。

  

読み取り参照:

上で  説明したように、レプリカ セットはプライマリと複数のセカンダリで構成されます。プライマリは書き込み操作を受け入れるため、データは最新である必要があり、セカンダリは oplog を通じて書き込み操作を同期するため、データには一定の遅延が生じます。適時性をあまり重視しないクエリ サービスの場合は、セカンダリ ノードからクエリを実行して、クラスタへの負荷を軽減できます。

  

  MongoDB は、さまざまな状況でさまざまな読み取り参照を選択することが非常に柔軟であると指摘しました。MongoDB ドライバーは、いくつかの読み取り参照をサポートしています。

  プライマリ: デフォルト モード。すべての読み取り操作はレプリカ セットのプライマリ ノードにルーティングされます。

  PrimaryPreferred: 通常はプライマリ ノードにルーティングされ、プライマリ ノードが使用できない場合 (フェールオーバー) のみ、セカンダリ ノードにルーティングされます。

  セカンダリ: すべての読み取り操作はレプリカ セットのセカンダリ ノードにルーティングされます。

  SecondaryPreferred: 通常はセカンダリ ノードにルーティングされ、セカンダリ ノードが利用できない場合にのみプライマリ ノードにルーティングされます。

  最も近い: プライマリまたはセカンダリに関係なく、最小の遅延でノードからデータを読み取ります。分散アプリケーションと MongoDB が複数のデータ センターにデプロイされている場合、最も近いデータ センターが最適なデータの局所性を保証できます。

  Secondary または SecondaryPreferred を使用する場合は、次の点に注意する必要があります。

  (1) 遅延により、読み取られたデータが最新ではない可能性があり、別のセカンダリから返されるデータが異なる可能性があります。

  (2) デフォルトでバランサーが有効になっているシャード コレクションの場合、チャンクの移行がまだ終了していないか異常終了しているため、セカンダリは欠落しているデータまたは冗長なデータを返す可能性があります。

  (3) 複数のセカンダリ ノードがある場合、どのセカンダリ ノードを選択するか? 簡単に言えば、「最も近い」、つまり平均遅延が最も小さいノードです。具体的には、サーバ選択アルゴリズムに参加します 

読み取りに関する懸念事項:

  読み取り懸念は、MongoDB3.2 で追加された新機能で、レプリカ セット (シャード クラスター内のレプリカ セットを使用するシャードを含む) に対して返されるデータの種類を示します。ストレージ エンジンが異なれば、読み取り懸念に対するサポートも異なります。

  読み取り懸念には次の 3 つのレベルがあります。

  local: デフォルト値、現在のノードの最新データを返します。現在のノードは読み取り参照に依存します。

  Majority:過半数のノードへの書き込みが確認された最新のデータを返します。このオプションを使用するには、次の条件が必要です: WiredTiger ストレージ エンジン、選択 プロトコル バージョン 1を使用、MongoDB インスタンスの起動時に指定 --enableMajorityReadConcern选项。

  Linearizable: バージョン 3.4 で導入されましたが、ここでは省略します。興味のある読者はドキュメントを参照してください。

  記事の中にこんな一文があります。

読み取り懸念レベルに関係なく  、ノード上の最新のデータは、システム内のデータの最新バージョンを反映していない可能性があります。

  つまり、Read Concern:Majority を使用しても、返されるデータが必ずしも最新のデータであるとは限らず、NWR の理論と同じではありません。根本的な原因は、最終的に返される値が 1 つの MongoDB ノードからのみ取得され、このノードの選択が読み取り参照に依存することです。

この記事  では、readconcern 導入の意義と実装について詳しく紹介されており、ここでは核心部分のみを引用します。

readConcern 本来の目的は、「ダーティ リード」問題を解決することです。たとえば、ユーザーが MongoDB のプライマリから特定のデータを読み取りますが、このデータはほとんどのノードと同期されていないため、プライマリが失敗します。回復後、プライマリ ノードは、ほとんどのノードと同期していないデータをロールバックするため、ユーザーは「ダーティ データ」を読み取ることになります。

readConcern レベルが多数として指定されている場合、ユーザーによって読み取られたデータが「ほとんどのノードに書き込まれている」ことが保証され、そのようなデータは確実にロールバックされず、ダーティ リードの問題が回避されます。

 一貫性か可用性か?

  CAP 理論における一貫性の可用性の問題を確認します。
  一貫性とは、各読み取り操作で、最後に書き込まれたデータを読み取ることができるか、エラーが発生することを意味します。
  可用性とは、各リクエストに対してタイムリーでエラーのない応答を取得できることを意味しますが、リクエストの結果が最新の書き込まれたデータに基づいているという保証はありません。

  前述したように、この記事での一貫した可用性の説明はレプリカ セットに基づいており、共有クラスターであるかどうかは影響しません。また、これはクライアントが 1 人の場合での議論ですが、クライアントが複数の場合は CAP 理論の範疇ではなく、分離の問題となるようです。書き込み懸念、読み取り懸念、および読み取り参照の理解に基づいて、次の結論を導き出すことができます。

  • デフォルト (w: 1、readconcern: local) では、読み取り設定がプライマリの場合、強い整合性を保って最新のデータを読み取ることができますが、この時点でプライマリが失敗すると、この時点でエラーが返されます。可用性は保証できません
  • デフォルト(w:1、readconcern:local)で、読み取り優先がセカンダリ(secondaryPreferred、primaryPreferred)の場合、古いデータが読み込まれる可能性がありますが、すぐにデータを取得でき、使い勝手が良くなります。
  • writeconcern: 書き込まれたデータがロールバックされないことを大部分が保証、readconcern: 読み取られたデータがロールバックされないことを大部分が保証
  • (w:1,readconcern;多数決)の場合はプライマリから読み取っても最新のデータが返ってくる保証はないので弱い整合性
  • if (w: Majority, readcocern:majority)、プライマリから読み取られる場合は、最新のデータを読み取る必要があり、このデータはロールバックされませんが、現時点では書き込み可用性が低いため、読み取られた場合は、セカンダリ Read からの最新データの読み取りは保証できず、一貫性が弱い。


  振り返ってみると、MongoDB で言及されている高可用性は、より一般的な意味での可用性です。物理的な障害が発生した場合でも、データ レプリケーションと自動フェイルオーバーを通じてクラスター全体が短時間で回復し、動作を継続できることは言うまでもありません。回復は自動的に行われます。この意味では、確かに可用性が高いといえます。

おすすめ

転載: blog.csdn.net/weixin_45925028/article/details/132234801