本記事は、Microsoft 社員である Andrei Zaichikov さんが投稿されている Azure Cosmos DB Design Patterns 記事の日本語訳です。
※日本語翻訳の実施については、著者への事前承諾を取得済みです。
※元の記事は、マイクロソフト社の公式記事ではありません。ブログ著者による個人的見解によるものです。
Picture. Self-Moving sawmill. Rodion Zaichikov
Azure Cosmos DB デザインパターン
免責事項。すべての意見や推奨事項は私自身のものです。言葉だけで信じないでください - テストして、あなたのケースに合わせてください。本番環境に適用する前に考えてください 😉
すべてはユースケースに依存します。 --Captain Obvious1
こんにちは。
本日は、Cosmos DB を使用してソリューションを構築するための、重要で効果的ないくつかのアプローチ(または設計パターン)についてお話します。これらの方法は本質的に構造的であり、Cosmos DB ワークロードを最適化するために他のサービスとツールを使用しています。
一般的には、私は成熟前の最適化はあまり好きではありません。また、随所でパターンを見るためいくつかの悲惨な試みを行った後(そのときに私が読んでいた本を推測してみてください)、私はパターンには慎重になっています。
しかしながら、私は過去 8 年間に、Azure Cosmos DB、Mongo DB、Cassandra、そしていくつかの他のものを含む、さまざまなテクノロジーを使用した NoSQL プロジェクトでこれらのテクニックを収集していました。これらのパターンは主に、既存の Cosmos DB の最適化やベストプラクティスを全て使用して、すでに最適に近い結果を出している方に役立つと思います。
ですが Cosmos DB で何かを構築することのみを計画している場合でも、アプリケーションを設計および構築する際にこれらのアプローチを念頭に置いて、後で使用できるようにすることは理にかなっているでしょう。
繰り返しになりますが、始める前に、マイクロソフトの推奨事項はすべて引き続き有効であり、良好な結果を達成できることを強調したいと思います。
- 目的のプロファイリング
- キャッシング
- ストリーミング
- データオフロード
- 一時的な構造を使用して大規模なイベントを処理する
- コアデザインブロックとしてのデータ移行スクリプト
- テスト/開発環境の設計
- データ移行エンジン
結論から言うと、「なぜ Cosmos DB を使うのか?」という疑問に対する回答を簡単に説明します。
はじめる前に
「高度な」最適化のケースと戦略に進む前に、マイクロソフトが提供するベストプラクティスのいくつかを要約します。
- Cosmos DB ワークロードを設計する際に従うべきいくつかの非常に優れたプラクティスは、こちらのビデオにまとめられています。
- いくつかの追加の推奨事項(コレクションとアプリケーションのレベルで適用されるべきもの):
- コストをさらに最適化するために使用できる Cosmos DB のいくつかの機能:
ということで、これらの資料に目を通してみて、かなり複雑なケースであることがわかったのであれば、さっそく始めてみましょう。
目的のためのプロファイリング
多くの場合、ワークロードは特定の目的を果たしています。まあ、理想的には、そうします … とにかく。
この絵に映っているメカニズムは、自動で動くチェーンソーです(クレジットは息子の Rodion、LinkedIn には未掲載)。
ポイントは、この図式を使用して、安定した、予測可能な、効率的なデータライフサイクルを整理するために利用できることです。
例えば、ゲームやその他の B2C アプリケーションを持っているとします。我々は、通常、グループに編成されている異なる顧客にサービスを提供しています。無課金の顧客、標準の顧客、プレミアムの顧客などです。
明らかに、これらの顧客は似たようなデータモデルを持っていますが、データの量が非常に異なり、このデータを処理する方法(異なるサービス)と異なる SLA を持っています。単一のコレクションの場合、どのように見えるでしょうか?
すべてのお客様は単一のコレクションに保存されます。私たちの目標が世界中のプレミアム顧客の待ち時間を減らすことであると仮定しましょう。私たちは何をしますか?私たちはマルチマスタ2を使用しています。少し待ってください、これは単一のコレクション(つまり、1 セットのコレクション)だけなので、プレミアムか無課金かに関係なく、すべてのユーザーの消費に対して料金を支払う必要がありますよね?はい、良い面は、世界中のすべてのお客様のレイテンシが削減されたことであり、無料利用者のお客様がそれに気づき、プレミアムサブスクリプションを購入することになります 😉
季節が過ぎ、コレクションは次第に大きくなっていきます。そこにはすべてが混在しています。どうやら、それがゲームである場合、私たちのプレミアムユーザーは、標準および無課金ユーザーよりも、戦利品、アイテム、履歴が多くなります。ただし、すべてのユーザーが同じパーティション構造からサービスを受けるため、1 つのコレクション内で異なる階層のユーザーの消費パターンが重複することで、いくつかの負の影響を見ることになります。
これを簡単にしましょう。私はプレミアムユーザーで、インベントリに 10,000 個の剣を集めました。そして、私はそれらを一度に検索して、私の給料の大半がどこに行ったかを妻に示したいと思います。
私はおそらくデータが保存されているパーティションのかなりの数の RU を消費するでしょう(すべてのパーティションには特定の RU 制限があることに注意してください)。同じことが、マーケティングイベントなどのために、膨大な数の無料利用枠のユーザーがデータベースを叩いている場合にも当てはまります。仕掛けは、それらのアクセスパターンは非常に異なる(少数のアイテム、小さな要求)ことですが、スロットリングや待ち時間が長くなる可能性のあるプレミアムユーザーと重なります。
さらに、古くなったユーザーを TTL アウトしないと、古いユーザーがそこに保存されているだけで、大規模なマーケティング活動が発生した場合にソリューション全体のパフォーマンスを大幅に低下させてしまう可能性があります。その理由は、RU は Cosmos DB によってすべてのパーティションに均等に分散されるためです。その結果、大量の古いデータがパーティションの数に影響を与えてしまいます。
したがって、より適切なシナリオは、固有の使用パターンに基づいて、さまざまな層の個別の構造(コレクション、コレクションのセット)をプロビジョニングおよびプロファイルすることです。さらに、下の図に示すように、顧客を異なる階層間で移動するためのデータライフサイクル(たとえば、移行スクリプトに基づく)を構築することができます。
このようなソリューションは複雑に見えますが、同じコレクション内のさまざまな使用パターンに対処する技術的な複雑さを克服できるだけでなく、コストとサービスレベルのコレクションをプロファイルすることもできます。プレミアムユーザーにはサービスが提供され、無課金や非アクティブのユーザーに対する費用は抑制されたままであることを保証することができます。
時間のプロファイリング
目的のためのプロファイリングの特殊なケースとして、時間のプロファイリングがあります。時間のプロファイリングのアプローチの 1 つは、たとえば、「リアルタイム」データ(5 〜 10 分間隔のデータ)、日別および月別のデータを個別に収集し、変更フィードを使用してデータのライフサイクルを有効にすることです。
あるいは、期間が満了すると(たとえば、毎日)、新しいコレクションが動的に追加されるケースがよく見られます。このアプローチにより、読み取り関連の RU を削減しながら、書き込み用の RU の数を一定に保つことができます。これにより、必要な場合にのみ読み取りをスケーリングできます。また、後でクエリすることができ、アクセス頻度の低いデータを Cosmos DB 分析ストレージにオフロードすることもできるため、コールドデータ用の追加サービスを使用するためにアプリケーションを適合させる必要はありません。
キャッシング
NoSQL PaaS の世界では、顧客はリクエストごとに料金を支払います。また、特定の状況では、リクエストは繰り返し読み取りに使用されます。たとえば、Cosmos DB コレクションに格納されている辞書(ゲーム内のアイテムなど)があるとします。
したがって、これらのアイテムを Cosmos DB コレクションから直接クエリするだけの場合は、リクエストごとに料金を支払います。したがって、前に説明したゲームで 10,000 個の剣に関する情報を抽出すると、(最悪のシナリオでは)時間をかけて 10,000 RU を消費する可能性があります。同じリクエストが数千のユーザーによって使用される場合、このコレクションの RU が急上昇します。
私の顧客の一人は、シングルユーザー向けの認証に 32 のリクエストを使用しており、AppFormsTemplate コレクションに対して 30 のリクエストが行われていました。この場合、認証処理には非常にコストがかかります。
ご想像のとおり、同じ機能を低コストで実現するためのより良い方法があります。
同じ情報を取得するために使用される RU の数を減らすために、さまざまなキャッシングツールと戦略を調べることができます。 通常、キャッシュアサイドアプローチは、キャッシュ自体に使用されるツールに関係なく、デフォルトのオプションです。 主な理由は、Cosmos DB が高速なルックアップを提供するのに十分なパフォーマンスを備えていることと、キャッシュを使用する主な理由は、RU コストを削減することです。
特定のケースでは、キャッシュの事前入力も有効です(特に、特定のデータが使用される場合)。
ストリーミング
場合によっては、読み取りと書き込みは非常に速いペースで到着し、すぐにキャプチャする必要があります。良い例の 1 つは、ゲームやデバイスのテレメトリです。 ここでは、デバイスのダイナミクスを確認し、特定の時間ウィンドウに1つのドキュメントとして保存することができます。
デバイスごとに毎分 100 バイトを取得し、それらを 20 分間隔で集約するとします。Cosmos DB にデータを直接取り込む場合、リクエストパターンは次のようになります:
- 単一のリクエストによる 100 バイトのドキュメントの最初の取り込み – 20 書き込み x 5 RU x 3 間隔 = 1 デバイスあたり 1 時間あたり 300 RU。
- ドキュメントを読んで集約し、プッシュバックする必要があります。そのために、変更フィードまたは API を直接使用できます。このケースに直接 API 読み取りアプローチを適用することは、最も理にかなっています。読み取りの場合(個別の読み取りの場合)、20 回の読み取り x 1 RU (デフォルトのセッション整合性レベルの場合) x 3 の間隔 = 1 デバイスあたり 1 時間あたり 60 RU。
- 集計を行ったら、5 〜 10 RU をさらに消費する Cosmos DB に集計ドキュメントを書き戻す必要があります。
別の方法としては、Cosmos DB の前に Event Hub を配置し、データを Cosmos DB に取り込む前に、Azure Functions または Azure Stream Analytics を使用して、いくつかの集計または高速分析を行うことができます。これにより、Cosmos DB からデータを読み取って書き戻すことができなくなり、最終的には RU を最適化できるようになります。
データオフロード
最新の NoSQL データベースのほとんどが、従来のリレーショナルデータベースが苦労していた特定のパターンの 1 つに対処するために作成されたことは自明のことです。このパターンは大規模な参照です。たとえば、1 人のユーザー(のショッピングカートを例として)に対して 1 つのドキュメントを取得し、物理的に可能な限り速く実行する必要がある場合です。これが、NoSQL を採用した最初の実用的な理由の 1 つでした。現在、NoSQL データベースは多くの点で非常に優れていますが、通常、特定のワークロードのプロファイルに頼っています。
Cosmos DB でも同じ – 参照に関しては非常に効率的であり、事前に集約されたデータを提供し、マルチリージョンでデプロイを行うのに非常に効率的です(これは DIY ソリューションでは非常に複雑です)。しかし、同時に、制限の全くない WHERE 句、大規模な時系列分析の実行などに関しては、それほど効率的ではありません。
かなり長い間、データをオフロードする唯一の手段は、Cosmos DB 変更フィードを使用した独自実装でした。それは非常に効率的で、個人的には素晴らしく、非常に堅牢な技術の一部であると感じましたが、独自実装は誰もが賛成するようなものではありません。
この問題に対処するために、マイクロソフトは、データを Azure Synapse Analytics にオフロードして、大規模な読み取りで前例のない機能を活用できる新しい Synapse Link 機能を導入しました。これにより、Cosmos DB での複数の大規模なクエリ実行を回避し、サービスの実行コストを大幅に削減することもできます。
データのオフロードと時系列モデルの操作に最適なもう 1 つのサービスは、Azure Data Explorer です。このサービスは、(取り込みエンジンとして使用している場合) Event Hub と直接統合するか、カスタムコードを使用して変更フィードを介し Cosmos DB と統合することができます。
データオフロードにより、次のことが可能になります:
- Cosmos DB デプロイメントの実行率を下げる
- 限られた WHERE 句を使用した高速な参照とクエリによる利益を獲得する
- たとえば、次のような専用エンジン(集約、結合、複雑なクエリ)のフルスケールの読み取り機能をお楽しみください。Azure Synapse Analytics と Azure Data Explorer
- たとえば Azure Synapse Analytics と Azure Data Explorer のような、特化したエンジンのフルスケールの読み取り機能(アグリゲーション、ジョイン、複雑なクエリ)を楽しむことができる
次の図にすべてのアプローチを示します。
一時的な構造を使用した大規模イベント (LSE) の処理
あなたが、Microsoft Store、Steam、AppStore、その他のプラットフォームでのプロモーションキャンペーン、または新しい積極的なマーケティングキャンペーンを開始する e コマースアプリの準備をしているゲーム会社であるとします。私たちは皆、通常よりもはるかに多くの容量を必要とする大規模なイベントを想像できます。
バックエンド側でこのようなイベントに対処するための単純なアプローチは、関連する Cosmos DB の RU を高い数にスケールアップして、増加する需要に対処することです。複数の状況でこのようなアプローチが完全に機能する場合がありますが、スケーリングで 10 倍以上の容量増加が必要な場合、イベント終了後にパフォーマンスの低下に直面する可能性があります。
つまり、その理由は、RU がパーティション間で均等に分散され、すべてのパーティションが最大 10,000 RU を維持できるためです。例えば、20,000 RU が設定されており、パーティションあたりのパフォーマンスが 10K であれば問題ありません。ここでは、2 個のパーティションを 100 個にスケールアップして、1,000,000 RU までスケールアップする必要があるとします。イベントが終了すると、特にパーティション戦略が完全にランダム化/均等に分散されたドキュメントに対応できるように設計されていない場合、パフォーマンスに重大な問題が発生する可能性があります。パーティションごとに一定のパフォーマンスを維持するために多くの RU を保持するか、新しいコレクションに移行するかのどちらかになります。
もちろん、問題が発生したときに対処するオプションはあります 😉 このようなアプローチでは、主なツールは監視スクリプトとデータ移行スクリプトになります(後ほど説明します)。
より良いアプローチは、一時的なテーブルを使用して LSE に対処することです。
大規模なイベントを処理するために一時テーブルを使用するという考えは、かなり単純です。実際のイベントの前に必要な量の RU をコレクションにプロビジョニングし、待機中にスケールダウンし、コードが有名なエラー 429 ("Too many requests") に直面したらすぐに少しずつスケールアップします。
イベントから一時的なコレクションにオフロードするすべてのトラフィックは、「辞書」コレクションを再利用する可能性があります。実際、イベントが 24 時間しか続かない場合、LSE に対処するための準備/保護のためには、最大24時間以上の時間がかかります。LSE が終わるとすぐに、顧客独自のスクリプトや変更フィード(オフロード時間とイベントの時間に依存します)を使用して、LSE コレクションからメインコレクションに対して、後続の処理には全くと言っていいほど影響を与えずに、非常に費用対効果の高い方法でデータを移動させることができます。
テスト/開発環境の設計
いくつかのプロジェクトで、主に辞書と見なされる数百のデータの小さなコレクションを含むソリューションに遭遇しました。それらはキャッシュされていましたが、同時に非常に高価なものでした。なぜか?主な理由は、これらのコレクションがコンテナベースのモデルでプロビジョニングされていたため、すべてのコレクションには少なくとも 400 の RU が割り当てられていたからです(実際にはそれぞれ約 1,000 の RU が割り当てられていました)。辞書の数と掛け算すると、お札が膨大になります。
ここで興味深いのは、これらのコレクションが積極的に使用されておらず(開発環境)、開発チームは陳腐なコレクションごとに 1 秒あたり 200 回(1000 RU)の書き込みにお金を払っていたことです。
良い点は、軽減策が非常に単純であることです - この場合、データベースをプロビジョニングされたモデルに移行することで問題は解決しました。
本番環境では、データベースのプロビジョニングされたコレクション(Cosmos DB 世界でいうコンテナー)は、独自の RU でプロビジョニングされたものとは異なる動作いをすることに注意してください。本番環境に移行する前に、本番環境に設定されたコンテナー上でソリューション(特に再試行とエラー処理)をテストすることを強くお勧めします。
コアデザインブロックの 1 つとしてのデータ移行スクリプト
厳密に言えば、NoSQLデータベースでのデータ移行はかなり頻繁に発生します。これには複数の理由があり、主な理由は、最高のパフォーマンス結果を達成する/パフォーマンスの問題を修正するためにデータの再配置を必要となることがあげられます。
1 つのコレクションにさまざまなものが大量にある場合や、シャード・キー3が間違っている場合や、データの場所を変更したい場合があります。Cosmos DB にそのようなスクリプトがあると、他の NoSQL データベースでも直面する可能性のある、同じ問題に対処できるため、非常に有益です。たとえば、パーティションキーを変更する必要がある場合や、パーティションを大規模にマージする必要がある場合、コレクションを分割したりする必要がある場合に備え、別のコンテナーに移行することができるようになります。
通常、これらのスクリプトはゼロダウンタイムの移行で実装する必要があります。実用的なソリューションを提供している顧客の一部は、次の方法でそれらを実装しています:
-
アプリケーションは、ターゲット(シンク)コンテナーからの読み取りを試みます。
a. シンクからの読み取りが失敗した場合、アプリケーションはクエリをソースコンテナーに再ルーティングします。また、移行するソースコンテナー内のアイテムをマークします。ここではエラー処理が重要です。
-
アプリケーションは常にデータをシンクコンテナーに書き込みます。
-
アイテムがマークされるとすぐに、そのアイテムは Cosmos DB 変更フィードにプッシュされます。カスタム移行スクリプト (Cosmos DB 変更フィード処理ライブラリ に支えられた専用アプリケーションまたは Azure Functions) が、変更フィードからアイテムを取得し、シンクコンテナーに書き込みます。また、ソースコレクション内のアイテムを移行済みとしてマークしているようで、最悪の場合、ここでいくつかの競合(コンフリクト)の解決対応が発生する可能性があります。
-
クリーンアップスクリプトは、移行済みとしてマークされたアイテムをシンクコンテナーから削除する責任があります。
-
移行が完了するとすぐに、ソースコレクションとアプリケーションエラー処理のルーティング情報を削除できます。
結論から言うと
この記事では、Cosmos DB 最適化のためのいくつかの複雑なアプローチについて説明しました。そして、これらの議論の後、あなたは有効な質問をしたくなるかもしれません: 「なぜ Cosmos DB が必要なのですか?」
そして、いくつかの答えがあります(もちろん、すべてはユースケースに依存します 😉):
- まず、Azure エコシステムには、大規模な検索のための優れたツールはありません。買い物かご、リーダーボード、セッション、デバイスの状態など、文字通り何でも構いません。グローバルフットプリントを追加することで、世界的に非常に低いレイテンシを顧客に提供するソリューションが得られます。
- 第 2 に、Cosmos DB は半構造化データを格納および配布するための優れたツールです。他にもいくつかのサービスがありますが、どれも他の Azure サービスとのネイティブな統合を行っていません。Azure Functions、Jupiter Notebooks、ダウンストリーミング用の変更フィード、Azure Synapse Link - そのほか何でもありです。
- 3 番目 - Cosmos DB はシンプルであることです - Cosmos DB を正しく設計し、目的に合わせて使用すれば、非常に大規模で複雑なタスクを解決するための効率的で非常に有用なツールになります。
- さらには、すべてのもの(または現在のところほとんどすべて)をスクリプト化してコードとして扱うことができるので、人生を面白くしてくれます 😉
経験則を覚えておいてください - すべてのリクエストにはコストがかかります。全てのデータベースにです。唯一の違いは、Cosmos DB が明示的にコストを設定していることです。それは十分に公平です。
Cosmos DB を使って楽しんでください!
翻訳について
筆者による翻訳内容について、何か訂正などございましたら、編集リクエストをお願いいたします。