Dynamodb-local-secondary-indexes
DynamoDB-ローカルセカンダリインデックス
一部のアプリケーションは主キーを使用してクエリを実行するだけですが、状況によっては代替ソートキーの恩恵を受ける場合があります。 単一または複数のローカルセカンダリインデックスを作成して、アプリケーションに選択を許可します。
何百万ものアイテムを組み合わせるなどの複雑なデータアクセス要件により、より効率的なクエリ/スキャンを実行する必要があります。 ローカルセカンダリインデックスは、パーティションキー値の代替ソートキーを提供します。 また、すべてまたは一部のテーブル属性のコピーも保持します。 テーブルパーティションキーでデータを整理しますが、異なるソートキーを使用します。
ローカルセカンダリインデックスを使用すると、テーブル全体をスキャンする必要がなくなり、ソートキーを使用した簡単で迅速なクエリが可能になります。
すべてのローカルセカンダリインデックスは、特定の条件を満たす必要があります-
- 同一のパーティションキーとソーステーブルパーティションキー。
- 1つのスカラー属性のみのソートキー。
- 非キー属性として機能するソーステーブルのソートキーの投影。
すべてのローカルセカンダリインデックスは、親テーブルのパーティションキーとソートキーを自動的に保持します。 クエリでは、これは、投影された属性の効率的な収集と、投影されていない属性の取得を意味します。
ローカルセカンダリインデックスのストレージ制限は、パーティションキー値ごとに10 GBのままです。これには、すべてのテーブルアイテムと、パーティションキー値を共有するインデックスアイテムが含まれます。
属性を投影する
一部の操作では、複雑さのために過剰な読み取り/フェッチが必要になります。 これらの操作はかなりのスループットを消費する可能性があります。 プロジェクションを使用すると、これらの属性を分離することで、コストのかかるフェッチを避け、豊富なクエリを実行できます。 プロジェクションは、セカンダリインデックスにコピーされた属性で構成されていることに注意してください。
セカンダリインデックスを作成するときに、投影される属性を指定します。 DynamoDBが提供する3つのオプションを思い出してください: KEYS_ONLY、INCLUDE、およびALL 。
投影で特定の属性を選択する場合、関連するコストのトレードオフを考慮してください-
- 必要な属性の小さなセットのみを投影すると、ストレージコストを劇的に削減できます。
- 頻繁にアクセスされる非キー属性を投影する場合、スキャンコストとストレージコストを相殺します。
- ほとんどまたはすべての非キー属性を投影する場合、これにより柔軟性が最大になり、スループットが低下します(取得なし)。ただし、ストレージコストは上昇します。
- 頻繁な書き込み/更新およびまれなクエリに対してKEYS_ONLYを予測すると、サイズは最小になりますが、クエリの準備は維持されます。
ローカルセカンダリインデックスの作成
CreateTableの LocalSecondaryIndex パラメーターを使用して、単一または複数のローカルセカンダリインデックスを作成します。 ソートキーに1つの非キー属性を指定する必要があります。 テーブルの作成時に、ローカルのセカンダリインデックスを作成します。 削除時に、これらのインデックスを削除します。
ローカルセカンダリインデックスを持つテーブルは、パーティションキー値ごとにサイズが10GBの制限に従う必要がありますが、任意の量のアイテムを格納できます。
ローカルセカンダリインデックスのクエリとスキャン
ローカルセカンダリインデックスでのクエリ操作は、インデックス内の複数のアイテムがソートキー値を共有している場合、一致するパーティションキー値を持つすべてのアイテムを返します。 一致するアイテムは特定の順序で返されません。 ローカルセカンダリインデックスのクエリでは、結果整合性または強力な整合性が使用され、強力な整合性のある読み取りによって最新の値が提供されます。
スキャン操作は、すべてのローカルセカンダリインデックスデータを返します。 スキャンでは、テーブル名とインデックス名を指定し、フィルター式を使用してデータを破棄できるようにする必要があります。
アイテムライティング
ローカルセカンダリインデックスの作成時に、ソートキー属性とそのデータ型を指定します。 アイテムを記述するとき、そのアイテムがインデックスキーの属性を定義している場合、そのタイプはキースキーマのデータタイプと一致する必要があります。
DynamoDBは、テーブルアイテムとローカルセカンダリインデックスアイテムに1対1の関係要件を課しません。 複数のローカルセカンダリインデックスを持つテーブルは、少ないテーブルよりも書き込みコストが高くなります。
ローカルセカンダリインデックスのスループットに関する考慮事項
クエリの読み取り容量の消費は、データアクセスの性質によって異なります。 クエリは、結果整合性のある読み取りの半分のユニットと比較して、1つのユニットを使用した強い整合性のある読み取りで、結果整合性または強力な整合性を使用します。
結果の制限には、最大1MBのサイズが含まれます。 結果のサイズは、一致するインデックスアイテムサイズの合計を最も近い4KBに切り上げ、一致するテーブルアイテムのサイズも最も近い4KBに切り上げます。
書き込み容量の消費量は、プロビジョニングされたユニット内に残ります。 テーブル書き込みで消費されたユニットとインデックス更新で消費されたユニットの合計を見つけることにより、プロビジョニングされた総コストを計算します。
また、コストに影響を与える重要な要因を考慮することができます。
- インデックス付き属性を定義するアイテムを作成するか、アイテムを更新して未定義のインデックス付き属性を定義すると、単一の書き込み操作が発生します。
- テーブルの更新によってインデックス付きキー属性値が変更されると、2つの書き込みが発生して削除され、次にアイテムが追加されます。
- 書き込みによってインデックス付き属性が削除されると、1回の書き込みが発生して古いアイテムの投影が削除されます。
- 更新前または更新後にアイテムがインデックス内に存在しない場合、書き込みは発生しません。
ローカルセカンダリインデックスストレージ
テーブル項目の書き込み時に、DynamoDBは適切な属性セットを必要なローカルセカンダリインデックスに自動的にコピーします。 これによりアカウントに課金されます。 使用されるスペースは、テーブルのプライマリキーのバイトサイズ、インデックスキーの属性バイトサイズ、現在の投影された属性のバイトサイズ、および各インデックス項目のオーバーヘッドの100バイトの合計から生じます。
推定ストレージは、インデックスアイテムの平均サイズを推定し、テーブルアイテムの数量で乗算することによって取得されます。
Javaを使用してローカルセカンダリインデックスを操作する
最初にDynamoDBクラスインスタンスを作成して、ローカルセカンダリインデックスを作成します。 次に、必要な要求情報を使用してCreateTableRequestクラスインスタンスを作成します。 最後に、createTableメソッドを使用します。
例
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メソッドに渡すだけです。
例
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クラスインスタンス、クエリオブジェクトを作成し、クエリメソッドを利用します。
例
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());
}
次の例を確認することもできます。
注-次の例では、以前に作成されたデータソースを想定しています。 実行を試みる前に、サポートライブラリを取得し、必要なデータソース(必要な特性を備えたテーブル、または他の参照ソース)を作成します。
次の例では、Eclipse IDE、AWS認証情報ファイル、およびEclipse AWS Javaプロジェクト内のAWS Toolkitも使用しています。
例
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());
}
}
}
}