Chaves de partição hierárquica no Azure Cosmos DB

APLICA-SE A: NoSQL

O Azure Cosmos DB distribui seus dados entre partições lógicas e físicas com base em suas chaves de partição para dar suporte ao dimensionamento horizontal. Usando chaves de partição hierárquicas (também chamadas de subpartitoning), você pode configurar até uma hierarquia de três níveis para suas chaves de partição para otimizar ainda mais a distribuição de dados e para um nível mais alto de dimensionamento.

Se você usa chaves sintéticas hoje, tem cenários em que as chaves de partição podem exceder 20 GB de dados ou gostaria de garantir que o documento de cada locatário seja mapeado para sua própria partição lógica, o subparticionamento pode ajudar. Se você usar esse recurso, os prefixos de chave de partição lógica podem exceder 20 GB e 10.000 unidades de solicitação por segundo (RU/s). As consultas por prefixo são encaminhadas de forma eficiente para o subconjunto de partições que contêm os dados.

Escolher as chaves de partição hierárquica

Se você tiver aplicativos multilocatários e atualmente isolar locatários por chave de partição, as partições hierárquicas podem beneficiá-lo. As partições hierárquicas permitem que você escale além do limite de chave de partição lógica de 20 GB e são uma boa solução se você quiser garantir que cada um dos documentos de seus locatários possa ser dimensionado infinitamente. Se a sua chave de partição atual ou se uma única chave de partição estiver frequentemente atingindo 20 GB, as partições hierárquicas são uma ótima opção para sua carga de trabalho.

No entanto, dependendo da natureza da sua carga de trabalho e de quão cardinal é a sua chave de primeiro nível, pode haver algumas compensações que abordamos em profundidade na nossa página de cenários de partição hierárquica.

Ao escolher cada nível da sua chave de partição hierárquica, é importante ter em mente os seguintes conceitos gerais de particionamento e entender como cada um deles pode afetar sua carga de trabalho:

  • Para todos os contêineres, cada nível do caminho completo (começando com o primeiro nível) da sua chave de partição hierárquica deve:

    • Ter uma cardinalidade elevada. A primeira, segunda e terceira chaves (se aplicável) da partição hierárquica devem ter uma ampla gama de valores possíveis.

      • Ter baixa cardinalidade no primeiro nível da chave de partição hierárquica limitará todas as suas operações de gravação no momento da ingestão a apenas uma partição física até que ela atinja 50 GB e se divida em duas partições físicas. Por exemplo, suponha que sua chave de primeiro nível esteja ativada TenantId e tenha apenas 5 locatários exclusivos. Cada uma das operações desses locatários terá como escopo apenas uma partição física, limitando seu consumo de taxa de transferência apenas ao que está nessa partição física. Isso ocorre porque as partições hierárquicas otimizam para que todos os documentos com a mesma chave de primeiro nível sejam colocados na mesma partição física para evitar consultas de distribuição completa.
      • Embora isso possa ser aceitável para cargas de trabalho em que fazemos uma ingestão única de todos os dados de nossos locatários e as operações a seguir são principalmente de leitura pesada depois, isso pode não ser ideal para cargas de trabalho em que seus requisitos de negócios envolvem a ingestão de dados dentro de um tempo específico. Por exemplo, se você tiver requisitos de negócios rigorosos para evitar latências, a taxa de transferência máxima que sua carga de trabalho pode teoricamente alcançar para ingerir dados é o número de partições físicas * 10k. Se sua chave de nível superior tiver baixa cardinalidade, seu número de partições físicas provavelmente será 1, a menos que haja dados suficientes para a chave de nível 1 para que ela seja espalhada por várias partições após divisões, o que pode levar entre 4 e 6 horas para ser concluído.
    • Espalhe o consumo da unidade de solicitação (RU) e o armazenamento de dados uniformemente em todas as partições lógicas. Esse spread garante o consumo de RU e a distribuição de armazenamento uniformes em suas partições físicas.

      • Se você escolher uma chave de primeiro nível que pareça ter alta cardinalidade como UserId, mas na prática sua carga de trabalho executa operações em apenas uma partição específica UserId, então é provável que você se depare com uma partição quente, pois todas as suas operações terão escopo para apenas uma ou poucas partições físicas.
  • Cargas de trabalho de leitura pesada: recomendamos que você escolha chaves de partição hierárquicas que aparecem com frequência em suas consultas.

    • Por exemplo, uma carga de trabalho que frequentemente executa consultas para filtrar sessões de usuário específicas em um aplicativo multilocatário pode se beneficiar de chaves de partição hierárquicas de TenantId, UserIde SessionId, nessa ordem. As consultas podem ser encaminhadas de forma eficiente apenas para as partições físicas relevantes, incluindo a chave de partição no predicado do filtro. Para obter mais informações sobre como escolher chaves de partição para cargas de trabalho de leitura pesada, consulte a visão geral do particionamento.
  • Cargas de trabalho pesadas de gravação: recomendamos o uso de um alto valor cardinal para o primeiro nível de sua chave de partição hierárquica. Alta cardinalidade significa que a chave de primeiro nível (e os níveis subsequentes também) tem pelo menos milhares de valores exclusivos e mais valores exclusivos do que o número de suas partições físicas.

    • Por exemplo, suponha que temos uma carga de trabalho que isola os locatários por chave de partição e tem alguns locatários grandes que são mais pesados em gravação do que outros. Hoje, o Azure Cosmos DB deixará de ingerir dados em qualquer valor de chave de partição se exceder 20 GB de dados. Nessa carga de trabalho, a Microsoft e a Contoso são grandes locatárias e prevemos que ela cresça muito mais rápido do que nossos outros locatários. Para evitar o risco de não ser possível ingerir dados para esses locatários, as chaves de partição hierárquica nos permitem dimensionar esses locatários além do limite de 20 GB. Podemos adicionar mais níveis como UserId e SessionId para garantir maior escalabilidade entre locatários.

    • Para garantir que sua carga de trabalho possa acomodar gravações para todos os documentos com a mesma chave de primeiro nível, considere usar a ID do item como uma chave de segundo ou terceiro nível.

    • Se o seu primeiro nível não tem cardinalidade alta e você está atingindo o limite de partição lógica de 20 GB na sua chave de partição hoje, sugerimos usar uma chave de partição sintética em vez de uma chave de partição hierárquica.

Exemplo de caso de uso

Suponha que você tenha um cenário multilocatário no qual armazena informações de eventos para usuários em cada locatário. As informações do evento podem ter ocorrências de eventos, incluindo, entre outros, eventos de entrada, fluxo de cliques ou pagamento.

Em um cenário do mundo real, alguns locatários podem crescer muito, com milhares de usuários, enquanto os muitos outros locatários são menores e têm poucos usuários. O particionamento por /TenantId pode levar a exceder o limite de armazenamento de 20 GB do Azure Cosmos DB em uma única partição lógica. O particionamento por /UserId faz todas as consultas em uma partição cruzada de locatário. Ambas as abordagens apresentam desvantagens significativas.

Usando uma chave de partição sintética que combina TenantId e UserId adiciona complexidade ao aplicativo. Além disso, as consultas de chave de partição sintética para um locatário ainda são partições cruzadas, a menos que todos os usuários sejam conhecidos e especificados com antecedência.

Se sua carga de trabalho tiver locatários com aproximadamente os mesmos padrões de carga de trabalho, a chave de partição hierárquica pode ajudar. Com chaves de partição hierárquicas, você pode particionar primeiro no e depois no TenantIdUserId. Se você espera que a combinação e UserId produza TenantId partições que excedam 20 GB, você pode até mesmo particionar mais abaixo para outro nível, como em SessionId. A profundidade geral não pode exceder três níveis. Quando uma partição física excede 50 GB de armazenamento, o Azure Cosmos DB divide automaticamente a partição física para que aproximadamente metade dos dados esteja em uma partição física e metade na outra. Efetivamente, o subparticionamento significa que um único TenantId valor pode exceder 20 GB de dados, e é possível que TenantId os dados abranjam várias partições físicas.

As consultas que especificam TenantId, ou ambas TenantId e UserId, são encaminhadas eficientemente apenas para o subconjunto de partições físicas que contêm os dados relevantes. Especificar o caminho da chave de partição subparticionada completa ou prefixada efetivamente evita uma consulta de distribuição completa. Por exemplo, se o contêiner tivesse 1.000 partições físicas, mas um valor específico TenantId estivesse apenas em 5 partições físicas, a consulta seria roteada para o menor número de partições físicas relevantes.

Usar ID de item na hierarquia

Se o contêiner tiver uma propriedade que tenha uma grande variedade de valores possíveis, a propriedade provavelmente será uma ótima opção de chave de partição para o último nível da hierarquia. Um exemplo possível desse tipo de propriedade é a ID do item. O ID do item de propriedade do sistema existe em cada item no contêiner. Adicionar o ID do item como outro nível garante que você possa escalar além do limite de chave de partição lógica de 20 GB. Você pode escalar além desse limite para o primeiro nível ou para o primeiro e segundo níveis de chaves.

Por exemplo, você pode ter um contêiner para uma carga de trabalho multilocatária particionada por TenantId e UserId. Se for possível para uma única combinação de TenantId e UserId exceder 20 GB, então recomendamos que você particione usando três níveis de chaves, e em que a chave de terceiro nível tem alta cardinalidade. Um exemplo desse cenário é se a chave de terceiro nível é um GUID que tem cardinalidade naturalmente alta. É improvável que a combinação de , UserIde um GUID exceda 20 GB, portanto, a combinação de e UserId pode efetivamente ser dimensionada além de TenantIdTenantId 20 GB.

Para obter mais informações sobre como usar a ID do item como uma chave de partição, consulte a visão geral do particionamento.

Começar agora

Importante

O trabalho com contêineres que usam chaves de partição hierárquica é suportado apenas nas seguintes versões do SDK. Você deve usar um SDK suportado para criar novos contêineres com chaves de partição hierárquica e para executar operações de criação, leitura, atualização e exclusão (CRUD) ou consulta nos dados. Se você quiser usar um SDK ou conector que não é suportado no momento, envie uma solicitação em nosso fórum da comunidade.

Encontre a versão de visualização mais recente de cada SDK suportado:

SDK Versões suportadas Link do gerenciador de pacotes
SDK v3 de .NET >= 3,33,0 https://www.nuget.org/packages/Microsoft.Azure.Cosmos/3.33.0/
SDK v4 de Java >= 4,42,0 https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/cosmos/azure-cosmos/CHANGELOG.md#4420-2023-03-17/
SDK do JavaScript 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/

Criar um contêiner usando chaves de partição hierárquica

Para começar, crie um novo contêiner usando uma lista predefinida de caminhos de chave de subparticionamento de até três níveis de profundidade.

Você pode criar um novo contêiner usando uma destas opções:

  • Portal do Azure
  • SDK
  • Modelo Azure Resource Manager
  • Emulador do Azure Cosmos DB

Portal do Azure

A maneira mais simples de criar um contêiner e especificar chaves de partição hierárquica é usando o portal do Azure.

  1. Inicie sessão no portal do Azure.

  2. Vá para a página existente da conta do Azure Cosmos DB para NoSQL.

  3. No menu à esquerda, selecione Data Explorer.

    Captura de tela que mostra a página de uma nova conta do Azure Cosmos DB para NoSQL com a opção de menu Data Explorer realçada.

  4. No Data Explorer, selecione a opção Novo Contêiner .

    Captura de ecrã da opção Novo Contentor no Data Explorer.

  5. Em Novo Contêiner, para Chave de partição, digite /TenantId. Para os campos restantes, insira qualquer valor que corresponda ao seu cenário.

    Nota

    Usamos /TenantId como exemplo aqui. Você pode especificar qualquer chave para o primeiro nível ao implementar chaves de partição hierárquica em seus próprios contêineres.

  6. Selecione Adicionar chave de partição hierárquica duas vezes.

    Captura de ecrã do botão para adicionar uma nova chave de partição hierárquica.

  7. Para a segunda e terceira camadas de subparticionamento, insira /UserId e /SessionId respectivamente.

    Captura de ecrã de uma lista de três chaves de partição hierárquicas.

  8. Selecione OK para criar o contentor.

SDK

Ao criar um novo contêiner usando o SDK, defina uma lista de caminhos de chave de subparticionamento de até três níveis de profundidade. Use a lista de chaves de subpartição ao configurar as propriedades do novo contêiner.

// 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);

Modelos do Azure Resource Manager

O modelo do Azure Resource Manager para um contêiner subparticionado é quase idêntico a um contêiner padrão. A única diferença fundamental é o valor do properties/partitionKey caminho. Para obter mais informações sobre como criar um modelo do Azure Resource Manager para um recurso do Azure Cosmos DB, consulte a referência de modelo do Azure Resource Manager para o Azure Cosmos DB.

Configure o partitionKey objeto usando os valores na tabela a seguir para criar um contêiner subparticionado:

Caminho Value
paths Lista de chaves de partição hierárquica (máximo de três níveis de profundidade)
kind MultiHash
version 2

Exemplo de definição de chave de partição

Por exemplo, suponha que você tenha uma chave de partição hierárquica composta por TenantIdSessionId>UserId>. O partitionKey objeto seria configurado para incluir todos os três valores na paths propriedade, um kind valor de MultiHash, e um version valor de 2.

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

Para obter mais informações sobre o partitionKey objeto, consulte a especificação ContainerPartitionKey.

Emulador do Azure Cosmos DB

Você pode testar o recurso de subparticionamento usando a versão mais recente do emulador local para o Azure Cosmos DB. Para habilitar a subparitioning no emulador, inicie o emulador a partir do diretório de instalação com o /EnablePreview sinalizador:

.\CosmosDB.Emulator.exe /EnablePreview

Aviso

Atualmente, o emulador não suporta todos os recursos de chave de partição hieárquica como o portal. O emulador atualmente não suporta:

  • Usando o Data Explorer para criar contêineres com chaves de partição hierárquica
  • Usando o Data Explorer para navegar e interagir com itens usando chaves de partição hierárquica

Para obter mais informações, consulte Emulador do Azure Cosmos DB.

Use os SDKs para trabalhar com contêineres que tenham chaves de partição hierárquica

Quando você tiver um contêiner com chaves de partição hierárquicas, use as versões especificadas anteriormente dos SDKs .NET ou Java para executar operações e consultas nesse contêiner.

Adicionar um item a um contêiner

Há duas opções para adicionar um novo item a um contêiner com chaves de partição hierárquicas habilitadas:

  • Extração automática
  • Especificar manualmente o caminho

Extração automática

Se você passar um objeto com o valor da chave de partição definido, o SDK poderá extrair automaticamente o caminho completo da chave de partição.

// 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);

Especificar manualmente o caminho

A PartitionKeyBuilder classe no SDK pode construir um valor para um caminho de chave de partição hierárquica definido anteriormente. Use essa classe quando você adicionar um novo item a um contêiner que tenha o subparticionamento habilitado.

Gorjeta

Em escala, o desempenho pode ser melhorado se você especificar o caminho completo da chave de partição, mesmo que o SDK possa extrair o caminho do objeto.

// 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);

Executar uma pesquisa de chave/valor (ponto de leitura) de um item

As pesquisas de chave/valor (leituras pontuais) são realizadas de forma semelhante a um contêiner não subparticionado. Por exemplo, suponha que você tenha uma chave de partição hierárquica que consiste em TenantIdSessionId>UserId>. O identificador exclusivo do item é um GUID. Ele é representado como uma cadeia de caracteres que serve como um identificador de transação de documento exclusivo. Para executar uma leitura pontual em um único item, passe a id propriedade do item e o valor completo para a chave de partição, incluindo todos os três componentes do caminho.

// 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
);

Executar uma consulta

O código SDK que você usa para executar uma consulta em um contêiner subparticionado é idêntico à execução de uma consulta em um contêiner não subparticionado.

Quando a consulta especifica todos os valores das chaves de partição no WHERE filtro ou em um prefixo da hierarquia de chaves, o SDK roteia automaticamente a consulta para as partições físicas correspondentes. As consultas que fornecem apenas o "meio" da hierarquia são consultas entre partições.

Por exemplo, considere uma chave de partição hierárquica composta por TenantIdSessionId>UserId>. Os componentes do filtro da consulta determinam se a consulta é uma consulta de partição única, uma consulta de partição cruzada direcionada ou uma consulta de ventilação.

Query Encaminhamento
SELECT * FROM c WHERE c.TenantId = 'Microsoft' AND c.UserId = '00aa00aa-bb11-cc22-dd33-44ee44ee44ee' AND c.SessionId = '0000-11-0000-1111' Roteado para a única partição lógica e física que contém os dados para os valores especificados de TenantId, e .SessionIdUserId
SELECT * FROM c WHERE c.TenantId = 'Microsoft' AND c.UserId = '00aa00aa-bb11-cc22-dd33-44ee44ee44ee' Roteado apenas para o subconjunto de destino da(s) partição(ões) lógica(s) e física(s) que contêm dados para os valores especificados de TenantId e UserId. Essa consulta é uma consulta entre partições direcionada que retorna dados para um usuário específico no locatário.
SELECT * FROM c WHERE c.TenantId = 'Microsoft' Roteado apenas para o subconjunto de destino da(s) partição(ões) lógica(s) física(s) que contêm dados para o valor especificado de TenantId. Esta consulta é uma consulta entre partições direcionada que retorna dados para todos os usuários em um locatário.
SELECT * FROM c WHERE c.UserId = '00aa00aa-bb11-cc22-dd33-44ee44ee44ee' Roteado para todas as partições físicas, resultando em uma consulta de partição cruzada de ventilação.
SELECT * FROM c WHERE c.SessionId = '0000-11-0000-1111' Roteado para todas as partições físicas, resultando em uma consulta de partição cruzada de ventilação.

Consulta de partição única em um contêiner subparticionado

Aqui está um exemplo de execução de uma consulta que inclui todos os níveis de subparticionamento, tornando efetivamente a consulta uma consulta de partição única.

// 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
    }
}

Consulta multiparticionária direcionada em um contêiner subparticionado

Aqui está um exemplo de uma consulta que inclui um subconjunto dos níveis de subparticionamento, tornando essa consulta efetivamente uma consulta de várias partições direcionada.

// 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
    }
}

Problemas conhecidos e de limitações

  • O trabalho com contêineres que usam chaves de partição hierárquicas é suportado apenas no SDK do .NET v3, no SDK do Java v4, no SDK do Python e na versão de visualização do SDK do JavaScript. Você deve usar um SDK suportado para criar novos contêineres que tenham chaves de partição hierárquicas e para executar operações CRUD ou de consulta nos dados. O suporte para outros SDKs, incluindo Python, não está disponível atualmente.
  • Há limitações com vários conectores do Azure Cosmos DB (por exemplo, com o Azure Data Factory).
  • Você pode especificar chaves de partição hierárquica apenas até três camadas de profundidade.
  • Atualmente, as chaves de partição hierárquica podem ser ativadas apenas em novos contêineres. Você deve definir caminhos de chave de partição no momento da criação do contêiner e não pode alterá-los posteriormente. Para usar partições hierárquicas em contêineres existentes, crie um novo contêiner com as chaves de partição hierárquica definidas e mova os dados usando trabalhos de cópia de contêiner.
  • Atualmente, as chaves de partição hierárquica são suportadas apenas para a API para contas NoSQL. As APIs para MongoDB e Cassandra não são suportadas no momento.
  • Atualmente, as chaves de partição hierárquica não são suportadas com o recurso de usuários e permissões. Não é possível atribuir uma permissão a um prefixo parcial do caminho da chave de partição hierárquica. As permissões só podem ser atribuídas a todo o caminho da chave de partição lógica. Por exemplo, se você particionou por TenantId - >UserId, não poderá atribuir uma permissão para um valor específico de TenantId. No entanto, você pode atribuir uma permissão para uma chave de partição se especificar o valor para TenantId e ''UserId'''.

Próximos passos