DynamoDB - Index secondaires mondiaux

Les applications nécessitant différents types de requêtes avec des attributs différents peuvent utiliser un ou plusieurs index secondaires globaux pour exécuter ces requêtes détaillées.

For example - Un système gardant une trace des utilisateurs, de leur statut de connexion et de leur temps de connexion. La croissance de l'exemple précédent ralentit les requêtes sur ses données.

Les index secondaires globaux accélèrent les requêtes en organisant une sélection d'attributs à partir d'une table. Ils utilisent des clés primaires pour trier les données et ne nécessitent aucun attribut de table de clé, ni schéma de clé identique à la table.

Tous les index secondaires globaux doivent inclure une clé de partition, avec l'option d'une clé de tri. Le schéma de clé d'index peut différer de la table et les attributs de clé d'index peuvent utiliser n'importe quel attribut de chaîne, de nombre ou de table binaire de niveau supérieur.

Dans une projection, vous pouvez utiliser d'autres attributs de table, mais les requêtes ne sont pas extraites des tables parentes.

Projections d'attributs

Les projections consistent en un jeu d'attributs copié de la table vers l'index secondaire. Une projection se produit toujours avec la clé de partition de table et la clé de tri. Dans les requêtes, les projections permettent à DynamoDB d'accéder à n'importe quel attribut de la projection; ils existent essentiellement comme leur propre table.

Dans une création d'index secondaire, vous devez spécifier des attributs pour la projection. DynamoDB propose trois méthodes pour effectuer cette tâche -

  • KEYS_ONLY- Tous les éléments d'index se composent de la partition de table, des valeurs de clé de tri et des valeurs de clé d'index. Cela crée le plus petit index.

  • INCLUDE - Il comprend les attributs KEYS_ONLY et les attributs non clés spécifiés.

  • ALL - Il inclut tous les attributs de la table source, créant ainsi le plus grand index possible.

Notez les compromis dans la projection des attributs dans un index secondaire global, qui sont liés au débit et au coût de stockage.

Considérez les points suivants -

  • Si vous n'avez besoin que d'accéder à quelques attributs, avec une faible latence, projetez uniquement ceux dont vous avez besoin. Cela réduit les coûts de stockage et d'écriture.

  • Si une application accède fréquemment à certains attributs non clés, projetez-les car les coûts de stockage sont pâles par rapport à la consommation d'analyse.

  • Vous pouvez projeter de grands ensembles d'attributs fréquemment utilisés, mais cela entraîne un coût de stockage élevé.

  • Utilisez KEYS_ONLY pour les requêtes de table peu fréquentes et les écritures / mises à jour fréquentes. Cela contrôle la taille, mais offre toujours de bonnes performances sur les requêtes.

Requêtes et analyses d'index secondaire global

Vous pouvez utiliser des requêtes pour accéder à un ou plusieurs éléments dans un index. Vous devez spécifier le nom de l'index et de la table, les attributs souhaités et les conditions; avec la possibilité de renvoyer les résultats par ordre croissant ou décroissant.

Vous pouvez également utiliser des analyses pour obtenir toutes les données d'index. Il nécessite un nom de table et d'index. Vous utilisez une expression de filtre pour récupérer des données spécifiques.

Synchronisation des données de table et d'index

DynamoDB effectue automatiquement la synchronisation des index avec leur table parent. Chaque opération de modification sur les éléments entraîne des mises à jour asynchrones, cependant, les applications n'écrivent pas directement dans les index.

Vous devez comprendre l'impact de la maintenance DynamoDB sur les index. Lors de la création d'un index, vous spécifiez des attributs clés et des types de données, ce qui signifie que lors d'une écriture, ces types de données doivent correspondre aux types de données de schéma clés.

Lors de la création ou de la suppression d'un élément, les index sont mis à jour de manière cohérente, cependant, les mises à jour des données se propagent en une fraction de seconde (à moins qu'une défaillance du système d'un certain type ne se produise). Vous devez tenir compte de ce retard dans les candidatures.

Throughput Considerations in Global Secondary Indexes- Plusieurs index secondaires globaux ont un impact sur le débit. La création d'index nécessite des spécifications d'unité de capacité, qui existent séparément de la table, ce qui entraîne des opérations consommant des unités de capacité d'index plutôt que des unités de table.

Cela peut entraîner une limitation si une requête ou une écriture dépasse le débit provisionné. Afficher les paramètres de débit à l'aide deDescribeTable.

Read Capacity- Les index secondaires globaux offrent une cohérence éventuelle. Dans les requêtes, DynamoDB effectue des calculs de provisionnement identiques à ceux utilisés pour les tables, avec une seule différence d'utiliser la taille de l'entrée d'index plutôt que la taille de l'élément. La limite des retours de requête reste 1 Mo, ce qui inclut la taille et les valeurs du nom d'attribut pour chaque élément retourné.

Capacité d'écriture

Lorsque des opérations d'écriture se produisent, l'index affecté consomme des unités d'écriture. Les coûts de débit d'écriture sont la somme des unités de capacité d'écriture consommées dans les écritures de table et des unités consommées dans les mises à jour d'index. Une opération d'écriture réussie nécessite une capacité suffisante ou entraîne une limitation.

Les coûts d'écriture restent également dépendants de certains facteurs, dont certains sont les suivants:

  • Les nouveaux éléments définissant des attributs indexés ou des mises à jour d'éléments définissant des attributs indexés non définis utilisent une seule opération d'écriture pour ajouter l'élément à l'index.

  • Les mises à jour modifiant la valeur d'attribut de clé indexée utilisent deux écritures pour supprimer un élément et en écrire un nouveau.

  • Une écriture de table déclenchant la suppression d'un attribut indexé utilise une seule écriture pour effacer l'ancienne projection d'élément dans l'index.

  • Les éléments absents de l'index avant et après une opération de mise à jour n'utilisent aucune écriture.

  • Les mises à jour modifiant uniquement la valeur d'attribut projetée dans le schéma de clé d'index, et non la valeur d'attribut de clé indexée, utilisent une écriture pour mettre à jour les valeurs des attributs projetés dans l'index.

Tous ces facteurs supposent une taille d'élément inférieure ou égale à 1 Ko.

Stockage d'index secondaire global

Lors de l'écriture d'un élément, DynamoDB copie automatiquement le bon ensemble d'attributs dans tous les index où les attributs doivent exister. Cela a un impact sur votre compte en le facturant pour le stockage des éléments de table et le stockage des attributs. L'espace utilisé résulte de la somme de ces quantités -

  • Taille d'octet de la clé primaire de la table
  • Taille d'octet de l'attribut de clé d'index
  • Taille en octets des attributs projetés
  • 100 octets de surcharge par élément d'index

Vous pouvez estimer les besoins de stockage en estimant la taille moyenne des éléments et en multipliant par la quantité des éléments de table avec les attributs de clé d'index secondaire global.

DynamoDB n'écrit pas de données d'élément pour un élément de table avec un attribut non défini défini comme une partition d'index ou une clé de tri.

Global Secondary Index Crud

Créez une table avec des index secondaires globaux à l'aide de la CreateTable opération associée au GlobalSecondaryIndexesparamètre. Vous devez spécifier un attribut pour servir de clé de partition d'index ou en utiliser un autre pour la clé de tri d'index. Tous les attributs de clé d'index doivent être des chaînes, des nombres ou des scalaires binaires. Vous devez également fournir des paramètres de débit, consistant enReadCapacityUnits et WriteCapacityUnits.

Utilisation UpdateTable pour ajouter à nouveau des index secondaires globaux aux tables existantes à l'aide du paramètre GlobalSecondaryIndexes.

Dans cette opération, vous devez fournir les entrées suivantes -

  • Nom de l'index
  • Schéma clé
  • Attributs projetés
  • Paramètres de débit

L'ajout d'un index secondaire global peut prendre beaucoup de temps avec des tables volumineuses en raison du volume d'éléments, du volume d'attributs projeté, de la capacité d'écriture et de l'activité d'écriture. UtilisationCloudWatch métriques pour surveiller le processus.

Utilisation DescribeTablepour récupérer les informations d'état d'un index secondaire global. Il renvoie l'un des quatreIndexStatus pour les GlobalSecondaryIndexes -

  • CREATING - Il indique la phase de construction de l'index et son indisponibilité.

  • ACTIVE - Il indique que l'index est prêt à être utilisé.

  • UPDATING - Il indique l'état de mise à jour des paramètres de débit.

  • DELETING - Il indique le statut de suppression de l'index, et son indisponibilité permanente pour l'utilisation.

Mettez à jour les paramètres de débit provisionné de l'index secondaire global pendant l'étape de chargement / remplissage (DynamoDB écrivant les attributs dans un index et suivant les éléments ajoutés / supprimés / mis à jour). UtilisationUpdateTable pour effectuer cette opération.

N'oubliez pas que vous ne pouvez pas ajouter / supprimer d'autres index pendant l'étape de remblayage.

Utilisez UpdateTable pour supprimer les index secondaires globaux. Il permet la suppression d'un seul index par opération, mais vous pouvez exécuter plusieurs opérations simultanément, jusqu'à cinq. Le processus de suppression n'affecte pas les activités de lecture / écriture de la table parent, mais vous ne pouvez pas ajouter / supprimer d'autres index tant que l'opération n'est pas terminée.

Utilisation de Java pour travailler avec des index secondaires globaux

Créez une table avec un index via CreateTable. Créez simplement une instance de classe DynamoDB, unCreateTableRequest classe pour les informations de requête et transmettez l'objet de requête à la méthode CreateTable.

Le programme suivant est un court exemple -

DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient ( 
   new ProfileCredentialsProvider()));
   
// Attributes 
ArrayList<AttributeDefinition> attributeDefinitions = new 
   ArrayList<AttributeDefinition>();  
attributeDefinitions.add(new AttributeDefinition() 
   .withAttributeName("City") 
   .withAttributeType("S"));
   
attributeDefinitions.add(new AttributeDefinition() 
   .withAttributeName("Date") 
   .withAttributeType("S"));
   
attributeDefinitions.add(new AttributeDefinition() 
   .withAttributeName("Wind") 
   .withAttributeType("N"));
   
// Key schema of the table 
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>(); 
tableKeySchema.add(new KeySchemaElement()
   .withAttributeName("City") 
   .withKeyType(KeyType.HASH));              //Partition key
   
tableKeySchema.add(new KeySchemaElement() 
   .withAttributeName("Date") 
   .withKeyType(KeyType.RANGE));             //Sort key
   
// Wind index 
GlobalSecondaryIndex windIndex = new GlobalSecondaryIndex() 
   .withIndexName("WindIndex") 
   .withProvisionedThroughput(new ProvisionedThroughput() 
   .withReadCapacityUnits((long) 10) 
   .withWriteCapacityUnits((long) 1)) 
   .withProjection(new Projection().withProjectionType(ProjectionType.ALL));
   
ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>(); 
indexKeySchema.add(new KeySchemaElement() 
   .withAttributeName("Date") 
   .withKeyType(KeyType.HASH));              //Partition key
   
indexKeySchema.add(new KeySchemaElement() 
   .withAttributeName("Wind") 
   .withKeyType(KeyType.RANGE));             //Sort key
   
windIndex.setKeySchema(indexKeySchema);  
CreateTableRequest createTableRequest = new CreateTableRequest() 
   .withTableName("ClimateInfo") 
   .withProvisionedThroughput(new ProvisionedThroughput() 
   .withReadCapacityUnits((long) 5) 
   .withWriteCapacityUnits((long) 1))
   .withAttributeDefinitions(attributeDefinitions) 
   .withKeySchema(tableKeySchema) 
   .withGlobalSecondaryIndexes(windIndex); 
Table table = dynamoDB.createTable(createTableRequest); 
System.out.println(table.getDescription());

Récupérez les informations d'index avec DescribeTable. Tout d'abord, créez une instance de classe DynamoDB. Créez ensuite une instance de classe Table pour cibler un index. Enfin, passez le tableau à la méthode describe.

Voici un petit exemple -

DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient ( 
   new ProfileCredentialsProvider()));
   
Table table = dynamoDB.getTable("ClimateInfo"); 
TableDescription tableDesc = table.describe();  
Iterator<GlobalSecondaryIndexDescription> gsiIter = 
   tableDesc.getGlobalSecondaryIndexes().iterator(); 

while (gsiIter.hasNext()) { 
   GlobalSecondaryIndexDescription gsiDesc = gsiIter.next(); 
   System.out.println("Index data " + gsiDesc.getIndexName() + ":");  
   Iterator<KeySchemaElement> kse7Iter = gsiDesc.getKeySchema().iterator(); 
   
   while (kseIter.hasNext()) { 
      KeySchemaElement kse = kseIter.next(); 
      System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType()); 
   }
   Projection projection = gsiDesc.getProjection(); 
   System.out.println("\tProjection type: " + projection.getProjectionType()); 
   
   if (projection.getProjectionType().toString().equals("INCLUDE")) { 
      System.out.println("\t\tNon-key projected attributes: " 
         + projection.getNonKeyAttributes()); 
   } 
}

Utilisez Query pour effectuer une requête d'index comme avec une requête de table. Créez simplement une instance de classe DynamoDB, une instance de classe Table pour l'index cible, une instance de classe Index pour l'index spécifique et transmettez l'index et l'objet de requête à la méthode de requête.

Jetez un œil au code suivant pour mieux comprendre -

DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient ( 
   new ProfileCredentialsProvider()));
   
Table table = dynamoDB.getTable("ClimateInfo"); 
Index index = table.getIndex("WindIndex");  
QuerySpec spec = new QuerySpec() 
   .withKeyConditionExpression("#d = :v_date and Wind = :v_wind") 
   .withNameMap(new NameMap() 
   .with("#d", "Date"))
   .withValueMap(new ValueMap() 
   .withString(":v_date","2016-05-15") 
   .withNumber(":v_wind",0));
   
ItemCollection<QueryOutcome> items = index.query(spec);
Iterator<Item> iter = items.iterator();

while (iter.hasNext()) {
   System.out.println(iter.next().toJSONPretty()); 
}

Le programme suivant est un plus grand exemple pour une meilleure compréhension -

Note- Le programme suivant peut supposer une source de données précédemment créée. Avant de tenter de l'exécuter, achetez les bibliothèques de prise en charge et créez les sources de données nécessaires (tables avec les caractéristiques requises ou autres sources référencées).

Cet exemple utilise également Eclipse IDE, un fichier d'informations d'identification AWS et AWS Toolkit dans un projet Eclipse AWS Java.

import java.util.ArrayList;
import java.util.Iterator;

import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Index;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
import com.amazonaws.services.dynamodbv2.document.QueryOutcome;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.spec.QuerySpec;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;

import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;

public class GlobalSecondaryIndexSample {  
   static DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient ( 
      new ProfileCredentialsProvider()));  
   public static String tableName = "Bugs";   
   public static void main(String[] args) throws Exception {  
      createTable(); 
      queryIndex("CreationDateIndex"); 
      queryIndex("NameIndex"); 
      queryIndex("DueDateIndex"); 
   }
   public static void createTable() {  
      // Attributes 
      ArrayList<AttributeDefinition> attributeDefinitions = new 
         ArrayList<AttributeDefinition>();  
      attributeDefinitions.add(new AttributeDefinition()
         .withAttributeName("BugID") 
         .withAttributeType("S")); 
         
      attributeDefinitions.add(new AttributeDefinition() 
         .withAttributeName("Name")
         .withAttributeType("S"));
         
      attributeDefinitions.add(new AttributeDefinition() 
         .withAttributeName("CreationDate")
         .withAttributeType("S"));
         
      attributeDefinitions.add(new AttributeDefinition() 
         .withAttributeName("DueDate") 
         .withAttributeType("S"));
         
      // Table Key schema
      ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>(); 
      tableKeySchema.add (new KeySchemaElement() 
         .withAttributeName("BugID") 
         .withKeyType(KeyType.HASH));              //Partition key 
      
      tableKeySchema.add (new KeySchemaElement() 
         .withAttributeName("Name") 
         .withKeyType(KeyType.RANGE));             //Sort key
         
      // Indexes' initial provisioned throughput
      ProvisionedThroughput ptIndex = new ProvisionedThroughput()
         .withReadCapacityUnits(1L)
         .withWriteCapacityUnits(1L);
         
      // CreationDateIndex 
      GlobalSecondaryIndex creationDateIndex = new GlobalSecondaryIndex() 
         .withIndexName("CreationDateIndex") 
         .withProvisionedThroughput(ptIndex) 
         .withKeySchema(new KeySchemaElement() 
         .withAttributeName("CreationDate") 
         .withKeyType(KeyType.HASH),               //Partition key 
         new KeySchemaElement()
         .withAttributeName("BugID") 
         .withKeyType(KeyType.RANGE))              //Sort key 
         .withProjection(new Projection() 
         .withProjectionType("INCLUDE") 
         .withNonKeyAttributes("Description", "Status"));
         
      // NameIndex 
      GlobalSecondaryIndex nameIndex = new GlobalSecondaryIndex() 
         .withIndexName("NameIndex") 
         .withProvisionedThroughput(ptIndex) 
         .withKeySchema(new KeySchemaElement()  
         .withAttributeName("Name")  
         .withKeyType(KeyType.HASH),                  //Partition key 
         new KeySchemaElement()  
         .withAttributeName("BugID")  
         .withKeyType(KeyType.RANGE))                 //Sort key 
         .withProjection(new Projection() 
         .withProjectionType("KEYS_ONLY"));
         
      // DueDateIndex 
      GlobalSecondaryIndex dueDateIndex = new GlobalSecondaryIndex() 
         .withIndexName("DueDateIndex") 
         .withProvisionedThroughput(ptIndex) 
         .withKeySchema(new KeySchemaElement() 
         .withAttributeName("DueDate") 
         .withKeyType(KeyType.HASH))               //Partition key 
         .withProjection(new Projection() 
         .withProjectionType("ALL"));
         
      CreateTableRequest createTableRequest = new CreateTableRequest() 
         .withTableName(tableName) 
         .withProvisionedThroughput( new ProvisionedThroughput() 
         .withReadCapacityUnits( (long) 1) 
         .withWriteCapacityUnits( (long) 1)) 
         .withAttributeDefinitions(attributeDefinitions)
         .withKeySchema(tableKeySchema)
         .withGlobalSecondaryIndexes(creationDateIndex, nameIndex, dueDateIndex);  
         System.out.println("Creating " + tableName + "..."); 
         dynamoDB.createTable(createTableRequest);  
      
      // Pause for active table state 
      System.out.println("Waiting for ACTIVE state of " + tableName); 
      try { 
         Table table = dynamoDB.getTable(tableName); 
         table.waitForActive(); 
      } catch (InterruptedException e) { 
         e.printStackTrace(); 
      } 
   }
   public static void queryIndex(String indexName) { 
      Table table = dynamoDB.getTable(tableName);  
      System.out.println 
      ("\n*****************************************************\n"); 
      System.out.print("Querying index " + indexName + "...");  
      Index index = table.getIndex(indexName);  
      ItemCollection<QueryOutcome> items = null; 
      QuerySpec querySpec = new QuerySpec();  
      
      if (indexName == "CreationDateIndex") { 
         System.out.println("Issues filed on 2016-05-22"); 
         querySpec.withKeyConditionExpression("CreationDate = :v_date and begins_with
            (BugID, :v_bug)") 
            .withValueMap(new ValueMap() 
            .withString(":v_date","2016-05-22")
            .withString(":v_bug","A-")); 
         items = index.query(querySpec); 
      } else if (indexName == "NameIndex") { 
         System.out.println("Compile error"); 
         querySpec.withKeyConditionExpression("Name = :v_name and begins_with
            (BugID, :v_bug)") 
            .withValueMap(new ValueMap() 
            .withString(":v_name","Compile error") 
            .withString(":v_bug","A-")); 
         items = index.query(querySpec); 
      } else if (indexName == "DueDateIndex") { 
         System.out.println("Items due on 2016-10-15"); 
         querySpec.withKeyConditionExpression("DueDate = :v_date") 
         .withValueMap(new ValueMap() 
         .withString(":v_date","2016-10-15")); 
         items = index.query(querySpec); 
      } else { 
         System.out.println("\nInvalid index name"); 
         return; 
      }  
      Iterator<Item> iterator = items.iterator(); 
      System.out.println("Query: getting result..."); 
      
      while (iterator.hasNext()) { 
         System.out.println(iterator.next().toJSONPretty()); 
      } 
   } 
}