- Azure Blog > Azure Cosmos DB partitioning design patterns – Part 1 (2018/10/25)
Rafat Sarosh (Principal Program Manager, Azure Cosmos DB)
この記事では、データを効率的に分散し、アプリケーションのパフォーマンスを改善し、より高速なルックアップを可能にするための、パーティション キーの使い方を学びます。この記事の前提条件は、Azure Cosmos DBの一般知識と、変更フィード、要求ユニット (RU)、Azure Functionsの十分な理解です。
高いスループットで挿入したいデータを持っており、2つ以上の異なるキーでクエリすることを想像してみましょう。このシナリオでは、あなたが航空会社に勤めており、ユーザーの予約情報をコレクションに格納する必要がある、と仮定します。ユーザー データは、次のように定義されます。
{ UserId: user@email.com, FirstName: John, LastName: Doe, PNR: 2345423, CityOfOrigin: Seattle, CityOfDestination: London, DOB: 12.2.76, 他の詳細… }
あなたは、パーティション キーとして、多くの可能な値から「UserId」(ユーザーの電子メール アドレス) を選択します。「UserId」は各ユーザーに固有のものであり、これによってデータが十分に分散されるようになるので、これはパーティション キーとして良い選択です。図1に示したように、データは、すべてのパーティションにわたって均等に分散されます。しかし、データをクエリする際に、常に「UserId」が分かっているわけではありません。ユーザーの姓やユーザーのPNR (旅客予約記録) を使って、データをクエリしたい場合もあります。
図1:パーティションにわたって均等に分散されたデータ
Azure Cosmos DBは、既定ですべてのデータにインデックスを作成します。「LastName」でデータをクエリしようとする場合、結果を得られますが、パーティション キーのないクエリはファンアウト クエリになるので、より多くの要求ユニット (RU/s) がかかります。ファンアウト クエリはすべてのパーティションを確認します。これには余分なRU/sがかかり、アプリケーションのパフォーマンスに影響を与えることがあります。データが少なく、パーティション数も少ない場合は、ファンアウト クエリの大幅な副作用に気づかないことがありますが、多数のパーティション、大量のデータを持つようになり始めると、ファンアウト クエリがアプリケーションに弊害をもたらすことがあります。頻度の低いクロス パーティション クエリは問題ありませんが、それが頻度の高いクエリの場合、解決策は何でしょうか?
1つの選択肢は、「PNR」から「UserId」へのマッピングのための「PNR」コレクション、「LastName」から「UserId」へのマッピングのための「LastName」コレクションという、2つのルックアップ コレクションを持つことです。「PNR」コレクションは、パーティション キー、行キーとして「PNR」を持ち、値として「UserId」を持ちます。
これらの異なるルックアップ コレクションは、アプリケーションをより効率的にします。PNRで詳細をルックアップするには、まず、「UserId」を取得するために、「PNR」コレクションをクエリします。それから、「UserId」を使って、「User」コレクションをクエリします。これら2回の呼び出しは数ミリ秒以内に完了でき、単一のファンアウト クエリより少ないRU/sを消費します。ほとんどのポイント ルックアップ クエリは、1-2ミリ秒以内に完了できます。2回のルックアップであっても、ほとんどのクエリは10ミリ秒以内に完了できます。
呼び出しに余計な数ミリ秒を追加したくなく、代わりに「PNR」コレクション、「LastName」コレクションでデータを複製することにするかもしれません。これは高速なルックアップを可能にしますが、データの更新時に複雑さとコストが加わることがあるので、これは推奨されません。結局は、要件、パフォーマンス、複雑さのバランスを取らなければなりません。多くの場合、単純な解決策から始めることが最高のアプローチです。
ここで、異なるコレクションにおけるデータの分散について見てみましょう。たとえば「LastName」コレクションを見てみると、姓が「Smith」の人は姓が「Zubrkee」の人より多いので、データは均等に分散されないことが分かります。この場合、データは図2のようになります。
図2:パーティションにわたって不均等に分散されたデータ
このシナリオでは、データは不均等に分散されており、満杯のパーティションもあれば、十分に使われていないパーティションもあります。
- コレクション全体のRU/sは、すべてのパーティションにわたって分割されます。これは、1,000 RU/sが5つのパーティションにわたって分散され、各パーティションが200 RU/sを持つことを意味しています。これらのパーティションのいずれかに200 RU/s以上の呼び出しを行おうした場合、閾値を超えるので、呼び出しが失敗し始めます。開発者が、コレクション レベルで1,000 RU/sを割り当てられているにもかかわらず、200 RU/sでスロットルされていることに気付いた場合は大抵、問題は、悪いパーティション キーのために、1つのパーティションだけにアクセスしていることです。
-
現在、パーティションは、最大10 GBのデータを持つことができます (今後、変更される可能性があります)。このため、すべてのパーティションを効率的に満たすパーティション キーを使うことが、重要になります。「LastName」の例では、データを均等に分散するためには、より粒度の細かいパーティション キーが必要です。データが「CityOfOrigin」も含んでいるので、「LastName」と「CityOfOrigin」から新しいパーティション キーを作ることができます。結果は、次の図3のようになります。
図3:粒度の細かいパーティション キーを適用した後のデータ分散
これは、ずっと良いように見えます。データがより均等に分散しており、旅行者は、自分の姓と出発地を入力するだけで、簡単かつ迅速に自分の予約をルックアップできます。
データを均等に分散したので、次は、どのようにして他のコレクションにデータを入力するのでしょうか? このためには、変更フィードを理解する必要があります。変更フィードは、コレクション内で起こっている内部変更のすべてを公開します。Azure Cosmos DBにおける変更フィードのサポートは、あらゆる変更のためにAzure Cosmos DBコレクションをリッスンすることで、動作します。変更フィードは、変更されたドキュメントの、変更された順序でソート済みのリストを出力します。変更を永続化して、非同期で漸進的に処理できます。並列処理のために、出力を1つ以上のコンシューマーにわたって分散できます。
ドキュメント コレクション内の各パーティション キー範囲に対して、変更フィードを利用可能なので、並列処理のために、1つ以上のコンシューマーにわたって変更フィードを分散できます。「User」コレクションにレコードが挿入されるたびに、そのレコードが変更フィードに現れます。変更フィードを利用する最も簡単な方法は、Azure Functionsです。Azure Functionsは、インフラストラクチャを明示的にプロビジョニング、管理する必要なしにオンデマンドでコードを実行できる、サーバーレス コンピューティング サービスです。Azure Functionsを使って、多様なイベントに応じてスクリプトやコードを実行します。
Azure Functionsを通して変更フィードを利用すると、挿入/変更されたすべてのドキュメントが、関数のパラメーターとして自分の関数に届きます。
public static async Task Run(IReadOnlyList<Document> input, TraceWriter log)
自分の関数でドキュメント全体を入手したら、それに応じて「PNR」コレクション、「LastName」コレクションを更新できます。
Azure Cosmos DB、Azure Functions、変更フィードの使い方についてさらに学ぶには、このスクリーンキャストを見てください。または、変更フィードについて読んでください。
4 thoughts on “Azure Cosmos DB パーティション分割デザイン パターン – パート1 (Azure Cosmos DB partitioning design patterns – Part 1)”