DynamoDB - Lokalne indeksy pomocnicze
Niektóre aplikacje wykonują zapytania tylko z kluczem podstawowym, ale w niektórych sytuacjach można skorzystać z alternatywnego klucza sortowania. Pozwól aplikacji na wybór, tworząc jeden lub wiele lokalnych indeksów pomocniczych.
Złożone wymagania dotyczące dostępu do danych, takie jak łączenie milionów elementów, powodują konieczność wykonywania bardziej wydajnych zapytań / skanów. Lokalne indeksy pomocnicze zapewniają alternatywny klucz sortowania dla wartości klucza partycji. Przechowują również kopie wszystkich lub niektórych atrybutów tabeli. Organizują dane według klucza partycji tabeli, ale używają innego klucza sortowania.
Korzystanie z lokalnego indeksu pomocniczego eliminuje potrzebę skanowania całej tabeli i umożliwia proste i szybkie zapytanie przy użyciu klucza sortowania.
Wszystkie lokalne indeksy dodatkowe muszą spełniać określone warunki -
- Identyczny klucz partycji i klucz partycji tabeli źródłowej.
- Klucz sortowania zawierający tylko jeden atrybut skalarny.
- Projekcja klucza sortowania tabeli źródłowej działającej jako atrybut niebędący kluczem.
Wszystkie lokalne indeksy pomocnicze automatycznie przechowują klucze partycji i sortowania z tabel nadrzędnych. W zapytaniach oznacza to wydajne gromadzenie przewidywanych atrybutów, a także pobieranie atrybutów nieprzewidywanych.
Limit magazynu dla lokalnego indeksu pomocniczego pozostaje 10 GB na wartość klucza partycji, która obejmuje wszystkie elementy tabeli i elementy indeksu współużytkujące wartość klucza partycji.
Projekcja atrybutu
Niektóre operacje wymagają nadmiernych odczytów / pobierania ze względu na złożoność. Operacje te mogą pochłaniać znaczną przepustowość. Projekcja pozwala uniknąć kosztownego pobierania i wykonywania rozbudowanych zapytań poprzez izolowanie tych atrybutów. Pamiętaj, że prognozy składają się z atrybutów skopiowanych do indeksu dodatkowego.
Tworząc indeks pomocniczy, należy określić rzutowane atrybuty. Przypomnij sobie trzy opcje oferowane przez DynamoDB:KEYS_ONLY, INCLUDE, and ALL.
Decydując się na pewne atrybuty w projekcji, rozważ powiązane koszty -
Projektując tylko niewielki zestaw niezbędnych atrybutów, znacznie zmniejszasz koszty przechowywania.
Jeśli projektujesz często używane atrybuty niebędące kluczowymi, kompensujesz koszty skanowania kosztami przechowywania.
Jeśli projektujesz większość lub wszystkie atrybuty niebędące kluczami, maksymalizuje to elastyczność i zmniejsza przepustowość (brak pobierania); jednak koszty przechowywania rosną.
Jeśli projektujesz KEYS_ONLY dla częstych zapisów / aktualizacji i rzadkich zapytań, minimalizuje to rozmiar, ale utrzymuje przygotowanie zapytania.
Tworzenie lokalnego indeksu dodatkowego
Użyj LocalSecondaryIndexparametr CreateTable, aby utworzyć jeden lub wiele lokalnych indeksów pomocniczych. Musisz określić jeden atrybut niebędący kluczem dla klucza sortowania. Podczas tworzenia tabeli tworzysz lokalne indeksy wtórne. Podczas usuwania usuwasz te indeksy.
Tabele z lokalnym indeksem pomocniczym muszą przestrzegać limitu 10 GB na wartość klucza partycji, ale mogą przechowywać dowolną liczbę elementów.
Lokalne zapytania i skany dotyczące dodatkowego indeksu
Operacja zapytania dotycząca lokalnych indeksów pomocniczych zwraca wszystkie elementy z pasującą wartością klucza partycji, gdy wiele elementów w indeksie współużytkuje wartości klucza sortowania. Pasujące przedmioty nie wracają w określonej kolejności. Zapytania dla lokalnych indeksów pomocniczych używają ostatecznej lub silnej spójności, z silnie spójnymi odczytami dostarczającymi najnowsze wartości.
Operacja skanowania zwraca wszystkie lokalne dane indeksu pomocniczego. Skanowania wymagają podania nazwy tabeli i indeksu oraz pozwalają na użycie wyrażenia filtru w celu odrzucenia danych.
Pisanie przedmiotów
Podczas tworzenia lokalnego indeksu pomocniczego należy określić atrybut klucza sortowania i jego typ danych. Kiedy piszesz element, jego typ musi być zgodny z typem danych schematu klucza, jeśli element definiuje atrybut klucza indeksu.
DynamoDB nie nakłada żadnych wymagań dotyczących relacji jeden do jednego dla elementów tabeli i lokalnych drugorzędnych elementów indeksu. Tabele z wieloma lokalnymi indeksami pomocniczymi wiążą się z wyższymi kosztami zapisu niż te z mniejszymi.
Uwagi dotyczące przepustowości w lokalnych indeksach pomocniczych
Zużycie pojemności odczytu zapytania zależy od charakteru dostępu do danych. Zapytania używają ostatecznej lub silnej spójności, z silnie spójnymi odczytami przy użyciu jednej jednostki w porównaniu do połowy jednostki w ostatecznie spójnych odczytach.
Ograniczenia wyników obejmują maksymalny rozmiar 1 MB. Rozmiary wyników pochodzą z sumy pasującego rozmiaru elementu indeksu zaokrąglonego w górę do najbliższego 4KB, a dopasowany rozmiar elementu tabeli również zaokrąglony w górę do najbliższego 4KB.
Zużycie pojemności zapisu pozostaje w obsługiwanych jednostkach. Oblicz całkowity koszt aprowizacji, znajdując sumę zużytych jednostek w tabeli i zużytych jednostek podczas aktualizacji indeksów.
Możesz również wziąć pod uwagę kluczowe czynniki wpływające na koszty, z których niektóre mogą być -
Podczas pisania elementu definiującego atrybut indeksowany lub aktualizowania elementu w celu zdefiniowania niezdefiniowanego atrybutu indeksowanego następuje pojedyncza operacja zapisu.
Gdy aktualizacja tabeli zmienia wartość atrybutu klucza indeksowanego, występują dwa zapisy w celu usunięcia, a następnie - dodania elementu.
Kiedy zapis powoduje usunięcie atrybutu indeksowanego, następuje jeden zapis w celu usunięcia projekcji starej pozycji.
Jeśli element nie istnieje w indeksie przed lub po aktualizacji, zapis nie występuje.
Lokalne przechowywanie indeksu wtórnego
W przypadku zapisu elementu tabeli DynamoDB automatycznie kopiuje prawy zestaw atrybutów do wymaganych lokalnych indeksów pomocniczych. To obciąża Twoje konto. Wykorzystane miejsce jest wynikiem sumy rozmiaru bajtów klucza podstawowego tabeli, rozmiaru bajtu atrybutu klucza indeksu, dowolnego obecnego przewidywanego rozmiaru bajtu atrybutu i 100 bajtów narzutu dla każdego elementu indeksu.
Szacunkowe przechowywanie uzyskuje się poprzez oszacowanie średniego rozmiaru pozycji indeksu i pomnożenie przez ilość pozycji tabeli.
Używanie języka Java do pracy z lokalnymi indeksami pomocniczymi
Utwórz lokalny indeks pomocniczy, najpierw tworząc instancję klasy DynamoDB. Następnie utwórz instancję klasy CreateTableRequest z niezbędnymi informacjami o żądaniu. Na koniec użyj metody createTable.
Przykład
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());
Pobierz informacje o lokalnym indeksie pomocniczym za pomocą metody opisywania. Po prostu utwórz instancję klasy DynamoDB, utwórz instancję klasy Table i przekaż tabelę do metody opisywania.
Przykład
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());
}
}
Wykonaj zapytanie, wykonując te same kroki, co w przypadku zapytania tabeli. Wystarczy utworzyć instancję klasy DynamoDB, instancję klasy Table, instancję klasy Index, obiekt zapytania i użyć metody zapytania.
Przykład
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());
}
Możesz również przejrzeć następujący przykład.
Note- W poniższym przykładzie można założyć, że zostało utworzone wcześniej źródło danych. Przed przystąpieniem do wykonania należy nabyć biblioteki pomocnicze i utworzyć niezbędne źródła danych (tabele z wymaganymi cechami lub inne źródła, do których istnieją odniesienia).
W poniższym przykładzie użyto również środowiska Eclipse IDE, pliku referencji AWS oraz zestawu narzędzi AWS w ramach projektu Eclipse AWS Java.
Przykład
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());
}
}
}
}