共用方式為


Azure Cosmos DB 中的階層式分割區索引鍵

適用於:NoSQL

Azure Cosmos DB 會根據您的分割區索引鍵,將資料分散到邏輯和實體分割區,以支援水平縮放。 藉由使用階層式分割區索引鍵 (也稱為子分割),您可以為分割區索引鍵設定最多三的層級的階層,以進一步最佳化資料散發,並達到更高的調整層級。

如果您目前使用綜合索引鍵,而有分割區索引鍵超過 20 GB 資料的情況,或您想要確保每個租用戶的文件對應到自己的邏輯分割區,則使用子分割區會有幫助。 如果您使用這項功能,邏輯分割區索引鍵前置詞可以超過 20 GB 和每秒 10,000 個要求單位 (RU/秒)。 以前置詞為依據的查詢會有效率地路由至保存資料的分割區子集。

選擇您的階層式分割區索引鍵

如有多租用戶應用程式,且目前依分割區索引鍵隔離租用戶,則階層式分割可能會讓您受益。 階層式分割區可讓您擴充超過 20 GB 的邏輯分割區索引鍵限制,而且如果您想要確保每個租用戶的文件可以無限擴充,則這是很好的解決方案。 如果您目前的分割區索引鍵或單一分割區索引鍵經常達到 20 GB,階層式分割區是您工作負載的絕佳選擇。

不過,視工作負載的性質以及第一層索引鍵的基數而定,我們可能會有一些取捨,我們會在階層式分割區案例頁面中深入討論。

選擇階層式分割區索引鍵的每個層級時,請務必記住下列一般資料分割概念,並了解每個層級如何影響您的工作負載:

  • 對於「所有」容器來說,階層式分割區索引鍵的完整路徑的「每個層級」(從「第一層」開始) 都應該:

    • 具有高基數。 階層式分割區的第一個、第二個和第三個索引鍵 (如果適用) 應該都要有廣泛的可能值。

      • 在階層式分割區索引鍵的第一層具有低基數時,會將資料擷取時的所有寫入作業限制為只有一個實體分割區,直到達到 50 GB 並分割成兩個實體分割區。 例如,假設您的第一層索引鍵已開啟 TenantId,且只有 5 個唯一租用戶。 這些租用戶的每項作業只會限定為一個實體分割區,將輸送量耗用量限制為該實體分割區上的輸送量。 這是因為階層式分割區會針對具有相同第一層索引鍵的所有文件進行最佳化,以在相同的實體分割區上共置,以避免完整展開查詢。
      • 雖然這對於我們一次性擷取所有租用戶資料並且隨後的操作主要是大量讀取的工作負載來說可能沒問題,但對於您的業務需求涉及在特定時間內擷取資料的工作負載來說,這可能並不理想。 例如,如果您有嚴格的業務需求以避免延遲,則您的工作負載理論上可以實現的接收資料的最大輸送量為實體分割區數 * 10k。 如果您的最上層索引鍵具有低基數,則實體分割區數目可能會是 1,除非有足夠的資料讓層級 1 索引鍵在分割之後分散到多個分割區,而分割可能需要 4-6 小時才能完成。
    • 將要求單位 (RU) 耗用量和資料儲存體平均分配給所有邏輯分割區。 分配可確保 RU 使用量與儲存體平均分配在實體分割區。

      • 若所選的第一層索引鍵的基數似乎較高 (例如 UserId),但工作負載實際上只會對一個特定 UserId 執行作業,則您可能會遇到經常性分割,因為所有作業的範圍都只限於一或幾個實體分割區。
  • 大量讀取的工作負載:建議您選擇經常出現在查詢中的階層式分割區索引鍵。

    • 例如,經常執行查詢以篩選出多租用戶應用程式中特定使用者工作階段的工作負載,適用依序為 TenantIdUserIdSessionId 的階層式分割索引鍵。 您可以透過在篩選述詞中包含分割區索引鍵,僅將查詢有效傳送至相關實體分割區。 如需為大量讀取工作負載選擇分割區索引鍵的詳細資訊,請參閱分割概觀
  • 大量寫入工作負載:建議您針對階層式分割區索引鍵的第一層使用高基數值。 高基數表示第一層索引鍵 (以及後續層級) 至少有數千個唯一值,而且比實體分割區的數目還要多。

    • 例如,假設我們有一個工作負載,可依分割區索引鍵隔離租用戶,而且有幾個大型租用戶比其他租用戶更大量寫入。 目前,如果資料超過 20 GB,Azure Cosmos DB 將會停止擷取任何分割區索引鍵值上的資料。 在此工作負載中,Microsoft 和 Contoso 是大型租用戶,我們預期其成長速度遠高於其他租用戶。 為了避免無法擷取這些租用戶的資料的風險,階層式分割區索引鍵可讓我們擴充這些租用戶超過 20 GB 的限制。 我們可以新增更多層級 (例如 UserId 和 SessionId 等),以確保租用戶之間的可擴縮性更高。

    • 若要確保您的工作負載可以容納具有相同第一層索引鍵之所有文件的寫入,請考慮使用項目識別碼作為第二層或第三層索引鍵。

    • 若第一個層級沒有高基數,而且您目前在分割區索引鍵上達到 20 GB 邏輯分割區限制,建議您使用綜合分割區索引鍵,而不是階層式分割區索引鍵。

使用案例範例

假設您有多租用戶案例,您可以在其中儲存每個租用戶中使用者的事件資訊。 此事件資訊可能包含事件發生次數,包括但不限於登入、點選流或付款事件。

在真實世界的案例中,有些租用戶可以隨著數千位使用者成長,但仍有其他許多較小的租用戶只有少數使用者。 透過 /TenantId 分割可能會超過 Azure Cosmos DB 在單一邏輯分割區上的 20 GB 儲存限制。 透過 /UserId 分割會跨分割區對租用戶進行所有查詢。 這兩種方法都有顯著缺點。

使用結合 TenantIdUserId 的綜合分割區索引鍵,會對應用程式增加複雜度。 此外,除非事先知道所有使用者並進行指定,否則租用戶綜合分割區索引鍵查詢仍會跨分割區進行。

若工作負載有大致相同工作負載模式的租用戶,階層式分割區索引鍵會有幫助。 使用階層式分割索引鍵時,您可以先在 TenantId 上分割,然後在 UserId 上分割。 如果您預期 TenantIdUserId 組合會產生超過 20 GB 的分割區,您甚至可以進一步向下分割至另一個層級,例如在 SessionId 上。 整體深度不能超過三個層級。 當實體分割區有超過 50 GB 的儲存體時,Azure Cosmos DB 會自動分割實體分割區,讓大約一半的資料位於一個實體分割區上,而另一半則位於其他實體分割區上。 實際上,子分割表示單一 TenantId 值可以超過 20 GB 的資料限制,而且 TenantId 的資料可能會橫跨多個實體分割區。

指定 TenantId,或同時指定 TenantIdUserId 的查詢將有效路由至僅包含相關資料的實體分割區子集。 指定完整或前置詞子分割索引鍵路徑,可有效地避免完整的展開傳送查詢。 例如,如果容器有 1,000 個實體分割區,但特定 TenantId 值只位於其中 5 個實體分割區之上,則查詢只會路由傳送至較少數量的相關實體分割區上。

在階層中使用項目識別碼

如果您容器的其中一個屬性具有各式各樣的可能值,則該屬性便可能很適合作為階層最後一層的分割區索引鍵。 這類屬性的其中一個可能範例是項目識別碼。 容器中的每個項目都有系統屬性項目識別碼。 將項目識別碼新增為另一個層級,保證您進行調整時可超過 20 GB 的邏輯分割區索引鍵限制。 您可以將第一層或第一層和第二層的索引鍵調整到超過此限制。

例如,您可能有用於多租用戶工作負載的容器,且依據 TenantIdUserId 分割。 如果 TenantIdUserId 的單一組合可能超過 20 GB,則建議您使用三個索引鍵層級進行分割,而第三層索引鍵使用高基數。 此案例的範例是,如果第三層索引鍵是具有自然高基數的 GUID。 TenantIdUserId 和 GUID 的組合不太可能超過 20 GB,因此 TenantIdUserId 的組合可以有效地調整到超過 20 GB。

如需使用項目識別碼作為分割區索引鍵的詳細資訊,請參閱分割概觀

開始使用

重要

僅下列 SDK 版本可支援使用階層式分割區索引鍵的容器。 您必須使用支援的 SDK 來建立具有階層式分割區索引鍵的新容器,以及對資料執行建立、讀取、更新和刪除 (CRUD) 或查詢作業。 如果您想要使用目前不支援的 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/

使用階層式分割區索引鍵建立容器

若要開始使用,請使用預先定義且深度最多為三個層級的子分割索引鍵路徑清單,建立新容器。

您可以使用下列其中一個選項來建立新容器:

  • Azure 入口網站
  • SDK
  • Azure Resource Manager 範本
  • Azure Cosmos DB 模擬器

Azure 入口網站

建立容器並指定階層式分割區索引鍵最簡單的方式是使用 Azure 入口網站。

  1. 登入 Azure 入口網站

  2. 移至現有的 Azure Cosmos DB for NoSQL 帳戶頁面。

  3. 在左側功能表上,選取 [資料總管]

    顯示新 Azure Cosmos DB for NoSQL 帳戶頁面的螢幕擷取畫面,其中已醒目提示 [資料總管] 功能表選項。

  4. 在 [資料總管] 上,選取 [新增容器] 選項。

    [資料總管] 內 [新增容器] 選項的螢幕擷取畫面。

  5. 在 [新增容器] 中,針對 [分割索引鍵] 輸入 /TenantId。 在其餘欄位中輸入符合您情況的任何值。

    注意

    我們在這裡使用 /TenantId 作為範例。 您可以在自己的容器上實作階層式分割索引鍵時,為第一層指定任何索引鍵。

  6. 選取 [新增階層式分割區索引鍵] 兩次。

    螢幕擷取畫面顯示用來新增階層式分割區索引鍵的按鈕。

  7. 針對子分割的第二層和第三層,分別輸入 /UserId/SessionId

    列出三個階層式分割區索引鍵的螢幕擷取畫面。

  8. 選取 [確定] 以建立容器。

SDK

使用 SDK 建立新容器時,請定義最多三個深度層級的子分割索引鍵路徑清單。 設定新容器屬性時,請使用子分割索引鍵的清單。

// 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 階層式分割區索引鍵清單 (最多三個深度層級)
kind MultiHash
version 2

範例分割區索引鍵定義

例如,假設您有由 TenantIdUserId>>SessionId 組成的階層式分割區索引鍵。 partitionKey 物件會設定為包含 paths 屬性中的所有三個值、MultiHashkind 值和 2version 值。

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

如需 partitionKey 物件的詳細資訊,請參閱 ContainerPartitionKey 規格

Azure Cosmos DB 模擬器

您可以使用最新版的 Azure Cosmos DB 本機模擬器來測試子分割功能。 若要在模擬器上啟用子分割功能,請使用 /EnablePreview 旗標來從安裝目錄啟動模擬器:

.\CosmosDB.Emulator.exe /EnablePreview

警告

模擬器目前不支援所有階層式分割區索引鍵功能作為入口網站。 模擬器目前不支援:

  • 使用資料總管建立具有階層式分割區索引鍵的容器
  • 使用資料總管瀏覽至使用階層式分割區索引鍵的項目並與之互動

如需詳細資訊,請參閱 Azure Cosmos DB 模擬器

使用 SDK 來處理具有階層式分割區索引鍵的容器

當您的容器具有階層式分割區索引鍵時,請使用先前指定的 .NET 或 Java SDK 版本,在該容器上執行作業和執行查詢。

將項目新增至容器

有兩個選項可將新項目新增至已啟用階層式分割區索引鍵的容器:

  • 自動擷取
  • 手動指定路徑

自動擷取

如果您傳入已設定分割區索引鍵值的物件,SDK 可以自動擷取完整的分割區索引鍵路徑。

// Create a new item
UserSession item = new UserSession()
{
    id = "f7da01b0-090b-41d2-8416-dacae09fbb4a",
    TenantId = "Microsoft",
    UserId = "8411f20f-be3e-416a-a3e7-dcd5a3c1f28b",
    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 = "8411f20f-be3e-416a-a3e7-dcd5a3c1f28b",
    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 屬性,以及分割區索引鍵的完整值,該值需包括路徑的三個元件。

// 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("8411f20f-be3e-416a-a3e7-dcd5a3c1f28b") //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 = '8411f20f-be3e-416a-a3e7-dcd5a3c1f28b' AND c.SessionId = '0000-11-0000-1111' 路由傳送至單一邏輯和實體分割區,其中包含 TenantIdUserIdSessionId 指定值的資料。
SELECT * FROM c WHERE c.TenantId = 'Microsoft' AND c.UserId = '8411f20f-be3e-416a-a3e7-dcd5a3c1f28b' 路由傳送至單一邏輯和實體分割區,其中包含 TenantIdUserId 指定值的資料。 此查詢是鎖定目標跨分割區查詢,可傳回租用戶中特定使用者的資料。
SELECT * FROM c WHERE c.TenantId = 'Microsoft' 路由傳送至單一邏輯和實體分割區,其中包含 TenantId 指定值的資料。 此查詢是鎖定目標跨分割區查詢,可傳回租用戶中所有使用者的資料。
SELECT * FROM c WHERE c.UserId = '8411f20f-be3e-416a-a3e7-dcd5a3c1f28b' 路由傳送至所有實體分割區,導致展開跨分割區查詢。
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", "8411f20f-be3e-416a-a3e7-dcd5a3c1f28b")
    .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 預覽版本中才支援使用階層式分割區索引鍵的容器。 您必須使用支援的 SDK 來建立具有階層式分割區索引鍵的新容器,以及對資料執行 CRUD 或查詢作業。 目前無法支援其他 SDK,包括 Python。
  • 各種 Azure Cosmos DB 連接器都有限制 (例如,Azure Data Factory)。
  • 您最多僅可指定三層的階層式分割區索引鍵。
  • 階層式分割區索引鍵目前僅可於新容器上啟用。 您必須在建立容器時設定分割區索引鍵路徑,且之後無法變更。 若要在現有的容器上使用階層式分割區,請使用已設定階層式分割區索引鍵來建立新的容器,並使用容器複製作業來移動資料。
  • 目前只有適用於 NoSQL 的 API 帳戶才支援階層式分割區索引鍵。 目前不支援 MongoDB 和 Cassandra 的 API。
  • 權限功能目前不支援與階層式分割區索引鍵搭配使用。 您無法將權限指派給階層式分割區索引鍵路徑的部分前置詞。 權限只能指派給整個邏輯分割區索引鍵路徑。 例如,如果您已依 TenantId - >UserId 進行分割,則無法為 TenantId 的特定值指派權限。 不過,如果您同時指定 TenantId 和 "UserId" 的值,則可以為分割區索引鍵指派權限。

下一步