DynamoDB - Chỉ mục phụ cục bộ
Một số ứng dụng chỉ thực hiện truy vấn với khóa chính, nhưng một số trường hợp được hưởng lợi từ khóa sắp xếp thay thế. Cho phép ứng dụng của bạn lựa chọn bằng cách tạo một hoặc nhiều chỉ mục phụ cục bộ.
Các yêu cầu truy cập dữ liệu phức tạp, chẳng hạn như kết hợp hàng triệu mục, khiến nó cần thực hiện các truy vấn / quét hiệu quả hơn. Các chỉ mục phụ cục bộ cung cấp khóa sắp xếp thay thế cho giá trị khóa phân vùng. Họ cũng giữ bản sao của tất cả hoặc một số thuộc tính bảng. Họ sắp xếp dữ liệu theo khóa phân vùng bảng, nhưng sử dụng khóa sắp xếp khác.
Sử dụng chỉ mục phụ cục bộ loại bỏ nhu cầu quét toàn bộ bảng và cho phép truy vấn đơn giản và nhanh chóng bằng cách sử dụng khóa sắp xếp.
Tất cả các chỉ mục phụ cục bộ phải đáp ứng các điều kiện nhất định -
- Khóa phân vùng giống hệt nhau và khóa phân vùng bảng nguồn.
- Một khóa sắp xếp của chỉ một thuộc tính vô hướng.
- Phép chiếu của bảng nguồn sắp xếp khóa hoạt động như một thuộc tính không phải khóa.
Tất cả các chỉ mục phụ cục bộ tự động giữ các khóa phân vùng và sắp xếp từ các bảng cha. Trong các truy vấn, điều này có nghĩa là thu thập hiệu quả các thuộc tính được chiếu và cũng có thể truy xuất các thuộc tính không được chiếu.
Giới hạn lưu trữ cho một chỉ mục phụ cục bộ vẫn là 10GB cho mỗi giá trị khóa phân vùng, bao gồm tất cả các mục trong bảng và các mục chỉ mục chia sẻ giá trị khóa phân vùng.
Chiếu một thuộc tính
Một số hoạt động yêu cầu đọc / tìm nạp vượt quá do phức tạp. Các hoạt động này có thể tiêu tốn thông lượng đáng kể. Phép chiếu cho phép bạn tránh tìm nạp tốn kém và thực hiện các truy vấn phong phú bằng cách cô lập các thuộc tính này. Hãy nhớ các phép chiếu bao gồm các thuộc tính được sao chép vào một chỉ mục phụ.
Khi tạo chỉ mục phụ, bạn chỉ định các thuộc tính được chiếu. Nhớ lại ba tùy chọn được cung cấp bởi DynamoDB:KEYS_ONLY, INCLUDE, and ALL.
Khi chọn một số thuộc tính nhất định trong dự báo, hãy xem xét sự cân bằng chi phí liên quan -
Nếu bạn chỉ chiếu một tập hợp nhỏ các thuộc tính cần thiết, bạn sẽ giảm đáng kể chi phí lưu trữ.
Nếu bạn chiếu các thuộc tính không phải khóa được truy cập thường xuyên, bạn sẽ bù đắp chi phí quét bằng chi phí lưu trữ.
Nếu bạn chiếu hầu hết hoặc tất cả các thuộc tính không phải khóa, điều này tối đa hóa tính linh hoạt và giảm thông lượng (không có truy xuất); tuy nhiên, chi phí lưu trữ tăng lên.
Nếu bạn chiếu KEYS_ONLY để ghi / cập nhật thường xuyên và các truy vấn không thường xuyên, nó sẽ giảm thiểu kích thước nhưng vẫn duy trì việc chuẩn bị truy vấn.
Tạo chỉ mục phụ cục bộ
Sử dụng LocalSecondaryIndextham số của CreateTable để tạo một hoặc nhiều chỉ mục phụ cục bộ. Bạn phải chỉ định một thuộc tính không phải khóa cho khóa sắp xếp. Khi tạo bảng, bạn tạo các chỉ mục phụ cục bộ. Khi xóa, bạn xóa các chỉ mục này.
Các bảng có chỉ mục phụ cục bộ phải tuân theo giới hạn kích thước 10GB cho mỗi giá trị khóa phân vùng, nhưng có thể lưu trữ bất kỳ số lượng mục nào.
Truy vấn và quét chỉ mục phụ cục bộ
Thao tác truy vấn trên các chỉ mục phụ cục bộ trả về tất cả các mục có giá trị khóa phân vùng phù hợp khi nhiều mục trong chỉ mục chia sẻ giá trị khóa sắp xếp. Các mục phù hợp không trở lại theo một thứ tự nhất định. Các truy vấn cho các chỉ mục phụ cục bộ sử dụng tính nhất quán cuối cùng hoặc mạnh mẽ, với các lần đọc nhất quán cao cung cấp các giá trị mới nhất.
Thao tác quét trả về tất cả dữ liệu chỉ mục phụ cục bộ. Quá trình quét yêu cầu bạn cung cấp bảng và tên chỉ mục, đồng thời cho phép sử dụng biểu thức bộ lọc để loại bỏ dữ liệu.
Viết mục
Khi tạo chỉ mục phụ cục bộ, bạn chỉ định thuộc tính khóa sắp xếp và kiểu dữ liệu của nó. Khi bạn viết một mục, kiểu của nó phải khớp với kiểu dữ liệu của lược đồ khóa nếu mục đó xác định một thuộc tính của khóa chỉ mục.
DynamoDB không áp đặt các yêu cầu về mối quan hệ một-một đối với các mục bảng và các mục chỉ mục phụ cục bộ. Các bảng có nhiều chỉ mục phụ cục bộ có chi phí ghi cao hơn các bảng có ít chỉ mục hơn.
Cân nhắc thông lượng trong chỉ mục phụ cục bộ
Mức tiêu thụ dung lượng đọc của một truy vấn phụ thuộc vào bản chất của việc truy cập dữ liệu. Các truy vấn sử dụng tính nhất quán cuối cùng hoặc mạnh mẽ, với các lần đọc nhất quán cao sử dụng một đơn vị so với một nửa đơn vị trong các lần đọc cuối cùng nhất quán.
Giới hạn kết quả bao gồm kích thước tối đa 1MB. Kích thước kết quả đến từ tổng kích thước mục chỉ mục phù hợp được làm tròn đến 4KB gần nhất và kích thước mục trong bảng phù hợp cũng được làm tròn đến 4KB gần nhất.
Mức tiêu thụ dung lượng ghi vẫn nằm trong các đơn vị được cung cấp. Tính tổng chi phí dự phòng bằng cách tìm tổng số đơn vị tiêu thụ khi ghi bảng và đơn vị tiêu thụ khi cập nhật chỉ số.
Bạn cũng có thể xem xét các yếu tố chính ảnh hưởng đến chi phí, một số trong số đó có thể là:
Khi bạn viết một mục xác định một thuộc tính được lập chỉ mục hoặc cập nhật một mục để xác định một thuộc tính được lập chỉ mục không xác định, một thao tác ghi sẽ xảy ra.
Khi cập nhật bảng thay đổi giá trị thuộc tính khóa được lập chỉ mục, hai lần ghi xảy ra để xóa và sau đó - thêm một mục.
Khi một lần ghi gây ra việc xóa một thuộc tính được lập chỉ mục, một lần ghi xảy ra để xóa phép chiếu mục cũ.
Khi một mục không tồn tại trong chỉ mục trước hoặc sau khi cập nhật, không có lần ghi nào xảy ra.
Lưu trữ chỉ mục phụ cục bộ
Khi ghi một mục trong bảng, DynamoDB sẽ tự động sao chép thuộc tính bên phải được đặt thành các chỉ mục phụ cục bộ được yêu cầu. Điều này tính phí tài khoản của bạn. Khoảng trống được sử dụng là kết quả từ tổng kích thước byte khóa chính của bảng, kích thước byte thuộc tính khóa chỉ mục, bất kỳ kích thước byte thuộc tính dự kiến nào hiện tại và 100 byte chi phí cho mỗi mục chỉ mục.
Việc lưu trữ ước tính có được bằng cách ước tính kích thước mục chỉ mục trung bình và nhân với số lượng mục trong bảng.
Sử dụng Java để làm việc với các chỉ mục phụ cục bộ
Tạo một chỉ mục phụ cục bộ bằng cách tạo một phiên bản lớp DynamoDB trước. Sau đó, tạo một cá thể lớp CreateTableRequest với thông tin yêu cầu cần thiết. Cuối cùng, sử dụng phương thức createTable.
Thí dụ
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());
Lấy thông tin về một chỉ mục phụ cục bộ với phương thức mô tả. Chỉ cần tạo một cá thể lớp DynamoDB, tạo một cá thể lớp Bảng và chuyển bảng vào phương thức mô tả.
Thí dụ
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());
}
}
Thực hiện truy vấn bằng cách sử dụng các bước tương tự như truy vấn bảng. Chỉ tạo một cá thể lớp DynamoDB, một cá thể lớp Bảng, một cá thể lớp Chỉ mục, một đối tượng truy vấn và sử dụng phương thức truy vấn.
Thí dụ
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());
}
Bạn cũng có thể xem lại ví dụ sau.
Note- Ví dụ sau có thể giả sử một nguồn dữ liệu đã tạo trước đó. Trước khi cố gắng thực thi, hãy thu thập các thư viện hỗ trợ và tạo các nguồn dữ liệu cần thiết (các bảng có các đặc điểm bắt buộc hoặc các nguồn tham chiếu khác).
Ví dụ sau cũng sử dụng Eclipse IDE, tệp thông tin đăng nhập AWS và Bộ công cụ AWS trong Dự án Java AWS của Eclipse.
Thí dụ
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());
}
}
}
}