Banco de Dados de Documentos - Particionamento

Quando seu banco de dados começa a crescer além de 10 GB, você pode dimensionar simplesmente criando novas coleções e, em seguida, espalhando ou particionando seus dados em mais e mais coleções.

Mais cedo ou mais tarde, uma única coleção, com capacidade de 10 GB, não será suficiente para conter seu banco de dados. Agora, 10 GB pode não parecer um número muito grande, mas lembre-se de que estamos armazenando documentos JSON, que são apenas texto simples e você pode colocar muitos documentos de texto simples em 10 GB, mesmo considerando a sobrecarga de armazenamento para os índices.

O armazenamento não é a única preocupação quando se trata de escalabilidade. A taxa de transferência máxima disponível em uma coleção é 2,5 mil unidades de solicitação por segundo que você obtém com uma coleção S3. Portanto, se você precisar de uma taxa de transferência mais alta, também precisará escalar horizontalmente, particionando com várias coleções. O particionamento de expansão também é chamadohorizontal partitioning.

Existem muitas abordagens que podem ser usadas para particionar dados com o Azure DocumentDB. A seguir estão as estratégias mais comuns -

  • Particionamento Spillover
  • Particionamento de intervalo
  • Particionamento de Pesquisa
  • Particionamento Hash

Particionamento Spillover

O particionamento indireto é a estratégia mais simples porque não há chave de partição. Geralmente, é uma boa escolha começar quando você não tem certeza sobre muitas coisas. Você pode não saber se precisará dimensionar além de uma única coleção ou quantas coleções você pode precisar adicionar ou com que rapidez pode ser necessário adicioná-las.

  • O particionamento spillover começa com uma única coleção e não há chave de partição.

  • A coleção começa a crescer e depois cresce um pouco mais, e então um pouco mais, até você começar a chegar perto do limite de 10 GB.

  • Ao atingir 90% da capacidade, você passa para uma nova coleção e começa a usá-la para novos documentos.

  • Depois que seu banco de dados for dimensionado para um número maior de coleções, você provavelmente desejará mudar para uma estratégia baseada em uma chave de partição.

  • Ao fazer isso, você precisará reequilibrar seus dados movendo documentos para coleções diferentes com base em qualquer estratégia para a qual está migrando.

Particionamento de intervalo

Uma das estratégias mais comuns é o particionamento de intervalo. Com essa abordagem, você determina a faixa de valores em que a chave de partição de um documento pode se enquadrar e direciona o documento para uma coleção correspondente a essa faixa.

  • As datas são normalmente usadas com esta estratégia, quando você cria uma coleção para conter documentos que se enquadram no intervalo de datas definido. Quando você define intervalos pequenos o suficiente, onde você tem certeza de que nenhuma coleção excederá seu limite de 10 GB. Por exemplo, pode haver um cenário em que uma única coleção possa lidar razoavelmente com documentos de um mês inteiro.

  • Também pode ser o caso de a maioria dos usuários consultar dados atuais, que seriam dados deste mês ou talvez do mês passado, mas os usuários raramente procuram dados muito mais antigos. Então, você começa em junho com uma coleção S3, que é a coleção mais cara que você pode comprar e oferece o melhor rendimento possível.

  • Em julho, você compra outra coleção S3 para armazenar os dados de julho e também reduz os dados de junho para uma coleção S2 menos cara. Então, em agosto, você obtém outra coleção S3 e escala julho até S2 e junho até S1. Vai, mês após mês, onde você sempre mantém os dados atuais disponíveis para alto rendimento e os dados mais antigos são mantidos disponíveis com rendimentos mais baixos.

  • Contanto que a consulta forneça uma chave de partição, apenas a coleção que precisa ser consultada será consultada e não todas as coleções no banco de dados como acontece com o particionamento de spillover.

Particionamento de Pesquisa

Com o particionamento de pesquisa, você pode definir um mapa de partição que roteia documentos para coleções específicas com base em sua chave de partição. Por exemplo, você pode particionar por região.

  • Armazene todos os documentos dos EUA em uma coleção, todos os documentos europeus em outra coleção e todos os documentos de qualquer outra região em uma terceira coleção.

  • Use este mapa de partição e um resolvedor de partição de pesquisa pode descobrir em qual coleção criar um documento e em quais coleções consultar, com base na chave de partição, que é a propriedade de região contida em cada documento.

Particionamento Hash

No particionamento hash, as partições são atribuídas com base no valor de uma função hash, permitindo distribuir uniformemente as solicitações e os dados em várias partições.

Isso é comumente usado para particionar dados produzidos ou consumidos de um grande número de clientes distintos e é útil para armazenar perfis de usuário, itens de catálogo, etc.

Vamos dar uma olhada em um exemplo simples de particionamento de intervalo usando o RangePartitionResolver fornecido pelo .NET SDK.

Step 1- Crie um novo DocumentClient e iremos criar duas coleções na tarefa CreateCollections. Um conterá documentos para usuários que têm IDs de usuário começando com A a M e o outro para IDs de usuário de N a 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 - Registre o resolvedor de intervalo para o banco de dados.

Step 3- Crie um novo RangePartitionResolver <string>, que é o tipo de dados de nossa chave de partição. O construtor usa dois parâmetros, o nome da propriedade da chave de partição e um dicionário que é o mapa de fragmentos ou mapa de partição, que é apenas uma lista dos intervalos e coleções correspondentes que estamos predefinindo para o resolvedor.

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

É necessário codificar o maior valor UTF-8 possível aqui. Do contrário, o primeiro intervalo não corresponderia a nenhum Ms, exceto um único M, e da mesma forma para Z no segundo intervalo. Portanto, você pode apenas pensar neste valor codificado aqui como um caractere curinga para correspondência na chave de partição.

Step 4- Depois de criar o resolvedor, registre-o no banco de dados com o DocumentClient atual. Para fazer isso, basta atribuí-lo à propriedade de dicionário do PartitionResolver.

Criaremos e consultaremos os documentos no banco de dados, não em uma coleção como você normalmente faz. O resolvedor usará este mapa para encaminhar as solicitações às coleções apropriadas.

Agora vamos criar alguns documentos. Primeiro, criaremos um para userId Kirk e, em seguida, um para Spock.

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

O primeiro parâmetro aqui é um auto-link para o banco de dados, não uma coleção específica. Isso não é possível sem um resolvedor de partição, mas com um ele funciona perfeitamente.

Ambos os documentos foram salvos no banco de dados myfirstdb, mas sabemos que Kirk está sendo armazenado na coleção de A a M e Spock está sendo armazenado na coleção de N a Z, se nosso RangePartitionResolver estiver funcionando corretamente.

Vamos chamá-los da tarefa CreateDocumentClient, conforme mostrado no código a seguir.

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

Quando o código acima for executado, você receberá a seguinte saída.

**** Create Documents Across Partitions **** 
Document 1: dbs/Ic8LAA==/colls/Ic8LAO2DxAA=/docs/Ic8LAO2DxAABAAAAAAAAAA==/ 
Document 2: dbs/Ic8LAA==/colls/Ic8LAP12QAE=/docs/Ic8LAP12QAEBAAAAAAAAAA==/

Como visto, os self-links dos dois documentos têm IDs de recursos diferentes porque existem em duas coleções separadas.