DocumentDB - Partitionnement

Lorsque votre base de données commence à croître au-delà de 10 Go, vous pouvez évoluer simplement en créant de nouvelles collections, puis en répartissant ou en partitionnant vos données sur de plus en plus de collections.

Tôt ou tard, une seule collection, d'une capacité de 10 Go, ne suffira pas à contenir votre base de données. Maintenant, 10 Go peut ne pas sembler un très grand nombre, mais rappelez-vous que nous stockons des documents JSON, qui ne sont que du texte brut et que vous pouvez contenir de nombreux documents en texte brut dans 10 Go, même si vous considérez la surcharge de stockage pour les index.

Le stockage n'est pas le seul problème en matière d'évolutivité. Le débit maximal disponible sur une collection est de deux mille cinq cents unités de demande par seconde que vous obtenez avec une collection S3. Par conséquent, si vous avez besoin d'un débit plus élevé, vous devrez également évoluer en partitionnant avec plusieurs collections. Le partitionnement avec montée en puissance est également appeléhorizontal partitioning.

Il existe de nombreuses approches qui peuvent être utilisées pour partitionner les données avec Azure DocumentDB. Voici les stratégies les plus courantes -

  • Partitionnement Spillover
  • Partitionnement de plage
  • Recherche de partitionnement
  • Partitionnement de hachage

Partitionnement Spillover

Le partitionnement par débordement est la stratégie la plus simple car il n'y a pas de clé de partition. C'est souvent un bon choix pour commencer lorsque vous n'êtes pas sûr de beaucoup de choses. Vous ne savez peut-être pas si vous aurez même besoin de passer au-delà d'une seule collection, combien de collections vous devrez peut-être ajouter ou à quelle vitesse vous devrez peut-être les ajouter.

  • Le partitionnement de débordement commence par une seule collection et il n'y a pas de clé de partition.

  • La collection commence à se développer, puis se développe un peu plus, puis un peu plus, jusqu'à ce que vous commenciez à vous rapprocher de la limite de 10 Go.

  • Lorsque vous atteignez une capacité de 90%, vous accédez à une nouvelle collection et commencez à l'utiliser pour de nouveaux documents.

  • Une fois que votre base de données évolue vers un plus grand nombre de collections, vous voudrez probablement passer à une stratégie basée sur une clé de partition.

  • Lorsque vous faites cela, vous devrez rééquilibrer vos données en déplaçant les documents vers différentes collections en fonction de la stratégie vers laquelle vous migrez.

Partitionnement de plage

L'une des stratégies les plus courantes est le partitionnement par plage. Avec cette approche, vous déterminez la plage de valeurs dans laquelle la clé de partition d'un document peut appartenir et dirigez le document vers une collection correspondant à cette plage.

  • Les dates sont très généralement utilisées avec cette stratégie dans laquelle vous créez une collection pour contenir des documents compris dans la plage de dates définie. Lorsque vous définissez des plages suffisamment petites, vous êtes sûr qu'aucune collection ne dépassera jamais sa limite de 10 Go. Par exemple, il peut y avoir un scénario où une seule collection peut raisonnablement traiter des documents pendant un mois entier.

  • Il se peut également que la plupart des utilisateurs demandent des données actuelles, qui seraient des données pour ce mois ou peut-être le mois dernier, mais les utilisateurs recherchent rarement des données beaucoup plus anciennes. Vous commencez donc en juin avec une collection S3, qui est la collection la plus chère que vous puissiez acheter et qui offre le meilleur débit possible.

  • En juillet, vous achetez une autre collection S3 pour stocker les données de juillet et vous réduisez également les données de juin en une collection S2 moins chère. Puis en août, vous obtenez une autre collection S3 et réduisez juillet à un S2 et juin à un S1. Cela va, mois après mois, où vous gardez toujours les données actuelles disponibles pour un débit élevé et où les données plus anciennes sont maintenues disponibles à des débits inférieurs.

  • Tant que la requête fournit une clé de partition, seule la collection qui doit être interrogée sera interrogée et pas toutes les collections de la base de données comme cela se produit avec le partitionnement par débordement.

Recherche de partitionnement

Avec le partitionnement de recherche, vous pouvez définir une mappe de partition qui achemine les documents vers des collections spécifiques en fonction de leur clé de partition. Par exemple, vous pouvez partitionner par région.

  • Stockez tous les documents américains dans une seule collection, tous les documents européens dans une autre collection et tous les documents de toute autre région dans une troisième collection.

  • Utilisez cette carte de partition et un résolveur de partition de recherche peut déterminer dans quelle collection créer un document et quelles collections interroger, en fonction de la clé de partition, qui est la propriété de région contenue dans chaque document.

Partitionnement de hachage

Dans le partitionnement de hachage, les partitions sont attribuées en fonction de la valeur d'une fonction de hachage, ce qui vous permet de répartir uniformément les demandes et les données sur un certain nombre de partitions.

Ceci est couramment utilisé pour partitionner les données produites ou consommées à partir d'un grand nombre de clients distincts, et est utile pour stocker des profils utilisateur, des éléments de catalogue, etc.

Jetons un coup d'œil à un exemple simple de partitionnement de plage à l'aide du RangePartitionResolver fourni par le SDK .NET.

Step 1- Créez un nouveau DocumentClient et nous créerons deux collections dans la tâche CreateCollections. L'un contiendra des documents pour les utilisateurs qui ont des ID utilisateur commençant par A à M et l'autre pour les ID utilisateur N à 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 - Enregistrez le résolveur de plage pour la base de données.

Step 3- Créez un nouveau RangePartitionResolver <string>, qui est le type de données de notre clé de partition. Le constructeur prend deux paramètres, le nom de propriété de la clé de partition et un dictionnaire qui est la carte de partition ou la carte de partition, qui est juste une liste des plages et des collections correspondantes que nous prédéfinissons pour le résolveur.

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

Il est nécessaire d'encoder ici la plus grande valeur UTF-8 possible. Ou bien la première plage ne correspondrait à aucun Ms sauf le seul M, et de même pour Z dans la deuxième plage. Donc, vous pouvez simplement considérer cette valeur codée ici comme un caractère générique pour la correspondance sur la clé de partition.

Step 4- Après avoir créé le résolveur, enregistrez-le pour la base de données avec le DocumentClient actuel. Pour ce faire, affectez-le simplement à la propriété de dictionnaire du PartitionResolver.

Nous créerons et interrogerons des documents par rapport à la base de données, pas une collection comme vous le faites normalement, le résolveur utilisera cette carte pour acheminer les demandes vers les collections appropriées.

Créons maintenant quelques documents. Nous allons d'abord en créer un pour userId Kirk, puis un pour 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); 
}

Le premier paramètre ici est un auto-lien vers la base de données, pas une collection spécifique. Ce n'est pas possible sans un résolveur de partition, mais avec un seul, cela fonctionne de manière transparente.

Les deux documents ont été enregistrés dans la base de données myfirstdb, mais nous savons que Kirk est stocké dans la collection pour A à M et Spock est stocké dans la collection pour N à Z, si notre RangePartitionResolver fonctionne correctement.

Appelons-les à partir de la tâche CreateDocumentClient comme indiqué dans le code suivant.

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

Lorsque le code ci-dessus est exécuté, vous recevrez la sortie suivante.

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

Comme on l'a vu, les auto-liens des deux documents ont des ID de ressources différents car ils existent dans deux collections distinctes.