DocumentDB-パーティショニング
データベースが10GBを超えて大きくなり始めたら、新しいコレクションを作成し、データをますます多くのコレクションに分散または分割するだけでスケールアウトできます。
遅かれ早かれ、10GBの容量を持つ単一のコレクションでは、データベースを含めるのに十分ではありません。10GBはそれほど多くないように聞こえるかもしれませんが、JSONドキュメントを保存していることを忘れないでください。これは単なるプレーンテキストであり、インデックスのストレージオーバーヘッドを考慮しても、多くのプレーンテキストドキュメントを10GBに収めることができます。
スケーラビリティに関しては、ストレージだけが問題ではありません。コレクションで利用可能な最大スループットは、S3コレクションで得られる1秒あたり2.5千リクエストユニットです。したがって、より高いスループットが必要な場合は、複数のコレクションでパーティション化してスケールアウトする必要もあります。スケールアウトパーティショニングは、horizontal partitioning。
AzureDocumentDBを使用してデータをパーティション分割するために使用できるアプローチは多数あります。以下は最も一般的な戦略です-
- スピルオーバーパーティショニング
- 範囲分割
- ルックアップパーティショニング
- ハッシュ分割
スピルオーバーパーティショニング
パーティションキーがないため、波及分割は最も単純な戦略です。多くのことについて確信が持てない場合は、最初から始めることをお勧めします。単一のコレクションを超えてスケールアウトする必要があるかどうか、追加する必要のあるコレクションの数、またはそれらを追加する必要がある速度がわからない場合があります。
波及効果のパーティション分割は単一のコレクションから始まり、パーティションキーはありません。
コレクションは増加し始め、その後、10 GBの制限に近づき始めるまで、さらに増加します。
容量が90%に達すると、新しいコレクションに波及し、新しいドキュメントに使用し始めます。
データベースがより多くのコレクションにスケールアウトしたら、パーティションキーに基づく戦略に移行することをお勧めします。
その場合、移行先の戦略に基づいてドキュメントを別のコレクションに移動することにより、データのバランスを取り直す必要があります。
範囲分割
最も一般的な戦略の1つは、範囲の分割です。このアプローチでは、ドキュメントのパーティションキーが含まれる可能性のある値の範囲を決定し、その範囲に対応するコレクションにドキュメントを送信します。
日付は、定義された日付の範囲内にあるドキュメントを保持するコレクションを作成するこの戦略で非常に一般的に使用されます。十分に小さい範囲を定義する場合、コレクションが10GBの制限を超えることはないと確信できます。たとえば、1つのコレクションで1か月全体のドキュメントを合理的に処理できるシナリオがあります。
また、ほとんどのユーザーが現在のデータ(今月またはおそらく先月のデータ)を照会している場合もありますが、ユーザーがはるかに古いデータを検索することはめったにありません。したがって、6月にS3コレクションから始めます。これは、購入できる最も高価なコレクションであり、最高のスループットを実現します。
7月に別のS3コレクションを購入して7月のデータを保存し、6月のデータをより安価なS2コレクションにスケールダウンします。次に、8月に別のS3コレクションを取得し、7月をS2に、6月をS1にスケールダウンします。毎月、現在のデータを常に高スループットで利用できるようにし、古いデータを低スループットで利用できるようにします。
クエリがパーティションキーを提供する限り、クエリが必要なコレクションのみがクエリされ、スピルオーバーパーティションの場合のようにデータベース内のすべてのコレクションがクエリされるわけではありません。
ルックアップパーティショニング
ルックアップパーティションを使用すると、パーティションキーに基づいてドキュメントを特定のコレクションにルーティングするパーティションマップを定義できます。たとえば、地域ごとに分割できます。
すべての米国のドキュメントを1つのコレクションに、すべてのヨーロッパのドキュメントを別のコレクションに、他の地域のすべてのドキュメントを3番目のコレクションに保存します。
このパーティションマップを使用すると、ルックアップパーティションリゾルバーは、各ドキュメントに含まれる領域プロパティであるパーティションキーに基づいて、ドキュメントを作成するコレクションとクエリするコレクションを特定できます。
ハッシュ分割
ハッシュパーティショニングでは、ハッシュ関数の値に基づいてパーティションが割り当てられるため、リクエストとデータを複数のパーティションに均等に分散できます。
これは通常、多数の個別のクライアントから生成または消費されるデータを分割するために使用され、ユーザープロファイル、カタログアイテムなどを保存するのに役立ちます。
.NETSDKが提供するRangePartitionResolverを使用した範囲分割の簡単な例を見てみましょう。
Step 1−新しいDocumentClientを作成し、CreateCollectionsタスクで2つのコレクションを作成します。1つにはAからMで始まるユーザーIDを持つユーザーのドキュメントが含まれ、もう1つにはユーザーIDNからZのドキュメントが含まれます。
private static async Task CreateCollections(DocumentClient client) {
await client.CreateDocumentCollectionAsync(“dbs/myfirstdb”, new DocumentCollection {
Id = “CollectionAM” });
await client.CreateDocumentCollectionAsync(“dbs/myfirstdb”, new DocumentCollection {
Id = “CollectionNZ” });
}
Step 2 −データベースの範囲リゾルバを登録します。
Step 3−パーティションキーのデータ型である新しいRangePartitionResolver <string>を作成します。コンストラクターは、パーティションキーのプロパティ名と、シャードマップまたはパーティションマップであるディクショナリの2つのパラメーターを取ります。これは、リゾルバー用に事前定義している範囲と対応するコレクションのリストです。
private static void RegisterRangeResolver(DocumentClient client) {
//Note: \uffff is the largest UTF8 value, so M\ufff includes all strings that start with M.
var resolver = new RangePartitionResolver<string>(
"userId", new Dictionary<Range<string>, string>() {
{ new Range<string>("A", "M\uffff"), "dbs/myfirstdb/colls/CollectionAM" },
{ new Range<string>("N", "Z\uffff"), "dbs/myfirstdb/colls/CollectionNZ" },
});
client.PartitionResolvers["dbs/myfirstdb"] = resolver;
}
ここでは、可能な限り最大のUTF-8値をエンコードする必要があります。そうでなければ、最初の範囲は、単一のMを除くどのMにも一致せず、同様に2番目の範囲のZにも一致しません。したがって、ここでは、このエンコードされた値を、パーティションキーで照合するためのワイルドカードと考えることができます。
Step 4−リゾルバーを作成したら、現在のDocumentClientを使用してデータベースに登録します。これを行うには、PartitionResolverのディクショナリプロパティに割り当てるだけです。
通常のようにコレクションではなく、データベースに対してドキュメントを作成してクエリを実行します。リゾルバーはこのマップを使用して、リクエストを適切なコレクションにルーティングします。
それでは、いくつかのドキュメントを作成しましょう。最初にuserIdKirk用に1つ作成し、次にSpock用に1つ作成します。
private static async Task CreateDocumentsAcrossPartitions(DocumentClient client) {
Console.WriteLine();
Console.WriteLine("**** Create Documents Across Partitions ****");
var kirkDocument = await client.CreateDocumentAsync("dbs/myfirstdb", new { userId =
"Kirk", title = "Captain" });
Console.WriteLine("Document 1: {0}", kirkDocument.Resource.SelfLink);
var spockDocument = await client.CreateDocumentAsync("dbs/myfirstdb", new { userId =
"Spock", title = "Science Officer" });
Console.WriteLine("Document 2: {0}", spockDocument.Resource.SelfLink);
}
ここでの最初のパラメーターは、特定のコレクションではなく、データベースへの自己リンクです。これはパーティションリゾルバーなしでは不可能ですが、パーティションリゾルバーを使用するとシームレスに機能します。
両方のドキュメントはデータベースmyfirstdbに保存されましたが、RangePartitionResolverが正しく機能している場合、KirkはAからMのコレクションに保存され、SpockはNからZのコレクションに保存されていることがわかります。
次のコードに示すように、CreateDocumentClientタスクからこれらを呼び出しましょう。
private static async Task CreateDocumentClient() {
// Create a new instance of the DocumentClient
using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey)) {
await CreateCollections(client);
RegisterRangeResolver(client);
await CreateDocumentsAcrossPartitions(client);
}
}
上記のコードを実行すると、次の出力が表示されます。
**** Create Documents Across Partitions ****
Document 1: dbs/Ic8LAA==/colls/Ic8LAO2DxAA=/docs/Ic8LAO2DxAABAAAAAAAAAA==/
Document 2: dbs/Ic8LAA==/colls/Ic8LAP12QAE=/docs/Ic8LAP12QAEBAAAAAAAAAA==/
ご覧のとおり、2つのドキュメントの自己リンクは、2つの別々のコレクションに存在するため、異なるリソースIDを持っています。