次の方法で共有


Azure Cosmos DB での階層パーティション キー

適用対象: NoSQL

Azure Cosmos DB では水平スケーリングをサポートするために、パーティション キーに基づいて論理パーティションと物理パーティション全体にデータを分散します。 階層パーティション キーまたは "サブパーティション分割" を使用すると、パーティション キーに対して最大 3 レベルの階層を構成して、データ分散をさらに最適化し、より高いレベルのスケーリングを実現できます。

現在、合成キーを使用しており、パーティション キーのデータが 20 GB を超えるシナリオがある場合、または各テナントのドキュメントが独自の論理パーティションにマップされることを保証したい場合は、サブパーティション分割が役立ちます。 この機能を使用する場合、論理パーティション キーのプレフィックスは 20 GB、および 1 秒あたり 10,000 要求ユニット (RU/秒) を超える可能性があります。 プレフィックスによるクエリは、データを保持するパーティションのサブセットに効率的にルーティングされます。

階層パーティション キーの選択

マルチテナント アプリケーションがあり、現在、テナントをパーティション キーで分離している場合は、階層パーティションが役立つ可能性があります。 階層パーティションを使用すると、20 GB の論理パーティション キーの制限を超えてスケーリングできます。各テナントのドキュメントを無限にスケーリングできることを保証したい場合は、これが適切なソリューションです。 現在のパーティション キーまたは単一のパーティション キーが頻繁に 20 GB に達する場合、ワークロードには階層パーティションが最適です。

ただし、ワークロードの性質と、最初のレベルのキーのカーディナリティに応じて、いくつかのトレードオフが発生する場合があります。これについては、階層パーティション シナリオのページで詳しく説明します。

各レベルの階層パーティション キーを選択する際は、次の一般的なパーティション分割の概念を念頭に置き、それぞれがワークロードに与える可能性のある影響を理解することが重要です。

  • すべてのコンテナーで、階層パーティション キーの完全なパスの各レベルが ("最初のレベル" から) 以下を満たす必要があります。

    • カーディナリティが高いこと。 階層パーティションの最初のキー、2 番目のキー、3 番目のキー (該当する場合) のすべてに広範囲の値を指定できる必要があります。

      • 階層パーティション キーの最初のレベルでカーディナリティが低い場合、インジェスト時のすべての書き込み操作は、50 GB に達して 2 つの物理パーティションに分割されるまで、1 つの物理パーティションのみに制限されます。 たとえば、最初のレベルのキーが TenantId 上にあり、かつ一意のテナントが 5 つしかないとします。 これらのテナントの各操作は 1 つの物理パーティションのみにスコープが設定され、スループットの消費量は、その 1 つの物理パーティション上のものだけに制限されます。 これは、階層パーティションでは、同じ最初のレベルのキーを持つすべてのドキュメントを最適化し、同じ物理パーティション上に併置して、完全なファンアウト クエリを回避するためです。
      • これは、すべてのテナントのデータを 1 回だけ取り込み、その後に続く操作が主に読み取り負荷の高いワークロードでは問題ないかもしれませんが、ビジネス要件に特定の時間内でのデータ取り込みが含まれるワークロードでは不向きになる場合があります。 たとえば、待機時間を回避するための厳密なビジネス要件がある場合、データ取り込みのワークロードが理論的に達成できる最大スループットは、物理パーティションの数 * 10,000 です。 最上位レベルのキーのカーディナリティが低い場合、分割 (完了までに 4 - 6 時間かかることがあります) した後にレベル 1 のキーを複数のパーティションに分散するのに十分なデータがない限り、物理パーティションの数は 1 になる可能性があります。
    • 要求ユニット (RU) の消費とデータ ストレージをすべての論理パーティションに均等に分散すること。 この分散により、物理パーティション全体でも RU の消費とストレージの分散が保証されます。

      • UserId のようにカーディナリティが高いと思われる最初のレベルのキーを選択し、しかし実際にはワークロードが 1 つの特定の UserId だけに対して操作を実行する場合、すべての操作が 1 つまたは少数の物理パーティションだけにスコープが設定されるため、ホット パーティションに陥る可能性があります。
  • 読み取り負荷の高いワークロード: クエリ内で頻繁に現れる階層パーティション キーを選択することをお勧めします。

    • たとえば、マルチテナント アプリケーションで特定のユーザー セッションをフィルター処理するためにクエリを頻繁に実行するワークロードでは、TenantIdUserIdSessionId の階層パーティション キーをその順序で利用できます。 フィルターの述語にパーティション キーを含めることで、クエリ呼び出しを関係する物理パーティションにだけ効率的にルーティングできます。 読み取り負荷の高いワークロードでのパーティション キー選択の詳細については、「パーティション分割の概要」に関するページを参照してください。
  • 書き込み負荷の高いワークロード: 階層パーティション キーの最初のレベルに、高いカーディナル値を使用することをお勧めします。 高いカーディナリティとは、最初のレベルのキー (および後続のレベルも) に少なくとも数千の一意の値があり、かつ物理パーティションの数よりも多い一意の値があることを意味します。

    • たとえば、複数のテナントをパーティション キーで分離する 1 つのワークロードがあり、他のテナントよりも書き込み負荷の高い大規模なテナントがいくつかあるとします。 現在、Azure Cosmos DB では、データが 20 GB を超える場合、パーティション キー値へのデータの取り込みは停止されます。 このワークロード内で、Microsoft と Contoso は大規模なテナントであり、他のテナントよりもはるかに速く増加することが予想されます。 これらのテナントのデータを取り込めなくなるリスクを回避するために、階層パーティション キーを使用すると、20 GB の制限を超えてこれらのテナントをスケーリングできます。 テナント全体のスケーラビリティを高めることを保証するために、UserId や SessionId などのレベルをさらに追加できます。

    • ワークロードが、同じ最初のレベルのキーを持つすべてのドキュメントの書き込みに対応できることを保証するには、項目 ID を 2 番目または 3 番目のレベルのキーとして使用することを検討してください。

    • 最初のレベルのカーディナリティが高くなく、現在パーティション キー上で 20 GB の論理パーティション制限に達している場合は、階層パーティション キーではなく合成パーティション キーを使用することをお勧めします。

ユース ケースの例

各テナントのユーザーのイベント情報を格納するマルチテナント シナリオがあるとします。 このイベント情報には、サインイン、クリックストリーム、支払いイベントなどのイベント発生が含まれる場合がありますが、これらに限定されるものではありません。

実際のシナリオでは、一部のテナントは数千人のユーザーを抱える大規模なものになりますが、他の多くのテナントは少数のユーザーを抱える小規模なものです。 /TenantId によるパーティション分割では、1 つの論理パーティションで Azure Cosmos DB の 20 GB のストレージ制限を超える可能性があります。 /UserId によるパーティション分割では、テナント上のすべてのクエリがクロスパーティションになります。 どちらの方法にも重大な欠点があります。

TenantIdUserId を組み合わせた合成パーティション キーを使用すると、アプリケーションが複雑になります。 さらに、テナントの合成パーティション キー クエリは、すべてのユーザーが事前にわかっていて指定されていない限り、クロスパーティションのままです。

ワークロードにほぼ同じワークロード パターンを持つテナントが複数ある場合は、階層パーティション キーが役立ちます。 階層パーティション キーを使用すると、最初に TenantId でパーティション分割し、次に UserId でパーティション分割できます。 TenantIdUserId の組み合わせによって生成されるパーティションが 20 GB を超えることが予想される場合は、さらに下位の別のレベル (SessionId など) にパーティション分割することもできます。 全体的な深さは 3 つのレベルを超えることはできません。 物理パーティションの記憶域が 50 GB を超えると、Azure Cosmos DB は物理パーティションを自動的に分割して、データの約半分が 1 つの物理パーティションに、半分がもう 1 つの物理パーティションに格納されるようにします。 実質的に、サブパーティション分割とは、単一の TenantId 値が 20 GB のデータを超えることができ、TenantId のデータが複数の物理パーティションにまたがる可能性があることを意味します。

TenantId または TenantIdUserId の両方を指定するクエリは、関連するデータを含む物理パーティションのサブセットにのみ効率的にルーティングされます。 完全またはプレフィックスのサブパーティション分割されたパーティション キー パスを指定することで、完全なファンアウト クエリを効率的に回避できます。 たとえば、コンテナーに 1000 個の物理パーティションがあり、特定の TenantId 値がそのうちの 5 つにしかない場合、クエリは少数の関連する物理パーティションにのみルーティングされます。

階層内で項目 ID を使用する

コンテナーに、有効な値が広範囲にわたるプロパティがある場合は、そのプロパティは階層の最後のレベルのパーティション キーとして最適な選択肢となりえます。 この種類のプロパティの 1 つの考えられる例が項目 ID です。 "項目 ID" のシステム プロパティは、コンテナー内のすべての項目に存在します。 項目 ID を別のレベルとして追加すると、20 GB という論理パーティション キーの制限を超えるスケーリングが確実に可能となります。 キーの最初のレベル、または最初のレベルと 2 番目のレベルでこの制限を超えてスケーリングできます。

たとえば、TenantIdUserId でパーティション分割されたマルチテナント ワークロード用のコンテナーがあるとします。 TenantIdUserId の の組み合わせだけでは 20 GB を超える可能性がある場合は、3 つのレベルのキーを使用してパーティション分割することをお勧めします。ここで、3 番目のレベルのキーはカーディナリティが高いものを使用します。 このシナリオの一例は、3 番目のレベルのキーが、必然的にカーディナリティが高い GUID である場合です。 TenantIdUserId、および GUID の組み合わせが 20 GB を超える可能性は低いので、TenantIdUserId の組み合わせを実質的に 20 GB を超えてスケーリングすることができます。

項目 ID をパーティション キーとして使用する方法の詳細については、「パーティション分割の概要」に関するページを参照してください。

はじめに

重要

階層パーティション キーを使うコンテナーの操作は、次の SDK バージョンでのみサポートされています。 階層パーティション キーを持つ新しいコンテナーを作成し、データに対して作成、読み取り、更新、削除 (CRUD) の操作、またはクエリ操作を実行するには、サポートされている SDK を使用する必要があります。 現在サポートされていない SDK またはコネクタを使用する場合は、コミュニティ フォーラムで要求を提出してください。

サポートされている各 SDK の最新のプレビュー バージョンを確認してください。

SDK サポートされているバージョン パッケージ マネージャーのリンク
.NET SDK v3 >= 3.33.0 https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.33.0/
Java SDK v4 >= 4.42.0 https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/cosmos/azure-cosmos/CHANGELOG.md#4420-2023-03-17/
JavaScript SDK v4 4.0.0 https://www.npmjs.com/package/@azure/cosmos/
Python SDK >= 4.6.0 https://pypi.org/project/azure-cosmos/4.6.0/

階層パーティション キーを使用してコンテナーを作成する

まず、最大 3 レベルまでのサブパーティション分割のキー パスの事前定義されたリストを使用して、新しいコンテナーを作成します。

次のいずれかのオプションを使用して、新しいコンテナーを作成します。

  • Azure portal
  • SDK
  • Azure Resource Manager テンプレート
  • Cosmos DB エミュレーター

Azure portal

コンテナーを作成して階層パーティション キーを指定する最も簡単な方法は、Azure portal を使用することです。

  1. Azure portal にサインインします。

  2. 既存の Azure Cosmos DB for NoSQL アカウント ページに移動します。

  3. 左側のメニューで、[データ エクスプローラー] を選択します。

    [データ エクスプローラー] メニュー オプションが強調表示された、新しい Azure Cosmos DB for NoSQL アカウントのページを示すスクリーンショット。

  4. データ エクスプローラーで、[新しいコンテナー] オプションを選択します。

    データ エクスプローラー内の [新しいコンテナー] オプションのスクリーンショット。

  5. 新しいコンテナー[パーティション キー] には /TenantId と入力します。 残りのフィールドには、シナリオに適した値を入力します。

    Note

    ここでは、例として /TenantId を使用します。 独自のコンテナーに階層パーティション キーを実装する場合は、最初のレベルには任意のキーを指定できます。

  6. [階層パーティション キーの追加] を 2 回選択します。

    新しい階層パーティション キーを追加するボタンのスクリーンショット。

  7. サブパーティション分割の 2 番目と 3 番目の層では、/UserId/SessionId をそれぞれ入力します。

    新しい階層パーティション キーを追加するボタンのスクリーンショット。

  8. [OK] を選択してコンテナーを作成します。

SDK

SDK を使用して新しいコンテナーを作成する場合は、サブパーティション分割のキー パスの一覧を最大 3 レベルまで定義します。 新しいコンテナーのプロパティを構成するときは、サブパーティション分割のキーの一覧を使用します。

// List of partition keys, in hierarchical order. You can have up to three levels of keys.
List<string> subpartitionKeyPaths = new List<string> { 
    "/TenantId",
    "/UserId",
    "/SessionId"
};

// Create a container properties object
ContainerProperties containerProperties = new ContainerProperties(
    id: "<container-name>",
    partitionKeyPaths: subpartitionKeyPaths
);

// Create a container that's subpartitioned by TenantId > UserId > SessionId
Container container = await database.CreateContainerIfNotExistsAsync(containerProperties, throughput: 400);

Azure Resource Manager のテンプレート

サブパーティション分割されたコンテナー用の Azure Resource Manager のテンプレートは、標準のコンテナーのものとほぼ同一です。 唯一の重要な違いは、パスの properties/partitionKey 値です。 Azure Cosmos DB リソース用の Azure Resource Manager テンプレートの作成の詳細については、「Azure Cosmos DB の Azure Resource Manager テンプレート リファレンス」を参照してください。

次の表の値を使用して partitionKey オブジェクトを構成し、サブパーティション分割されたコンテナーを作成します。

パス
paths 階層パーティション キーの一覧 (最大 3 レベルの深さ)
kind MultiHash
version 2

パーティション キー定義の例

たとえば、TenantId>UserId>SessionId で構成される階層パーティション キーがあるとします。 partitionKey オブジェクトは、paths プロパティの 3 つの値すべて、MultiHashkind 値、および 2version値を含むように構成されます。

partitionKey: {
  paths: [
    '/TenantId'
    '/UserId'
    '/SessionId'
  ]
  kind: 'MultiHash'
  version: 2
}

partitionKey オブジェクトの詳細については、「ContainerPartitionKey の仕様」を参照してください。

Cosmos DB エミュレーター

Azure Cosmos DB 用の最新バージョンのローカル エミュレーターを使用して、サブパーティション分割機能をテストできます。 エミュレーターでサブパーティション分割を有効にするには、/EnablePreview フラグを使用してインストール ディレクトリからエミュレーターを起動します。

.\CosmosDB.Emulator.exe /EnablePreview

警告

エミュレーターは現在、ポータルとしてすべての階層パーティション キー機能をサポートしているわけではありません。 エミュレーターでは現在、次の機能はサポートされていません。

  • データ エクスプローラーを使用して階層パーティション キーを持つコンテナーを作成する
  • データ エクスプローラーを使用して階層パーティション キーを使用して項目に移動して操作する

詳細については、「Azure Cosmos DB エミュレーター」を参照してください。

SDK を使用して階層パーティション キーを持つコンテナーを操作する

階層パーティション キーを持つコンテナーがある場合、上で指定したバージョンの .NET または Java SDK を使用してそのコンテナーに対して操作やクエリを実行します。

コンテナーに項目を追加する

階層パーティション キーが有効になっているコンテナーに新しい項目を追加するには、2 つのオプションがあります。

  • 自動抽出
  • パスを手動で指定する

自動抽出

パーティション キー値が設定されたオブジェクトを渡すと、SDK は完全なパーティション キー パスを自動的に抽出できます。

// Create a new item
UserSession item = new UserSession()
{
    id = "f7da01b0-090b-41d2-8416-dacae09fbb4a",
    TenantId = "Microsoft",
    UserId = "00aa00aa-bb11-cc22-dd33-44ee44ee44ee",
    SessionId = "0000-11-0000-1111"
};

// Pass in the object, and the SDK automatically extracts the full partition key path
ItemResponse<UserSession> createResponse = await container.CreateItemAsync(item);

パスを手動で指定する

SDK の PartitionKeyBuilder クラスは、以前に定義した階層パーティション キー パスの値を構築できます。 サブパーティション分割が有効になっているコンテナーに新しい項目を追加する場合は、このクラスを使用します。

ヒント

大規模な場合は、SDK がオブジェクトからパスを抽出できる場合でも、完全なパーティションキー パスを指定するとパフォーマンスが改善されることがあります。

// Create a new item object
PaymentEvent item = new PaymentEvent()
{
    id = Guid.NewGuid().ToString(),
    TenantId = "Microsoft",
    UserId = "00aa00aa-bb11-cc22-dd33-44ee44ee44ee",
    SessionId = "0000-11-0000-1111"
};

// Specify the full partition key path when creating the item
PartitionKey partitionKey = new PartitionKeyBuilder()
            .Add(item.TenantId)
            .Add(item.UserId)
            .Add(item.SessionId)
            .Build();

// Create the item in the container
ItemResponse<PaymentEvent> createResponse = await container.CreateItemAsync(item, partitionKey);

項目のキー/値参照 (ポイント読み取り) を実行する

キー/値の参照 (ポイント読み取り) は、サブパーティション分割されていないコンテナーと同様の方法で実行されます。 たとえば、TenantId>UserId>SessionId から成る階層パーティション キーがあるとします。 アイテムの一意識別子は GUID です。 これは、一意のドキュメント トランザクション識別子として機能する文字列として表されます。 単一の項目に対してポイント読み取りを実行するには、項目の id プロパティと、パスの 3 つのコンポーネントすべてを含むパーティション キーの完全な値を渡します。

// Store the unique identifier
string id = "f7da01b0-090b-41d2-8416-dacae09fbb4a";

// Build the full partition key path
PartitionKey partitionKey = new PartitionKeyBuilder()
    .Add("Microsoft") //TenantId
    .Add("00aa00aa-bb11-cc22-dd33-44ee44ee44ee") //UserId
    .Add("0000-11-0000-1111") //SessionId
    .Build();

// Perform a point read
ItemResponse<UserSession> readResponse = await container.ReadItemAsync<UserSession>(
    id,
    partitionKey
);

クエリの実行

サブパーティション分割されたコンテナーでクエリを実行するために使用する SDK コードは、サブパーティション分割されていないコンテナーでクエリを実行する場合と同一です。

クエリで WHERE フィルター内またはキー階層のプレフィックス内のパーティション キーのすべての値が指定されると、SDK は対応する物理パーティションにクエリを自動的にルーティングします。 階層の "中間" のみを提供するクエリは、クロス パーティション クエリです。

たとえば、TenantId>UserId>SessionId で構成される階層パーティション キーを考えてみましょう。 クエリのフィルターのコンポーネントは、クエリが単一パーティション クエリ、ターゲット クロスパーティション クエリ、またはファンアウト クエリであるかどうかを判断します。

クエリ ルーティング
SELECT * FROM c WHERE c.TenantId = 'Microsoft' AND c.UserId = '00aa00aa-bb11-cc22-dd33-44ee44ee44ee' AND c.SessionId = '0000-11-0000-1111' TenantIdUserId、および SessionId の指定した値のデータを含む単一の論理パーティションと物理パーティションにルーティングされます。
SELECT * FROM c WHERE c.TenantId = 'Microsoft' AND c.UserId = '00aa00aa-bb11-cc22-dd33-44ee44ee44ee' 指定した値 TenantIdUserId のデータを含む論理パーティションと物理パーティションのターゲット サブセットにのみルーティングされます。 このクエリは、テナント内の特定のユーザーのデータを返すターゲット クロスパーティション クエリです。
SELECT * FROM c WHERE c.TenantId = 'Microsoft' 指定した値 TenantId のデータを含む論理パーティションと物理パーティションのターゲット サブセットにのみルーティングされます。 このクエリは、テナント内のすべてのユーザーのデータを返すターゲット クロスパーティション クエリです。
SELECT * FROM c WHERE c.UserId = '00aa00aa-bb11-cc22-dd33-44ee44ee44ee' すべての物理パーティションにルーティングされ、その結果、ファンアウト クロスパーティション クエリが発生します。
SELECT * FROM c WHERE c.SessionId = '0000-11-0000-1111' すべての物理パーティションにルーティングされ、その結果、ファンアウト クロスパーティション クエリが発生します。

サブパーティション分割コンテナーでの単一パーティション クエリ

すべてのサブパーティション分割レベルを含むクエリを実行する例を次に示します。このクエリは実質的に単一パーティション クエリになります。

// Define a single-partition query that specifies the full partition key path
QueryDefinition query = new QueryDefinition(
    "SELECT * FROM c WHERE c.TenantId = @tenant-id AND c.UserId = @user-id AND c.SessionId = @session-id")
    .WithParameter("@tenant-id", "Microsoft")
    .WithParameter("@user-id", "00aa00aa-bb11-cc22-dd33-44ee44ee44ee")
    .WithParameter("@session-id", "0000-11-0000-1111");

// Retrieve an iterator for the result set
using FeedIterator<PaymentEvent> results = container.GetItemQueryIterator<PaymentEvent>(query);

while (results.HasMoreResults)
{
    FeedResponse<UserSession> resultsPage = await resultSet.ReadNextAsync();
    foreach(UserSession result in resultsPage)
    {
        // Process result
    }
}

サブパーティション分割コンテナーでのターゲット マルチ パーティション クエリ

サブパーティション分割のレベルのサブセットを含むクエリの例を次に示します。このクエリは事実上、対象となるマルチパーティション クエリになります。

// Define a targeted cross-partition query specifying prefix path[s]
QueryDefinition query = new QueryDefinition(
    "SELECT * FROM c WHERE c.TenantId = @tenant-id")
    .WithParameter("@tenant-id", "Microsoft")

// Retrieve an iterator for the result set
using FeedIterator<PaymentEvent> results = container.GetItemQueryIterator<PaymentEvent>(query);

while (results.HasMoreResults)
{
    FeedResponse<UserSession> resultsPage = await resultSet.ReadNextAsync();
    foreach(UserSession result in resultsPage)
    {
        // Process result
    }
}

制限事項と既知の問題

  • 階層パーティション キーを使用するコンテナーの操作は、.NET v3 SDK、Java v4 SDK、Python SDK、プレビュー バージョンの JavaScript SDK 内でのみサポートされています。 階層パーティション キーを持つ新しいコンテナーを作成し、データに対して CRUD 操作またはクエリ操作を実行するには、サポートされている SDK を使用する必要があります。 Python を含む他の SDK のサポートは現在利用できません。
  • さまざまな Azure Cosmos DB コネクタには制限があります (例えば、Azure Data Factory)。
  • 階層パーティション キーの深さは、最大 3 層までのみ指定できます。
  • 階層パーティション キーは現在、新しいコンテナーでのみ有効にすることができます。 コンテナーの作成時にパーティション キー パスを設定する必要があり、後で変更することはできません。 既存のコンテナーで階層パーティションを使用するには、階層パーティション キーが設定された新しいコンテナーを作成し、コンテナー コピー ジョブを使用してデータを移動します。
  • 階層パーティション キーは、NoSQL 用 API アカウントでのみサポートされています。 MongoDB 用 API および Cassandra 用 API は現在サポートされていません。
  • 階層型パーティション キーは、現在、ユーザーとアクセス許可の機能ではサポートされていません。 階層パーティション キー パスの部分的なプレフィックスにアクセス許可を割り当てることはできません。 アクセス許可は、論理パーティション キー パス全体にのみ割り当てることができます。 たとえば、TenantId - >UserId でパーティション分割した場合、TenantId の特定の値に対するアクセス許可を割り当てることはできません。 ただし、TenantId と "UserId" の両方の値を指定する場合は、パーティション キーのアクセス許可を割り当てることができます。

次のステップ