CRDTベースのデータベースを使用する場合

Roshan Kumarは、RedisLabsのシニアプロダクトマネージャーです。

CAP定理で説明されているように一貫性と可用性を曲げることは、地理的に分散したアプリケーションのアーキテクトにとって大きな課題でした。ネットワークパーティションは避けられません。データセンター間の待ち時間が長いと、常にデータセンター間の切断が短期間発生します。したがって、地理分散アプリケーションの従来のアーキテクチャは、データの整合性を放棄するか、可用性に打撃を与えるように設計されています。

残念ながら、インタラクティブなユーザーアプリケーションの可用性を犠牲にする余裕はありません。最近では、アーキテクトは整合性を確認し、結果整合性モデルを採用しています。このモデルでは、アプリケーションはデータベース管理システムに依存して、データのすべてのローカルコピーをマージし、結果整合性を実現します。

データの競合が発生するまで、結果整合性モデルではすべてが良好に見えます。いくつかの最終的な整合性モデルは、競合を修正するための最善の努力を約束しますが、強力な整合性を保証するには不十分です。幸いなことに、競合のないレプリケートされたデータ型(CRDT)を中心に構築されたモデルは、強力な結果整合性を提供します。

CRDTは、事前に決定された一連の競合解決ルールとセマンティクスを通じて、強力な結果整合性を実現します。CRDTベースのデータベース上に構築されたアプリケーションは、競合解決のセマンティクスに対応するように設計する必要があります。この記事では、CRDTベースのデータベースを使用して、地理的に分散したアプリケーションを設計、開発、およびテストする方法について説明します。また、カウンター、分散キャッシング、共有セッション、マルチリージョンデータの取り込みという4つのサンプルユースケースについても検討します。

私の雇用主であるRedisLabsは最近、Redis EnterpriseでのCRDTサポートを発表しました。競合のない複製データタイプが、文字列、ハッシュ、リスト、セット、並べ替えられたセット、ビットフィールド、地理、ハイパーログログ、ストリームなどの豊富なデータ構造ポートフォリオに加わりました。私たちのデータベース製品。ただし、以下の説明は、Redis Enterpriseだけでなく、すべてのCRDTベースのデータベースにも当てはまります。

地理的に分散したアプリケーションのデータベース

地理的に分散したアプリケーションの場合、クライアントに対してローカルでサービスを実行するのが一般的です。これにより、ネットワークトラフィックとラウンドトリップによって発生する遅延が減少します。多くの場合、アーキテクトはローカルデータベースに接続するサービスを設計します。次に、すべてのデータベースで一貫したデータをどのように維持するかという問題が発生します。1つのオプションは、これをアプリケーションレベルで処理することです。つまり、すべてのデータベースを同期する定期的なジョブプロセスを作成できます。または、データベース間でデータを同期するデータベースに依存することもできます。

この記事の残りの部分では、2番目のオプションであるデータベースに作業を任せることを前提としています。下の図1に示すように、地理分散アプリケーションは複数のリージョンでサービスを実行し、各サービスはローカルデータベースに接続します。基盤となるデータベース管理システムは、リージョン全体に展開されたデータベース間でデータを同期します。

Redisラボ

データ整合性モデル

整合性モデルは、分散データベースとアプリケーションの間の契約であり、書き込み操作と読み取り操作の間のデータのクリーン度を定義します。

たとえば、強力な整合性モデルでは、データベースは、アプリケーションが常に最後の書き込みを読み取ることを保証します。逐次一貫性により、データベースは、読み取ったデータの順序がデータベースに書き込まれた順序と一致することを保証します。結果整合性モデルでは、分散データベースは、バックグラウンドでデータベースレプリカ間でデータを同期および統合することを約束します。したがって、あるデータベースレプリカにデータを書き込み、別のデータベースレプリカからデータを読み取る場合、データの最新のコピーを読み取れない可能性があります。

強い一貫性

2フェーズコミットは、強力な一貫性を実現するための一般的な手法です。ここで、ローカルデータベースノードでのすべての書き込み操作(追加、更新、削除)について、データベースノードはすべてのデータベースノードに変更を伝播し、すべてのノードが確認応答するのを待ちます。次に、ローカルノードはすべてのノードにコミットを送信し、別の確認応答を待ちます。アプリケーションは、2回目のコミット後にのみデータを読み取ることができます。ネットワークがデータベース間で切断されると、分散データベースは書き込み操作に使用できなくなります。

結果整合性

結果整合性モデルの主な利点は、分散データベースレプリカ間のネットワーク接続が切断された場合でも、データベースを使用して書き込み操作を実行できることです。一般に、このモデルは2フェーズコミットによって発生するラウンドトリップ時間を回避するため、他のモデルよりもはるかに多くの1秒あたりの書き込み操作をサポートします。結果整合性が対処しなければならない問題の1つは、競合です。つまり、2つの異なる場所にある同じアイテムへの同時書き込みです。競合を回避または解決する方法に基づいて、結果整合性のあるデータベースはさらに次のカテゴリに分類されます。

  1. 最後のライターが勝ちます(LWW)。 この戦略では、分散データベースはサーバー間のタイムスタンプ同期に依存しています。データベースは、データ自体とともに各書き込み操作のタイムスタンプを交換します。競合が発生した場合は、最新のタイムスタンプを使用した書き込み操作が優先されます。

    この手法の欠点は、すべてのシステムクロックが同期されていることを前提としていることです。実際には、すべてのシステムクロックを同期することは困難で費用がかかります。

  2. クォーラムベースの結果整合性:この手法は、2フェーズコミットに似ています。ただし、ローカルデータベースは、すべてのデータベースからの確認応答を待機しません。大多数のデータベースからの確認応答を待つだけです。大多数からの承認により、定足数が確立されます。競合が発生した場合、クォーラムを確立した書き込み操作が優先されます。

    反対に、この手法は書き込み操作にネットワークレイテンシを追加し、アプリのスケーラビリティを低下させます。また、ローカルデータベースがトポロジ内の他のデータベースレプリカから分離されている場合、ローカルデータベースは書き込みに使用できません。

  3. マージレプリケーション:リレーショナルデータベースで一般的なこの従来のアプローチでは、集中型マージエージェントがすべてのデータをマージします。この方法は、競合を解決するための独自のルールを実装する際にもある程度の柔軟性を提供します。

    マージレプリケーションは遅すぎて、リアルタイムの魅力的なアプリケーションをサポートできません。また、単一障害点もあります。このメソッドは、競合解決のための事前設定されたルールをサポートしていないため、競合解決のためのバグのある実装につながることがよくあります。

  4. 競合のないレプリケートされたデータ型(CRDT): CRDTについては、次のいくつかのセクションで詳しく学習します。一言で言えば、CRDTベースのデータベースは、競合のない結果整合性を提供するデータ型と操作をサポートします。CRDTベースのデータベースは、分散データベースのレプリカがデータを交換できない場合でも使用できます。これらは常に、読み取りおよび書き込み操作にローカルレイテンシーを提供します。

    制限?すべてのデータベースのユースケースがCRDTの恩恵を受けるわけではありません。また、CRDTベースのデータベースの競合解決セマンティクスは事前定義されており、オーバーライドすることはできません。

CRDTとは何ですか?

CRDTは、すべてのデータベースレプリカからのデータを収束する特別なデータ型です。人気のあるCRDTは、Gカウンター(成長のみのカウンター)、PNカウンター(正負のカウンター)、レジスター、Gセット(成長のみのセット)、2Pセット(2相セット)、ORセット(観測-削除セット)など。舞台裏では、データを収束するために次の数学的特性に依存しています。

  1. 可換性:a☆b = b☆a
  2. 結合法則:a☆(b☆c)=(a☆b)☆c
  3. べき等:  a☆a = a

Gカウンターは、操作をマージする操作CRDTの完璧な例です。ここで、a + b = b + aおよびa +(b + c)=(a + b)+ cです。レプリカは、更新(追加)のみを相互に交換します。CRDTは、更新を追加してマージします。たとえば、Gセットは、べき等({a、b、c} U {c} = {a、b、c})を適用してすべての要素をマージします。べき等性は、データ構造に追加された要素が異なるパスを介して移動および収束するときに、それらの要素の重複を回避します。

CRDTデータ型とその競合解決セマンティクス

競合のないデータ構造:Gカウンター、PNカウンター、Gセット

これらのデータ構造はすべて、設計上競合がありません。次の表は、データベースレプリカ間でデータがどのように同期されるかを示しています。

Redis Labs Redis Labs

GカウンターとPNカウンターは、グローバルポーリング、ストリームカウント、アクティビティ追跡などのユースケースで人気があります。Gセットは、ブロックチェーンテクノロジーを実装するために頻繁に使用されます。たとえば、ビットコインは追加専用のブロックチェーンエントリを採用しています。

レジスタ:文字列、ハッシュ

レジスターは本質的に競合がないわけではありません。これらは通常、LWWまたはクォーラムベースの競合解決のポリシーに従います。図4は、レジスタがLWWポリシーに従って競合を解決する方法の例を示しています。

Redisラボ

レジスタは主に、キャッシュとセッションデータ、ユーザープロファイル情報、製品カタログなどを格納するために使用されます。

2Pセット

2フェーズセットは、2セットのGセットを維持します。1つは追加されたアイテム用で、もう1つは削除されたアイテム用です。レプリカは、同期時にGセットの追加を交換します。両方のセットで同じ要素が見つかった場合、競合が発生します。Redis Enterpriseなどの一部のCRDTベースのデータベースでは、これは「追加が削除よりも優先される」というポリシーによって処理されます。

Redisラボ

2Pセットは、ショッピングカート、共有ドキュメント、スプレッドシートなどの共有セッションデータを格納するための優れたデータ構造です。

CRDTベースのデータベースを使用するようにアプリケーションを設計する方法

アプリケーションをCRDTベースのデータベースに接続することは、アプリケーションを他のデータベースに接続することと同じです。ただし、結果整合性ポリシーがあるため、アプリケーションは、一貫したユーザーエクスペリエンスを提供するために、特定のルールセットに従う必要があります。3つのキー: 

  1. アプリケーションをステートレスにします。ステートレスアプリケーションは通常、API駆動型です。 APIを呼び出すたびに、メッセージ全体が最初から再構築されます。これにより、いつでもデータのクリーンコピーを常にプルできます。 CRDTベースのデータベースによって提供される低いローカル遅延により、メッセージの再構築がより迅速かつ容易になります。 

  2. ユースケースに適した適切なCRDTを選択してください。カウンターは最も単純なCRDTです。グローバル投票、アクティブセッションの追跡、メータリングなどのユースケースに適用できます。ただし、分散オブジェクトの状態をマージする場合は、他のデータ構造も考慮する必要があります。たとえば、ユーザーが共有ドキュメントを編集できるようにするアプリケーションの場合、編集だけでなく、実行された順序も保持したい場合があります。その場合、編集内容をCRDTベースのリストまたはキューデータ構造に保存する方が、レジスターに保存するよりも優れたソリューションになります。また、CRDTによって適用される競合解決のセマンティクスを理解し、ソリューションがルールに準拠していることも重要です。
  3. CRDTは万能のソリューションではありません。CRDTは確かに多くのユースケースに最適なツールですが、すべてのユースケース(ACIDトランザクションなど)に最適であるとは限りません。CRDTベースのデータベースは、通常、マイクロサービスごとに専用のデータベースがあるマイクロサービスアーキテクチャに適しています。

ここでの主なポイントは、アプリケーションがロジックに焦点を合わせ、データ管理と同期の複雑さを基盤となるデータベースに委任する必要があるということです。

分散マルチマスターデータベースを使用したアプリケーションのテスト

より迅速な市場投入を実現するには、一貫した開発、テスト、ステージング、および本番環境のセットアップを行うことをお勧めします。特に、開発とテストのセットアップには、分散データベースの小型化されたモデルが必要です。CRDTベースのデータベースがDockerコンテナまたは仮想アプライアンスとして利用可能かどうかを確認します。データベースレプリカを異なるサブネットにデプロイして、接続および切断されたクラスターのセットアップをシミュレートできるようにします。

分散マルチマスターデータベースを使用したアプリケーションのテストは、複雑に聞こえるかもしれません。ただし、ほとんどの場合、テストするのは、分散データベースが接続されている場合と、データベース間にネットワークパーティションがある場合の2つの状況でのデータの整合性とアプリケーションの可用性だけです。

開発環境に3ノードの分散データベースをセットアップすることで、単体テストのほとんどのテストシナリオをカバー(さらには自動化)できます。アプリケーションをテストするための基本的なガイドラインは次のとおりです。