Dynamodb 简明教程

DynamoDB - Local Secondary Indexes

一些应用程序仅用主键执行查询,但某些情况可受益于备用排序密钥。通过创建单个或多个本地二级索引让应用程序做出选择。

复杂的数据访问要求,例如梳理数百万个项,需要执行更有效的查询/扫描。本地二级索引为分区密钥值提供备用排序密钥。它们还保存全部或部分表格属性的副本。它们按表格分区密钥组织数据,但使用不同的排序密钥。

使用本地二级索引无需对整个表进行扫描,并允许使用排序键进行简单快捷的查询。

所有本地二级索引都必须满足特定条件——

  1. 相同的分区键和源表分区键。

  2. 排序键只包含一个标量属性。

  3. 源表排序键的投射作为非键属性。

所有本地二级索引自动持有父表的 partition key 和 sort key。在查询中,这意味着可以有效收集投射的属性,也可以检索未投射的属性。

本地二级索引的存储限制仍然是每个 partition key 值 10GB,其中包括所有表项和共享 partition key 值的索引项。

Projecting an Attribute

由于复杂性,某些操作需要额外的读取/获取。这些操作可能消耗大量吞吐量。通过隔离这些属性,投射可避免代价高昂的获取并执行丰富查询。记住,投射由复制到二级索引的属性组成。

在创建二级索引时,可以指定投射的属性。回想一下 DynamoDB 提供的三个选项: KEYS_ONLY, INCLUDE, and ALL

在对投射中的某些属性进行选择时,考虑相关的成本权衡——

  1. 如果你仅投射一组必要的属性,你将显著减少存储成本。

  2. 如果你投射经常访问的非键属性,你将以存储成本抵消扫描成本。

  3. 如果你投射了大多数或所有非键属性,这将最大化灵活性并降低吞吐量(无需检索);但是,存储成本将上升。

  4. 如果你针对频繁的写/更新和不频繁的查询投射 KEYS_ONLY,它可以最小化大小,但会保留查询准备。

Local Secondary Index Creation

使用 CreateTable 的 LocalSecondaryIndex 参数可以创建单个或多个本地二级索引。你必须为排序键指定一个非键属性。在创建表时,你创建本地二级索引。在删除时,你删除这些索引。

具有本地二级索引的表的每个分区键值的大小必须遵守 10GB 的限制,但可以存储任意数量的项。

Local Secondary Index Queries and Scans

当索引中的多个项共享排序键值时,在本地二级索引上执行的查询操作会返回所有具有匹配分区键值的项。匹配的项不会按特定顺序返回。对本地二级索引的查询使用最终一致性或强一致性,其中强一致性读取可传递最新的值。

一个扫描操作返回全部的二级本地索引数据。扫描要求提供表名和索引名,并允许使用过滤器表达式来丢弃数据。

Item Writing

在创建本地二级索引时,可以指定排序键属性及其数据类型。如果该项会定义索引键的属性,当写入该项时,其类型必须与键模式的数据类型匹配。

DynamoDB 不对表项和本地二级索引项征收一对一关系要求。带有多个本地二级索引的表会比具有较少索引的表产生更高的写入成本。

Throughput Considerations in Local Secondary Indexes

查询的读取容量消耗取决于数据访问的本质。查询使用最终或强一致性,强一致性读取使用一个单位,而最终一致性读取使用 0.5 个单位。

结果限制包括 1MB 的最大值。结果大小来源于匹配索引项大小的总和(向上取整到最接近的 4KB)以及匹配表项大小的总和(同样向上取整到最接近的 4KB)。

写入容量消耗保持在预置单位内。通过找到表写入中消耗的单位总和及更新索引中消耗的单位总和来计算总预置成本。

你还可以考虑影响成本的关键因素,其中一些因素可以是 −

  1. 当你编写定义索引属性的项或更新项以定义未定义的索引属性时,会执行单个写入操作。

  2. 当表更新更改索引键属性值时,会发生两次写入来删除然后 - 添加项。

  3. 当写入导致删除索引的属性时,会执行一次写入来移除旧的项投影。

  4. 当项在更新前或更新后在索引中不存在时,不会执行写入。

Local Secondary Index Storage

在表项写入中,DynamoDB 会自动复制正确的属性集到所需的本地二级索引。这会产生账户费用。所用的空间来自于表主密钥字节大小、索引键属性字节大小、任何当前投影属性字节大小以及每个索引项的开销中 100 个字节的总和。

估算存储可以通过估算平均索引项大小并乘以表项数量来获得。

Using Java to Work with Local Secondary Indexes

首先创建一个 DynamoDB 类实例,来创建一个本地二级索引。然后,使用必要的请求信息创建一个 CreateTableRequest 类实例。最后,使用 createTable 方法。

Example

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

使用 describe 方法来检索有关本地二级索引的信息。只需创建一个 DynamoDB 类实例,创建一个 Table 类实例,并把表传递到 describe 方法。

Example

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

通过使用与表查询相同的步骤来执行查询。只需创建一个 DynamoDB 类实例、一个 Table 类实例、一个 Index 类实例、一个查询对象,并使用 query 方法。

Example

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

你还可以查看以下示例。

Note −以下示例可能假定已经创建了数据源。在尝试执行之前,获取支持库并创建必要的数据源(具有所需特征的表或其他引用源)。

以下示例还使用了 Eclipse IDE、AWS 凭证文件以及 Eclipse AWS Java 项目中的 AWS Toolkit。

Example

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", "jane@sample.com")
            .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", "jane@sample.com")
            .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", "jane@example.com"));

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