DynamoDB - Indici secondari locali

Alcune applicazioni eseguono solo query con la chiave primaria, ma alcune situazioni traggono vantaggio da una chiave di ordinamento alternativa. Consenti alla tua applicazione una scelta creando uno o più indici secondari locali.

I complessi requisiti di accesso ai dati, come la pettinatura di milioni di elementi, rendono necessario eseguire query / scansioni più efficienti. Gli indici secondari locali forniscono una chiave di ordinamento alternativa per un valore di chiave di partizione. Contengono anche copie di tutti o alcuni attributi della tabella. Organizzano i dati in base alla chiave di partizione della tabella, ma utilizzano una chiave di ordinamento diversa.

L'utilizzo di un indice secondario locale elimina la necessità di una scansione dell'intera tabella e consente una query semplice e rapida utilizzando una chiave di ordinamento.

Tutti gli indici secondari locali devono soddisfare determinate condizioni -

  • Chiave di partizione identica e chiave di partizione della tabella di origine.
  • Una chiave di ordinamento di un solo attributo scalare.
  • Proiezione della chiave di ordinamento della tabella di origine che funge da attributo non chiave.

Tutti gli indici secondari locali contengono automaticamente le chiavi di partizione e di ordinamento dalle tabelle padre. Nelle query, ciò significa una raccolta efficiente degli attributi proiettati e anche il recupero degli attributi non proiettati.

Il limite di archiviazione per un indice secondario locale rimane 10 GB per valore della chiave di partizione, che include tutti gli elementi della tabella e gli elementi dell'indice che condividono un valore della chiave di partizione.

Proiezione di un attributo

Alcune operazioni richiedono letture / recuperi in eccesso a causa della complessità. Queste operazioni possono consumare un throughput sostanziale. La proiezione consente di evitare costosi recuperi ed eseguire query complesse isolando questi attributi. Ricorda che le proiezioni consistono in attributi copiati in un indice secondario.

Quando si crea un indice secondario, si specificano gli attributi proiettati. Ricorda le tre opzioni fornite da DynamoDB:KEYS_ONLY, INCLUDE, and ALL.

Quando si opta per determinati attributi nella proiezione, considerare i compromessi sui costi associati:

  • Se si proietta solo un piccolo insieme di attributi necessari, si riducono notevolmente i costi di archiviazione.

  • Se si proiettano attributi non chiave a cui si accede frequentemente, si compensano i costi di scansione con i costi di archiviazione.

  • Se si proietta la maggior parte o tutti gli attributi non chiave, si massimizza la flessibilità e si riduce il throughput (nessun recupero); tuttavia, i costi di stoccaggio aumentano.

  • Se progetti KEYS_ONLY per scritture / aggiornamenti frequenti e query poco frequenti, riduce al minimo le dimensioni, ma mantiene la preparazione della query.

Creazione di un indice secondario locale

Utilizzare il LocalSecondaryIndexparametro di CreateTable per creare uno o più indici secondari locali. È necessario specificare un attributo non chiave per la chiave di ordinamento. Alla creazione della tabella, crei indici secondari locali. All'eliminazione, elimini questi indici.

Le tabelle con un indice secondario locale devono rispettare un limite di 10 GB di dimensione per valore della chiave di partizione, ma possono archiviare qualsiasi quantità di elementi.

Query e scansioni dell'indice secondario locale

Un'operazione di query sugli indici secondari locali restituisce tutti gli elementi con un valore di chiave di partizione corrispondente quando più elementi nell'indice condividono i valori della chiave di ordinamento. Gli articoli corrispondenti non vengono restituiti in un certo ordine. Le query per gli indici secondari locali utilizzano una coerenza finale o forte, con letture fortemente coerenti che forniscono i valori più recenti.

Un'operazione di scansione restituisce tutti i dati dell'indice secondario locale. Le scansioni richiedono di fornire una tabella e un nome di indice e consentire l'uso di un'espressione di filtro per eliminare i dati.

Scrittura dell'oggetto

Alla creazione di un indice secondario locale, si specifica un attributo della chiave di ordinamento e il relativo tipo di dati. Quando scrivi un elemento, il suo tipo deve corrispondere al tipo di dati dello schema della chiave se l'elemento definisce un attributo di una chiave di indice.

DynamoDB non impone requisiti di relazione uno-a-uno sugli elementi della tabella e sugli elementi dell'indice secondario locale. Le tabelle con più indici secondari locali comportano costi di scrittura più elevati rispetto a quelle con meno.

Considerazioni sulla velocità effettiva negli indici secondari locali

Il consumo di capacità di lettura di una query dipende dalla natura dell'accesso ai dati. Le query utilizzano una coerenza finale o forte, con letture fortemente coerenti utilizzando un'unità rispetto a mezza unità nelle letture coerenti alla fine.

Le limitazioni dei risultati includono una dimensione massima di 1 MB. Le dimensioni dei risultati derivano dalla somma della dimensione dell'elemento dell'indice corrispondente arrotondata per eccesso al 4KB più vicino e anche della dimensione dell'elemento della tabella corrispondente arrotondata al 4KB più vicino.

Il consumo di capacità di scrittura rimane all'interno delle unità fornite. Calcola il costo totale accantonato trovando la somma delle unità consumate nella scrittura della tabella e delle unità consumate negli indici di aggiornamento.

Puoi anche considerare i fattori chiave che influenzano i costi, alcuni dei quali possono essere:

  • Quando si scrive un elemento che definisce un attributo indicizzato o si aggiorna un articolo per definire un attributo indicizzato non definito, si verifica una singola operazione di scrittura.

  • Quando un aggiornamento della tabella modifica un valore di attributo chiave indicizzato, si verificano due scritture per eliminare e quindi aggiungere un elemento.

  • Quando una scrittura causa la cancellazione di un attributo indicizzato, si verifica una scrittura per rimuovere la vecchia proiezione dell'elemento.

  • Quando un elemento non esiste nell'indice prima o dopo un aggiornamento, non si verificano operazioni di scrittura.

Archiviazione dell'indice secondario locale

Durante la scrittura di un elemento di tabella, DynamoDB copia automaticamente l'attributo corretto impostato negli indici secondari locali richiesti. Questo addebita il tuo account. Lo spazio utilizzato risulta dalla somma della dimensione in byte della chiave primaria della tabella, della dimensione in byte dell'attributo della chiave dell'indice, di qualsiasi dimensione in byte dell'attributo proiettata presente e di 100 byte in overhead per ogni elemento dell'indice.

L'archiviazione della stima viene ottenuta stimando la dimensione media degli elementi dell'indice e moltiplicando per la quantità degli elementi della tabella.

Utilizzo di Java per lavorare con indici secondari locali

Crea un indice secondario locale creando prima un'istanza di classe DynamoDB. Quindi, crea un'istanza della classe CreateTableRequest con le informazioni sulla richiesta necessarie. Infine, usa il metodo createTable.

Esempio

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

Recupera le informazioni su un indice secondario locale con il metodo Descrivi. Crea semplicemente un'istanza di classe DynamoDB, crea un'istanza di classe Table e passa la tabella al metodo di descrizione.

Esempio

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

Eseguire una query utilizzando gli stessi passaggi di una query di tabella. Crea semplicemente un'istanza di classe DynamoDB, un'istanza di classe Table, un'istanza di classe Index, un oggetto query e utilizza il metodo query.

Esempio

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

Puoi anche rivedere il seguente esempio.

Note- L'esempio seguente può presupporre un'origine dati creata in precedenza. Prima di tentare l'esecuzione, acquisire le librerie di supporto e creare le origini dati necessarie (tabelle con caratteristiche richieste o altre fonti di riferimento).

L'esempio seguente utilizza anche Eclipse IDE, un file delle credenziali AWS e AWS Toolkit all'interno di un progetto Eclipse AWS Java.

Esempio

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