DynamoDB - Index secondaires locaux

Certaines applications n'effectuent des requêtes qu'avec la clé primaire, mais certaines situations bénéficient d'une autre clé de tri. Donnez à votre application un choix en créant un ou plusieurs index secondaires locaux.

Des exigences d'accès aux données complexes, telles que le peignage de millions d'éléments, rendent nécessaire d'effectuer des requêtes / analyses plus efficaces. Les index secondaires locaux fournissent une clé de tri alternative pour une valeur de clé de partition. Ils contiennent également des copies de tout ou partie des attributs de table. Ils organisent les données par clé de partition de table, mais utilisent une clé de tri différente.

L'utilisation d'un index secondaire local supprime la nécessité d'une analyse complète de la table et permet une requête simple et rapide à l'aide d'une clé de tri.

Tous les index secondaires locaux doivent satisfaire certaines conditions -

  • Clé de partition et clé de partition de table source identiques.
  • Une clé de tri d'un seul attribut scalaire.
  • Projection de la clé de tri de la table source agissant comme un attribut non clé.

Tous les index secondaires locaux contiennent automatiquement les clés de partition et de tri des tables parentes. Dans les requêtes, cela signifie une collecte efficace des attributs projetés, ainsi que la récupération des attributs non projetés.

La limite de stockage pour un index secondaire local reste de 10 Go par valeur de clé de partition, qui comprend tous les éléments de table et les éléments d'index partageant une valeur de clé de partition.

Projeter un attribut

Certaines opérations nécessitent des lectures / extractions excessives en raison de leur complexité. Ces opérations peuvent consommer un débit important. La projection vous permet d'éviter une récupération coûteuse et d'effectuer des requêtes riches en isolant ces attributs. N'oubliez pas que les projections consistent en des attributs copiés dans un index secondaire.

Lors de la création d'un index secondaire, vous spécifiez les attributs projetés. Rappelez-vous les trois options fournies par DynamoDB:KEYS_ONLY, INCLUDE, and ALL.

Lorsque vous optez pour certains attributs dans la projection, tenez compte des compromis de coût associés -

  • Si vous ne projetez qu'un petit ensemble d'attributs nécessaires, vous réduisez considérablement les coûts de stockage.

  • Si vous projetez des attributs non clés fréquemment utilisés, vous compensez les coûts d'analyse par les coûts de stockage.

  • Si vous projetez la plupart ou tous les attributs non clés, cela maximise la flexibilité et réduit le débit (pas de récupération); cependant, les coûts de stockage augmentent.

  • Si vous projetez KEYS_ONLY pour les écritures / mises à jour fréquentes et les requêtes peu fréquentes, cela minimise la taille, mais maintient la préparation des requêtes.

Création d'index secondaire local

Utilisez le LocalSecondaryIndexparamètre de CreateTable pour créer un ou plusieurs index secondaires locaux. Vous devez spécifier un attribut non clé pour la clé de tri. Lors de la création de table, vous créez des index secondaires locaux. Lors de la suppression, vous supprimez ces index.

Les tables avec un index secondaire local doivent obéir à une limite de 10 Go de taille par valeur de clé de partition, mais peuvent stocker n'importe quelle quantité d'éléments.

Requêtes et analyses d'index secondaire local

Une opération de requête sur les index secondaires locaux renvoie tous les éléments avec une valeur de clé de partition correspondante lorsque plusieurs éléments de l'index partagent les valeurs de clé de tri. Les articles correspondants ne reviennent pas dans un certain ordre. Les requêtes pour les index secondaires locaux utilisent une cohérence éventuelle ou forte, avec des lectures fortement cohérentes fournissant les dernières valeurs.

Une opération d'analyse renvoie toutes les données d'index secondaires locales. Les analyses nécessitent que vous fournissiez un nom de table et d'index et que vous autorisiez l'utilisation d'une expression de filtre pour supprimer les données.

Rédaction d'articles

Lors de la création d'un index secondaire local, vous spécifiez un attribut de clé de tri et son type de données. Lorsque vous écrivez un élément, son type doit correspondre au type de données du schéma de clé si l'élément définit un attribut d'une clé d'index.

DynamoDB n'impose aucune exigence de relation un à un sur les éléments de table et les éléments d'index secondaire local. Les tables avec plusieurs index secondaires locaux supportent des coûts d'écriture plus élevés que celles avec moins.

Considérations relatives au débit dans les index secondaires locaux

La consommation de capacité de lecture d'une requête dépend de la nature de l'accès aux données. Les requêtes utilisent une cohérence éventuelle ou forte, avec des lectures fortement cohérentes utilisant une unité contre une demi-unité dans des lectures finalement cohérentes.

Les limitations de résultats incluent une taille maximale de 1 Mo. Les tailles de résultat proviennent de la somme de la taille d'élément d'index correspondante arrondie au 4 Ko le plus proche, et de la taille d'élément de table correspondante également arrondie au 4 Ko le plus proche.

La consommation de capacité d'écriture reste dans les unités provisionnées. Calculez le coût total provisionné en trouvant la somme des unités consommées dans l'écriture de table et des unités consommées lors de la mise à jour des index.

Vous pouvez également tenir compte des facteurs clés influençant le coût, dont certains peuvent être -

  • Lorsque vous écrivez un élément définissant un attribut indexé ou mettez à jour un élément pour définir un attribut indexé non défini, une seule opération d'écriture se produit.

  • Lorsqu'une mise à jour de table modifie une valeur d'attribut de clé indexée, deux écritures se produisent pour supprimer puis - ajouter un élément.

  • Lorsqu'une écriture entraîne la suppression d'un attribut indexé, une écriture se produit pour supprimer l'ancienne projection d'élément.

  • Lorsqu'un élément n'existe pas dans l'index avant ou après une mise à jour, aucune écriture n'a lieu.

Stockage d'index secondaire local

Lors d'une écriture d'élément de table, DynamoDB copie automatiquement le bon jeu d'attributs dans les index secondaires locaux requis. Cela charge votre compte. L'espace utilisé résulte de la somme de la taille d'octet de clé primaire de table, de la taille d'octet d'attribut de clé d'index, de toute taille d'octet d'attribut projetée actuelle et de 100 octets de surcharge pour chaque élément d'index.

Le stockage estimé est obtenu en estimant la taille d'élément d'index moyenne et en multipliant par la quantité d'élément de table.

Utilisation de Java pour travailler avec des index secondaires locaux

Créez un index secondaire local en créant d'abord une instance de classe DynamoDB. Ensuite, créez une instance de classe CreateTableRequest avec les informations de demande nécessaires. Enfin, utilisez la méthode createTable.

Exemple

DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient( 
   new ProfileCredentialsProvider()));
String tableName = "Tools";  
CreateTableRequest createTableRequest = new 
   CreateTableRequest().withTableName(tableName);
   
//Provisioned Throughput
createTableRequest.setProvisionedThroughput (
   new ProvisionedThroughput()
   .withReadCapacityUnits((long)5)
   .withWriteCapacityUnits(( long)5));
   
//Attributes 
ArrayList<AttributeDefinition> attributeDefinitions = 
   new ArrayList<AttributeDefinition>();
   attributeDefinitions.add(new AttributeDefinition()
   .withAttributeName("Make")
   .withAttributeType("S"));
   
attributeDefinitions.add(new AttributeDefinition()
   .withAttributeName("Model")
   .withAttributeType("S"));
   
attributeDefinitions.add(new AttributeDefinition()
   .withAttributeName("Line")
   .withAttributeType("S"));
   
createTableRequest.setAttributeDefinitions(attributeDefinitions);

//Key Schema 
ArrayList<KeySchemaElement> tableKeySchema = new 
   ArrayList<KeySchemaElement>();
   
tableKeySchema.add(new KeySchemaElement()
   .withAttributeName("Make")
   .withKeyType(KeyType.HASH));                    //Partition key
   
tableKeySchema.add(new KeySchemaElement()
   .withAttributeName("Model")
   .withKeyType(KeyType.RANGE));                   //Sort key
   
createTableRequest.setKeySchema(tableKeySchema);
ArrayList<KeySchemaElement> indexKeySchema = new 
   ArrayList<KeySchemaElement>();
   
indexKeySchema.add(new KeySchemaElement()
   .withAttributeName("Make")
   .withKeyType(KeyType.HASH));                   //Partition key
   
indexKeySchema.add(new KeySchemaElement()
   .withAttributeName("Line")
   .withKeyType(KeyType.RANGE));                   //Sort key
   
Projection projection = new Projection()
   .withProjectionType(ProjectionType.INCLUDE);

ArrayList<String> nonKeyAttributes = new ArrayList<String>(); 
nonKeyAttributes.add("Type"); 
nonKeyAttributes.add("Year"); 
projection.setNonKeyAttributes(nonKeyAttributes);  

LocalSecondaryIndex localSecondaryIndex = new LocalSecondaryIndex() 
   .withIndexName("ModelIndex")
   .withKeySchema(indexKeySchema)
   .withProjection(p rojection);  

ArrayList<LocalSecondaryIndex> localSecondaryIndexes = new 
   ArrayList<LocalSecondaryIndex>(); 

localSecondaryIndexes.add(localSecondaryIndex); 
createTableRequest.setLocalSecondaryIndexes(localSecondaryIndexes);  
Table table = dynamoDB.createTable(createTableRequest); 
System.out.println(table.getDescription());

Récupérez des informations sur un index secondaire local avec la méthode describe. Créez simplement une instance de classe DynamoDB, créez une instance de classe Table et transmettez la table à la méthode describe.

Exemple

DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient( 
   new ProfileCredentialsProvider()));
   
String tableName = "Tools";
Table table = dynamoDB.getTable(tableName);
TableDescription tableDescription = table.describe();

List<LocalSecondaryIndexDescription> localSecondaryIndexes = 
   tableDescription.getLocalSecondaryIndexes();
   
Iterator<LocalSecondaryIndexDescription> lsiIter = 
   localSecondaryIndexes.iterator();
   
while (lsiIter.hasNext()) {  
   LocalSecondaryIndexDescription lsiDescription = lsiIter.next(); 
   System.out.println("Index info " + lsiDescription.getIndexName() + ":"); 
   Iterator<KeySchemaElement> kseIter = lsiDescription.getKeySchema().iterator(); 
   
   while (kseIter.hasNext()) { 
      KeySchemaElement kse = kseIter.next(); 
      System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType()); 
   }
   
   Projection projection = lsiDescription.getProjection(); 
   System.out.println("\tProjection type: " + projection.getProjectionType()); 
   
   if (projection.getProjectionType().toString().equals("INCLUDE")) { 
      System.out.println("\t\tNon-key projected attributes: " + 
         projection.getNonKeyAttributes()); 
   } 
}

Exécutez une requête en utilisant les mêmes étapes qu'une requête de table. Créez simplement une instance de classe DynamoDB, une instance de classe Table, une instance de classe Index, un objet de requête et utilisez la méthode de requête.

Exemple

DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient( 
   new ProfileCredentialsProvider()));
   
String tableName = "Tools";  
Table table = dynamoDB.getTable(tableName); 
Index index = table.getIndex("LineIndex");  
QuerySpec spec = new QuerySpec() 
   .withKeyConditionExpression("Make = :v_make and Line = :v_line") 
   .withValueMap(new ValueMap() 
   .withString(":v_make", "Depault") 
   .withString(":v_line", "SuperSawz"));
      
ItemCollection<QueryOutcome> items = index.query(spec);
Iterator<Item> itemsIter = items.iterator();

while (itemsIter.hasNext()) { 
   Item item = itemsIter.next(); 
   System.out.println(item.toJSONPretty()); 
}

Vous pouvez également consulter l'exemple suivant.

Note- L'exemple suivant peut supposer une source de données précédemment créée. Avant de tenter de l'exécuter, acquérez 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).

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

Exemple

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.PutItemOutcome;
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.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.LocalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProjectionType;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
import com.amazonaws.services.dynamodbv2.model.ReturnConsumedCapacity;
import com.amazonaws.services.dynamodbv2.model.Select;

public class LocalSecondaryIndexSample {  
   static DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient( 
      new ProfileCredentialsProvider()));  
   public static String tableName = "ProductOrders";  
   
   public static void main(String[] args) throws Exception {  
      createTable();
      query(null); 
      query("IsOpenIndex"); 
      query("OrderCreationDateIndex"); 
   }
   public static void createTable() { 
      CreateTableRequest createTableRequest = new CreateTableRequest() 
         .withTableName(tableName) 
         .withProvisionedThroughput(new ProvisionedThroughput() 
         .withReadCapacityUnits((long) 1) 
         .withWriteCapacityUnits((long) 1));
         
      // Table partition and sort keys attributes 
      ArrayList<AttributeDefinition> attributeDefinitions = new 
         ArrayList<AttributeDefinition>(); 
      
      attributeDefinitions.add(new AttributeDefinition() 
         .withAttributeName("CustomerID") 
         .withAttributeType("S"));
         
      attributeDefinitions.add(new AttributeDefinition() 
         .withAttributeName("OrderID") 
         .withAttributeType("N"));
         
      // Index primary key attributes 
      attributeDefinitions.add(new AttributeDefinition() 
         .withAttributeName("OrderDate") 
         .withAttributeType("N"));
         
      attributeDefinitions.add(new AttributeDefinition() 
         .withAttributeName("OpenStatus") 
         .withAttributeType("N"));  
      createTableRequest.setAttributeDefinitions(attributeDefinitions);
      
      // Table key schema 
      ArrayList<KeySchemaElement> tableKeySchema = new
         ArrayList<KeySchemaElement>(); 
      tableKeySchema.add(new KeySchemaElement()  
         .withAttributeName("CustomerID") 
         .withKeyType(KeyType.HASH));                    //Partition key
         
      tableKeySchema.add(new KeySchemaElement() 
         .withAttributeName("OrderID") 
         .withKeyType(KeyType.RANGE));                   //Sort key
         
      createTableRequest.setKeySchema(tableKeySchema);  
      ArrayList<LocalSecondaryIndex> localSecondaryIndexes = new 
         ArrayList<LocalSecondaryIndex>();  
      
      // OrderDateIndex 
      LocalSecondaryIndex orderDateIndex = new LocalSecondaryIndex() 
         .withIndexName("OrderDateIndex");
         
      // OrderDateIndex key schema 
      ArrayList<KeySchemaElement> indexKeySchema = new 
         ArrayList<KeySchemaElement>(); 
      indexKeySchema.add(new KeySchemaElement() 
         .withAttributeName("CustomerID") 
         .withKeyType(KeyType.HASH));                   //Partition key
         
      indexKeySchema.add(new KeySchemaElement() 
         .withAttributeName("OrderDate") 
         .withKeyType(KeyType.RANGE));                   //Sort key
      orderDateIndex.setKeySchema(indexKeySchema);
      
      // OrderCreationDateIndex projection w/attributes list 
      Projection projection = new Projection() 
         .withProjectionType(ProjectionType.INCLUDE); 
      
      ArrayList<String> nonKeyAttributes = new ArrayList<String>(); 
      nonKeyAttributes.add("ProdCat"); 
      nonKeyAttributes.add("ProdNomenclature"); 
      projection.setNonKeyAttributes(nonKeyAttributes);
      orderCreationDateIndex.setProjection(projection);  
      localSecondaryIndexes.add(orderDateIndex);  
      
      // IsOpenIndex 
      LocalSecondaryIndex isOpenIndex = new LocalSecondaryIndex() 
         .withIndexName("IsOpenIndex");  
      
      // OpenStatusIndex key schema 
      indexKeySchema = new ArrayList<KeySchemaElement>(); 
      indexKeySchema.add(new KeySchemaElement() 
         .withAttributeName("CustomerID") 
         .withKeyType(KeyType.HASH));                   //Partition key
         
      indexKeySchema.add(new KeySchemaElement() 
         .withAttributeName("OpenStatus") 
         .withKeyType(KeyType.RANGE));                   //Sort key
         
      // OpenStatusIndex projection 
      projection = new Projection() .withProjectionType(ProjectionType.ALL);  
      OpenStatusIndex.setKeySchema(indexKeySchema); 
      OpenStatusIndex.setProjection(projection);  
      localSecondaryIndexes.add(OpenStatusIndex);  
      
      // Put definitions in CreateTable request 
      createTableRequest.setLocalSecondaryIndexes(localSecondaryIndexes);  
      System.out.println("Spawning table " + tableName + "..."); 
      System.out.println(dynamoDB.createTable(createTableRequest));  
      
      // Pause for ACTIVE status 
      System.out.println("Waiting for ACTIVE table:" + tableName); 
      try { 
         Table table = dynamoDB.getTable(tableName);
         table.waitForActive(); 
      } catch (InterruptedException e) { 
         e.printStackTrace(); 
      } 
   }
   public static void query(String indexName) {  
      Table table = dynamoDB.getTable(tableName);  
      System.out.println("\n*************************************************\n"); 
      System.out.println("Executing query on" + tableName);  
      QuerySpec querySpec = new QuerySpec() 
         .withConsistentRead(true) 
         .withScanIndexForward(true) 
         .withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL);
      
      if (indexName == "OpenStatusIndex") {  
         System.out.println("\nEmploying index: '" + indexName 
            + "' open orders for this customer.");
            
         System.out.println( 
            "Returns only user-specified attribute list\n"); 
         Index index = table.getIndex(indexName); 
             
         querySpec.withKeyConditionExpression("CustomerID = :v_custmid and 
            OpenStatus = :v_openstat") 
            .withValueMap(new ValueMap() 
            .withString(":v_custmid", "[email protected]") 
            .withNumber(":v_openstat", 1));  
         
         querySpec.withProjectionExpression( 
            "OrderDate, ProdCat, ProdNomenclature, OrderStatus"); 
            ItemCollection<QueryOutcome> items = index.query(querySpec); 
            Iterator<Item> iterator = items.iterator();  
            System.out.println("Printing query results...");  
            
         while (iterator.hasNext()) { 
            System.out.println(iterator.next().toJSONPretty()); 
         }  
      } else if (indexName == "OrderDateIndex") { 
         System.out.println("\nUsing index: '" + indexName 
            + "': this customer's orders placed after 05/22/2016."); 
         System.out.println("Projected attributes are returned\n"); 
         Index index = table.getIndex(indexName); 
             
         querySpec.withKeyConditionExpression("CustomerID = :v_custmid and OrderDate 
            >= :v_ordrdate") 
            .withValueMap(new ValueMap() 
            .withString(":v_custmid", "[email protected]") 
            .withNumber(":v_ordrdate", 20160522));
               
         querySpec.withSelect(Select.ALL_PROJECTED_ATTRIBUTES);  
         ItemCollection<QueryOutcome> items = index.query(querySpec); 
         Iterator<Item> iterator = items.iterator();  
         System.out.println("Printing query results...");  
            
         while (iterator.hasNext()) { 
            System.out.println(iterator.next().toJSONPretty()); 
         }  
      } else { 
         System.out.println("\nNo index: All Jane's orders by OrderID:\n"); 
         querySpec.withKeyConditionExpression("CustomerID = :v_custmid") 
            .withValueMap(new ValueMap()
            .withString(":v_custmid", "[email protected]"));  
         
         ItemCollection<QueryOutcome> items = table.query(querySpec); 
         Iterator<Item> iterator = items.iterator();  
         System.out.println("Printing query results...");  
         
         while (iterator.hasNext()) { 
            System.out.println(iterator.next().toJSONPretty()); 
         } 
      } 
   } 
}