- DynamoDB 教程
- DynamoDB - 首页
- DynamoDB - 概览
- DynamoDB - 基本概念
- DynamoDB - 环境
- DynamoDB - 操作工具
- DynamoDB - 数据类型
- DynamoDB - 创建表
- DynamoDB - 加载表
- DynamoDB - 查询表
- DynamoDB - 删除表
- DynamoDB - API 接口
- DynamoDB - 创建项目
- DynamoDB - 获取项目
- DynamoDB - 更新项目
- DynamoDB - 删除项目
- DynamoDB - 批量写入
- DynamoDB - 批量检索
- DynamoDB - 查询
- DynamoDB - 扫描
- DynamoDB - 索引
- 全局二级索引
- 本地二级索引
- DynamoDB - 聚合
- DynamoDB - 访问控制
- DynamoDB - 权限 API
- DynamoDB - 条件
- Web 身份联合
- DynamoDB - 数据管道
- DynamoDB - 数据备份
- DynamoDB - 监控
- DynamoDB - CloudTrail
- DynamoDB - MapReduce
- DynamoDB - 表活动
- DynamoDB - 错误处理
- DynamoDB - 最佳实践
- DynamoDB 有用资源
- DynamoDB 快速指南
- DynamoDB - 有用资源
- DynamoDB - 讨论
DynamoDB 快速指南
DynamoDB - 概览
DynamoDB 允许用户创建能够存储和检索任意数量数据并为任意数量流量提供服务的数据库。它会自动将数据和流量分布在服务器上以动态管理每个客户的请求,并保持快速性能。
DynamoDB 与 RDBMS 的比较
DynamoDB 使用 NoSQL 模型,这意味着它使用非关系型系统。下表重点介绍了 DynamoDB 和 RDBMS 之间的区别 -
| 常见任务 | RDBMS | DynamoDB |
|---|---|---|
| 连接到源 | 它使用持久连接和 SQL 命令。 | 它使用 HTTP 请求和 API 操作 |
| 创建表 | 其基本结构是表,并且必须定义。 | 它仅使用主键,创建时没有模式。它使用各种数据源。 |
| 获取表信息 | 所有表信息仍然可访问 | 仅显示主键。 |
| 加载表数据 | 它使用由列组成的行。 | 在表中,它使用由属性组成的项目 |
| 读取表数据 | 它使用 SELECT 语句和过滤语句。 | 它使用 GetItem、Query 和 Scan。 |
| 管理索引 | 它使用通过 SQL 语句创建的标准索引。对其进行的修改会在表更改时自动发生。 | 它使用二级索引来实现相同的功能。它需要规范(分区键和排序键)。 |
| 修改表数据 | 它使用 UPDATE 语句。 | 它使用 UpdateItem 操作。 |
| 删除表数据 | 它使用 DELETE 语句。 | 它使用 DeleteItem 操作。 |
| 删除表 | 它使用 DROP TABLE 语句。 | 它使用 DeleteTable 操作。 |
优势
DynamoDB 的两个主要优势是可扩展性和灵活性。它不要求使用特定的数据源和结构,允许用户以统一的方式处理几乎任何内容。
其设计还支持从较轻的任务和操作到苛刻的企业功能的广泛用途。它还允许简单地使用多种语言:Ruby、Java、Python、C#、Erlang、PHP 和 Perl。
局限性
然而,DynamoDB 确实存在某些局限性,但这些局限性不一定造成重大问题或阻碍可靠的开发。
您可以从以下几点回顾它们 -
容量单位大小 - 读取容量单位是每秒对不大于 4KB 的项目进行一次一致读取。写入容量单位是每秒对不大于 1KB 的项目进行一次写入。
预置吞吐量最小值/最大值 - 所有表和全局二级索引的读取和写入容量单位至少为一个。最大值取决于区域。在美国,每个表的读取和写入上限为 40K(每个帐户 80K),其他区域每个表的读取和写入上限为 10K,每个帐户的读取和写入上限为 20K。
预置吞吐量的增加和减少 - 您可以根据需要随时增加它,但减少每天每个表最多仅限四次。
每个帐户的表大小和数量 - 表大小没有限制,但除非您请求更高的上限,否则帐户的表限制为 256 个。
每个表的二级索引 - 允许五个本地索引和五个全局索引。
每个表的投影二级索引属性 - DynamoDB 允许 20 个属性。
分区键长度和值 - 它们的最小长度为 1 字节,最大长度为 2048 字节,但是,DynamoDB 对值没有限制。
排序键长度和值 - 其最小长度为 1 字节,最大长度为 1024 字节,除非其表使用本地二级索引,否则对值没有限制。
表和二级索引名称 - 名称的最小长度必须为 3 个字符,最大长度为 255 个字符。它们使用以下字符:AZ、a-z、0-9、“_”、“-” 和“.”。
属性名称 - 最小为一个字符,最大为 64KB,但键和某些属性除外。
保留字 - DynamoDB 不阻止将保留字用作名称。
表达式长度 - 表达式字符串长度限制为 4KB。属性表达式的长度限制为 255 字节。表达式的替换变量长度限制为 2MB。
DynamoDB - 基本概念
在使用 DynamoDB 之前,您必须熟悉其基本组件和生态系统。在 DynamoDB 生态系统中,您使用表、属性和项目进行操作。表包含一组项目,项目包含一组属性。属性是数据的基本元素,不需要进一步分解,即字段。
主键
主键充当表项目唯一标识的方法,二级索引提供查询灵活性。DynamoDB 通过修改表数据来记录事件。
表创建不仅需要设置名称,还需要设置主键;它标识表项目。没有两个项目共享一个键。DynamoDB 使用两种类型的主键 -
分区键 - 此简单主键由一个称为“分区键”的单个属性组成。在内部,DynamoDB 使用键值作为哈希函数的输入来确定存储位置。
分区键和排序键 - 此键称为“复合主键”,由两个属性组成。
分区键和
排序键。
DynamoDB 将第一个属性应用于哈希函数,并将具有相同分区键的项目存储在一起;其顺序由排序键确定。项目可以共享分区键,但不能共享排序键。
主键属性仅允许标量(单个)值;以及字符串、数字或二进制数据类型。非键属性没有这些约束。
二级索引
这些索引允许您使用备用键查询表数据。尽管 DynamoDB 不要求使用它们,但它们可以优化查询。
DynamoDB 使用两种类型的二级索引 -
全局二级索引 - 此索引拥有分区键和排序键,它们可能与表键不同。
本地二级索引 - 此索引拥有与表相同的分区键,但其排序键不同。
API
DynamoDB 提供的 API 操作包括控制平面、数据平面(例如创建、读取、更新和删除)和流的操作。在控制平面操作中,您可以使用以下工具创建和管理表 -
- CreateTable
- DescribeTable
- ListTables
- UpdateTable
- DeleteTable
在数据平面中,您可以使用以下工具执行 CRUD 操作 -
| 创建 | 读取 | 更新 | 删除 |
|---|---|---|---|
PutItem BatchWriteItem |
GetItem BatchGetItem Query Scan |
UpdateItem | DeleteItem BatchWriteItem |
流操作控制表流。您可以查看以下流工具 -
- ListStreams
- DescribeStream
- GetShardIterator
- GetRecords
预置吞吐量
在表创建中,您指定预置吞吐量,它保留读取和写入的资源。您使用容量单位来衡量和设置吞吐量。
当应用程序超出设置的吞吐量时,请求会失败。DynamoDB GUI 控制台允许监控设置和使用的吞吐量,以便更好地进行动态配置。
读取一致性
DynamoDB 使用最终一致性和强一致性读取来支持动态应用程序需求。最终一致性读取并不总是提供当前数据。
强一致性读取始终提供当前数据(设备故障或网络问题除外)。最终一致性读取用作默认设置,需要在ConsistentRead参数中设置为 true 才能更改它。
分区
DynamoDB 使用分区进行数据存储。这些表的存储分配具有 SSD 支持,并在区域之间自动复制。DynamoDB 管理所有分区任务,无需用户参与。
在表创建中,表进入 CREATING 状态,该状态分配分区。当它达到 ACTIVE 状态时,您可以执行操作。当其容量达到最大值或您更改吞吐量时,系统会更改分区。
DynamoDB - 环境
DynamoDB 环境仅包括使用您的 Amazon Web Services 帐户访问 DynamoDB GUI 控制台,但是,您也可以执行本地安装。
导航到以下网站 - https://aws.amazon.com/dynamodb/
点击“开始使用 Amazon DynamoDB”按钮,如果您没有 Amazon Web Services 帐户,则点击“创建 AWS 帐户”按钮。简单的引导流程将告知您所有相关的费用和要求。
完成流程的所有必要步骤后,您将获得访问权限。只需登录 AWS 控制台,然后导航到 DynamoDB 控制台。
请务必删除未使用的或不必要的材料,以避免相关费用。
本地安装
AWS(Amazon Web Service)提供了一个用于本地安装的 DynamoDB 版本。它支持在没有 Web 服务或连接的情况下创建应用程序。它还可以通过允许本地数据库来减少预置吞吐量、数据存储和传输费用。本指南假定进行了本地安装。
准备好部署时,您可以对应用程序进行一些小的调整以将其转换为 AWS 使用。
安装文件是一个.jar 可执行文件。它可以在 Linux、Unix、Windows 和任何其他支持 Java 的操作系统上运行。使用以下链接之一下载文件 -
Tarball 压缩包 − http://dynamodb-local.s3-website-us-west2.amazonaws.com/dynamodb_local_latest.tar.gz
Zip 压缩包 − http://dynamodb-local.s3-website-us-west2.amazonaws.com/dynamodb_local_latest.zip
注意 − 其他存储库也提供此文件,但不一定是最新版本。请使用以上链接获取最新的安装文件。此外,请确保您已安装 Java 运行时环境 (JRE) 6.x 或更高版本。DynamoDB 无法在较旧的版本上运行。
下载相应的压缩包后,解压其目录(DynamoDBLocal.jar)并将其放置在所需位置。
然后,您可以通过打开命令提示符,导航到包含 DynamoDBLocal.jar 的目录,并输入以下命令来启动 DynamoDB −
java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb
您也可以通过关闭用于启动它的命令提示符来停止 DynamoDB。
工作环境
您可以使用 JavaScript Shell、GUI 控制台和多种语言来使用 DynamoDB。可用的语言包括 Ruby、Java、Python、C#、Erlang、PHP 和 Perl。
在本教程中,我们使用 Java 和 GUI 控制台示例来提高概念和代码的清晰度。安装 Java IDE、AWS SDK for Java,并为 Java SDK 设置 AWS 安全凭证以使用 Java。
从本地到 Web 服务代码的转换
准备好部署时,您需要更改代码。调整取决于代码语言和其他因素。主要更改仅包括将端点从本地点更改为 AWS 区域。其他更改需要更深入地分析您的应用程序。
本地安装在许多方面都不同于 Web 服务,包括但不限于以下关键区别 −
本地安装会立即创建表,但服务需要更长时间。
本地安装会忽略吞吐量。
本地安装中删除操作会立即发生。
由于没有网络开销,本地安装中的读/写操作会很快。
DynamoDB - 操作工具
DynamoDB 提供三种执行操作的选项:基于 Web 的 GUI 控制台、JavaScript Shell 和您选择的编程语言。
在本教程中,我们将重点介绍使用 GUI 控制台和 Java 语言,以提高清晰度和概念理解。
GUI 控制台
可以在以下地址找到 GUI 控制台或 Amazon DynamoDB 的 AWS 管理控制台 − https://console.aws.amazon.com/dynamodb/home
它允许您执行以下任务 −
- CRUD 操作
- 查看表项
- 执行表查询
- 设置表容量监控的警报
- 实时查看表指标
- 查看表警报
如果您的 DynamoDB 帐户没有表,则在访问时,它会指导您创建表。其主屏幕提供了三个执行常见操作的快捷方式 −
- 创建表
- 添加和查询表
- 监控和管理表
JavaScript Shell
DynamoDB 包含一个交互式 JavaScript Shell。Shell 在 Web 浏览器中运行,推荐的浏览器包括 Firefox 和 Chrome。
注意 − 使用其他浏览器可能会导致错误。
通过打开 Web 浏览器并输入以下地址来访问 Shell −https://:8000/shell
通过在左侧窗格中输入 JavaScript 并单击左侧窗格右上角的“播放”图标按钮来使用 Shell,这将运行代码。代码结果显示在右侧窗格中。
DynamoDB 和 Java
通过使用 Java 开发环境,在 DynamoDB 中使用 Java。操作符合正常的 Java 语法和结构。
DynamoDB - 数据类型
DynamoDB 支持的数据类型包括特定于属性、操作和您选择的编码语言的数据类型。
属性数据类型
DynamoDB 支持大量用于表属性的数据类型。每种数据类型都属于以下三类之一 −
标量 − 这些类型表示单个值,包括数字、字符串、二进制、布尔值和空值。
文档 − 这些类型表示具有嵌套属性的复杂结构,包括列表和映射。
集合 − 这些类型表示多个标量,包括字符串集合、数字集合和二进制集合。
请记住,DynamoDB 作为一种无模式 NoSQL 数据库,在创建表时不需要属性或数据类型定义。它只需要一个主键属性数据类型,这与需要在表创建时指定列数据类型的 RDBMS 形成对比。
标量
数字 − 限制为 38 位数字,可以是正数、负数或零。
字符串 − 使用 UTF-8 的 Unicode,最小长度 >0,最大长度为 400KB。
二进制 − 存储任何二进制数据,例如加密数据、图像和压缩文本。DynamoDB 将其字节视为无符号。
布尔值 − 存储真或假。
空值 − 表示未知或未定义的状态。
文档
列表 − 存储有序的值集合,并使用方括号 ([...])。
映射 − 存储无序的名称-值对集合,并使用花括号 ({...})。
集合
集合必须包含相同类型(数字、字符串或二进制)的元素。对集合施加的唯一限制包括 400KB 的项目大小限制,并且每个元素都必须是唯一的。
操作数据类型
DynamoDB API 包含操作使用各种数据类型。您可以查看以下关键类型的选择 −
AttributeDefinition − 表示键表和索引模式。
Capacity − 表示表或索引消耗的吞吐量数量。
CreateGlobalSecondaryIndexAction − 表示添加到表的新的全局二级索引。
LocalSecondaryIndex − 表示本地二级索引属性。
ProvisionedThroughput − 表示索引或表的预置吞吐量。
PutRequest − 表示 PutItem 请求。
TableDescription − 表示表属性。
支持的 Java 数据类型
DynamoDB 为 Java 提供对基本数据类型、Set 集合和任意类型的支持。
DynamoDB - 创建表
创建表通常包括生成表、命名表、建立其主键属性以及设置属性数据类型。
使用 GUI 控制台、Java 或其他选项来执行这些任务。
使用 GUI 控制台创建表
通过访问控制台 https://console.aws.amazon.com/dynamodb 创建表。然后选择“创建表”选项。
我们的示例生成一个填充产品信息的表,其中产品通过 ID 号(数字属性)识别唯一属性。在创建表屏幕中,在表名称字段中输入表名称;在分区键字段中输入主键(ID);并为数据类型输入“Number”。
输入所有信息后,选择创建。
使用 Java 创建表
使用 Java 创建相同的表。其主键包含以下两个属性 −
ID − 使用分区键和 ScalarAttributeType N,表示数字。
Nomenclature − 使用排序键和 ScalarAttributeType S,表示字符串。
Java 使用createTable 方法生成表;并在调用中指定表名称、主键属性和属性数据类型。
您可以查看以下示例 −
import java.util.Arrays;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType;
public class ProductsCreateTable {
public static void main(String[] args) throws Exception {
AmazonDynamoDBClient client = new AmazonDynamoDBClient()
.withEndpoint("https://:8000");
DynamoDB dynamoDB = new DynamoDB(client);
String tableName = "Products";
try {
System.out.println("Creating the table, wait...");
Table table = dynamoDB.createTable (tableName,
Arrays.asList (
new KeySchemaElement("ID", KeyType.HASH), // the partition key
// the sort key
new KeySchemaElement("Nomenclature", KeyType.RANGE)
),
Arrays.asList (
new AttributeDefinition("ID", ScalarAttributeType.N),
new AttributeDefinition("Nomenclature", ScalarAttributeType.S)
),
new ProvisionedThroughput(10L, 10L)
);
table.waitForActive();
System.out.println("Table created successfully. Status: " +
table.getDescription().getTableStatus());
} catch (Exception e) {
System.err.println("Cannot create the table: ");
System.err.println(e.getMessage());
}
}
}
在上述示例中,请注意端点:.withEndpoint。
它表示通过使用 localhost 使用本地安装。此外,请注意所需的ProvisionedThroughput 参数,本地安装会忽略此参数。
DynamoDB - 加载表
加载表通常包括创建源文件、确保源文件符合与 DynamoDB 兼容的语法、将源文件发送到目标,然后确认成功填充。
使用 GUI 控制台、Java 或其他选项来执行此任务。
使用 GUI 控制台加载表
使用命令行和控制台的组合加载数据。您可以通过多种方式加载数据,其中一些如下 −
- 控制台
- 命令行
- 代码以及
- 数据管道(本教程后面将讨论的功能)
但是,为了提高速度,此示例同时使用 Shell 和控制台。首先,使用以下语法将源数据加载到目标中 −
aws dynamodb batch-write-item -–request-items file://[filename]
例如 −
aws dynamodb batch-write-item -–request-items file://MyProductData.json
通过访问以下控制台来验证操作是否成功 −
https://console.aws.amazon.com/dynamodb
从导航窗格中选择表,然后从表列表中选择目标表。
选择项目选项卡以检查用于填充表的数据。选择取消以返回到表列表。
使用 Java 加载表
首先创建源文件,然后使用 Java。我们的源文件使用 JSON 格式。每个产品具有两个主键属性(ID 和 Nomenclature)和一个 JSON 映射(Stat)−
[
{
"ID" : ... ,
"Nomenclature" : ... ,
"Stat" : { ... }
},
{
"ID" : ... ,
"Nomenclature" : ... ,
"Stat" : { ... }
},
...
]
您可以查看以下示例 −
{
"ID" : 122,
"Nomenclature" : "Particle Blaster 5000",
"Stat" : {
"Manufacturer" : "XYZ Inc.",
"sales" : "1M+",
"quantity" : 500,
"img_src" : "http://www.xyz.com/manuals/particleblaster5000.jpg",
"description" : "A laser cutter used in plastic manufacturing."
}
}
下一步是将文件放在应用程序使用的目录中。
Java 主要使用putItem 和path 方法来执行加载。
您可以查看以下代码示例以处理文件并加载它 −
import java.io.File;
import java.util.Iterator;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.node.ObjectNode;
public class ProductsLoadData {
public static void main(String[] args) throws Exception {
AmazonDynamoDBClient client = new AmazonDynamoDBClient()
.withEndpoint("https://:8000");
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("Products");
JsonParser parser = new JsonFactory()
.createParser(new File("productinfo.json"));
JsonNode rootNode = new ObjectMapper().readTree(parser);
Iterator<JsonNode> iter = rootNode.iterator();
ObjectNode currentNode;
while (iter.hasNext()) {
currentNode = (ObjectNode) iter.next();
int ID = currentNode.path("ID").asInt();
String Nomenclature = currentNode.path("Nomenclature").asText();
try {
table.putItem(new Item()
.withPrimaryKey("ID", ID, "Nomenclature", Nomenclature)
.withJSON("Stat", currentNode.path("Stat").toString()));
System.out.println("Successful load: " + ID + " " + Nomenclature);
} catch (Exception e) {
System.err.println("Cannot add product: " + ID + " " + Nomenclature);
System.err.println(e.getMessage());
break;
}
}
parser.close();
}
}
DynamoDB - 查询表
查询表主要需要选择表、指定分区键并执行查询;可以选择使用二级索引并通过扫描操作执行更深入的过滤。
使用 GUI 控制台、Java 或其他选项来执行此任务。
使用 GUI 控制台查询表
使用先前创建的表执行一些简单的查询。首先,在 https://console.aws.amazon.com/dynamodb 中打开控制台
从导航窗格中选择表,然后从表列表中选择Reply。然后选择项目选项卡以查看加载的数据。
选择创建项目按钮下方的数据过滤链接(“扫描:[表] Reply”)。
在过滤屏幕中,为操作选择查询。输入相应的分区键值,然后单击开始。
然后,Reply 表将返回匹配的项目。
使用 Java 查询表
在 Java 中使用 query 方法执行数据检索操作。它需要指定分区键值,排序键为可选。
通过首先创建描述参数的querySpec 对象来编写 Java 查询。然后将对象传递给 query 方法。我们使用先前示例中的分区键。
您可以查看以下示例 −
import java.util.HashMap;
import java.util.Iterator;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
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.NameMap;
public class ProductsQuery {
public static void main(String[] args) throws Exception {
AmazonDynamoDBClient client = new AmazonDynamoDBClient()
.withEndpoint("https://:8000");
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("Products");
HashMap<String, String> nameMap = new HashMap<String, String>();
nameMap.put("#ID", "ID");
HashMap<String, Object> valueMap = new HashMap<String, Object>();
valueMap.put(":xxx", 122);
QuerySpec querySpec = new QuerySpec()
.withKeyConditionExpression("#ID = :xxx")
.withNameMap(new NameMap().with("#ID", "ID"))
.withValueMap(valueMap);
ItemCollection<QueryOutcome> items = null;
Iterator<Item> iterator = null;
Item item = null;
try {
System.out.println("Product with the ID 122");
items = table.query(querySpec);
iterator = items.iterator();
while (iterator.hasNext()) {
item = iterator.next();
System.out.println(item.getNumber("ID") + ": "
+ item.getString("Nomenclature"));
}
} catch (Exception e) {
System.err.println("Cannot find products with the ID number 122");
System.err.println(e.getMessage());
}
}
}
请注意,查询使用了分区键,但是,二级索引提供了另一种查询选项。它们的灵活性允许查询非键属性,本教程稍后将讨论此主题。
扫描方法还支持通过收集所有表数据来执行检索操作。可选的 .withFilterExpression 可防止指定条件之外的项出现在结果中。
在本教程的后面,我们将详细讨论扫描。现在,请查看以下示例 -
import java.util.Iterator;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
import com.amazonaws.services.dynamodbv2.document.ScanOutcome;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.spec.ScanSpec;
import com.amazonaws.services.dynamodbv2.document.utils.NameMap;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
public class ProductsScan {
public static void main(String[] args) throws Exception {
AmazonDynamoDBClient client = new AmazonDynamoDBClient()
.withEndpoint("https://:8000");
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("Products");
ScanSpec scanSpec = new ScanSpec()
.withProjectionExpression("#ID, Nomenclature , stat.sales")
.withFilterExpression("#ID between :start_id and :end_id")
.withNameMap(new NameMap().with("#ID", "ID"))
.withValueMap(new ValueMap().withNumber(":start_id", 120)
.withNumber(":end_id", 129));
try {
ItemCollection<ScanOutcome> items = table.scan(scanSpec);
Iterator<Item> iter = items.iterator();
while (iter.hasNext()) {
Item item = iter.next();
System.out.println(item.toString());
}
} catch (Exception e) {
System.err.println("Cannot perform a table scan:");
System.err.println(e.getMessage());
}
}
}
DynamoDB - 删除表
在本章中,我们将讨论如何删除表以及删除表的不同方法。
表删除是一个简单的操作,只需要表名即可。可以使用 GUI 控制台、Java 或任何其他选项来执行此任务。
使用 GUI 控制台删除表
首先访问以下控制台,执行删除操作 -
https://console.aws.amazon.com/dynamodb.
从导航窗格中选择表,然后从表列表中选择要删除的表,如下面的屏幕截图所示。
最后,选择删除表。选择“删除表”后,将显示一个确认消息。然后您的表将被删除。
使用 Java 删除表
使用delete方法删除表。下面给出一个示例来更好地解释这个概念。
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Table;
public class ProductsDeleteTable {
public static void main(String[] args) throws Exception {
AmazonDynamoDBClient client = new AmazonDynamoDBClient()
.withEndpoint("https://:8000");
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("Products");
try {
System.out.println("Performing table delete, wait...");
table.delete();
table.waitForDelete();
System.out.print("Table successfully deleted.");
} catch (Exception e) {
System.err.println("Cannot perform table delete: ");
System.err.println(e.getMessage());
}
}
}
DynamoDB - API 接口
DynamoDB 提供了一套广泛且强大的 API 工具,用于表操作、数据读取和数据修改。
Amazon 建议使用AWS SDK(例如,Java SDK)而不是调用低级 API。这些库使直接与低级 API 交互变得不必要。这些库简化了身份验证、序列化和连接等常见任务。
操作表
DynamoDB 提供了五个用于表管理的低级操作 -
CreateTable - 此操作创建表,并包含用户设置的吞吐量。它要求您设置主键,无论是复合键还是简单键。它还允许一个或多个二级索引。
ListTables - 此操作提供当前 AWS 用户帐户中与其端点关联的所有表的列表。
UpdateTable - 此操作更改吞吐量和全局二级索引吞吐量。
DescribeTable - 此操作提供表元数据;例如,状态、大小和索引。
DeleteTable - 此操作简单地擦除表及其索引。
读取数据
DynamoDB 提供了四个用于数据读取的低级操作 -
GetItem - 它接受主键并返回关联项的属性。它允许更改其默认的最终一致读取设置。
BatchGetItem - 它通过主键对多个项执行多个 GetItem 请求,并可以选择一个或多个表。它的返回项不超过 100 个,并且必须保持在 16MB 以内。它允许最终一致读取和强一致读取。
Scan - 它读取所有表项并生成最终一致的结果集。您可以通过条件过滤结果。它避免使用索引并扫描整个表,因此不要将其用于需要可预测性的查询。
Query - 它返回单个或多个表项或二级索引项。它使用分区键的指定值,并允许使用比较运算符缩小范围。它支持两种一致性类型,并且每个响应的大小都受 1MB 的限制。
修改数据
DynamoDB 提供了四个用于数据修改的低级操作 -
PutItem - 此操作创建新项或替换现有项。如果发现相同的主键,则默认情况下,它会替换该项。条件运算符允许您绕过默认值,并且仅在某些条件下替换项。
BatchWriteItem - 此操作执行多个 PutItem 和 DeleteItem 请求,以及多个表上的请求。如果一个请求失败,它不会影响整个操作。它的上限为 25 个项目,大小为 16MB。
UpdateItem - 它更改现有项的属性,并允许使用条件运算符仅在某些条件下执行更新。
DeleteItem - 它使用主键擦除项,并且还允许使用条件运算符指定删除条件。
DynamoDB - 创建项目
在 DynamoDB 中创建项主要包括项和属性规范,以及指定条件的选项。每个项都作为一组属性存在,每个属性都有名称并分配某个类型的值。
值类型包括标量、文档或集合。项的大小限制为 400KB,并且可以包含任何数量的属性,只要它们适合该限制即可。名称和值的大小(二进制和 UTF-8 长度)决定了项的大小。使用短属性名称有助于最小化项的大小。
注意 - 您必须指定所有主键属性,其中主键仅需要分区键;复合键需要分区键和排序键。
另外,请记住表没有预定义的架构。您可以在一个表中存储截然不同的数据集。
可以使用 GUI 控制台、Java 或其他工具来执行此任务。
如何使用 GUI 控制台创建项?
导航到控制台。在左侧的导航窗格中,选择表。选择用作目标的表名,然后选择项选项卡,如下面的屏幕截图所示。
选择创建项。创建项屏幕提供了一个界面,用于输入所需的属性值。还必须输入任何二级索引。
如果您需要更多属性,请选择消息左侧的操作菜单。然后选择追加和所需的數據類型。
输入所有必要信息后,选择保存以添加项。
如何在项创建中使用 Java?
在项创建操作中使用 Java 包括创建 DynamoDB 类实例、Table 类实例、Item 类实例,并指定您要创建的项的主键和属性。然后使用 putItem 方法添加新项。
示例
DynamoDB dynamoDB = new DynamoDB (new AmazonDynamoDBClient(
new ProfileCredentialsProvider()));
Table table = dynamoDB.getTable("ProductList");
// Spawn a related items list
List<Number> RELItems = new ArrayList<Number>();
RELItems.add(123);
RELItems.add(456);
RELItems.add(789);
//Spawn a product picture map
Map<String, String> photos = new HashMap<String, String>();
photos.put("Anterior", "http://xyz.com/products/101_front.jpg");
photos.put("Posterior", "http://xyz.com/products/101_back.jpg");
photos.put("Lateral", "http://xyz.com/products/101_LFTside.jpg");
//Spawn a product review map
Map<String, List<String>> prodReviews = new HashMap<String, List<String>>();
List<String> fiveStarRVW = new ArrayList<String>();
fiveStarRVW.add("Shocking high performance.");
fiveStarRVW.add("Unparalleled in its market.");
prodReviews.put("5 Star", fiveStarRVW);
List<String> oneStarRVW = new ArrayList<String>();
oneStarRVW.add("The worst offering in its market.");
prodReviews.put("1 Star", oneStarRVW);
// Generate the item
Item item = new Item()
.withPrimaryKey("Id", 101)
.withString("Nomenclature", "PolyBlaster 101")
.withString("Description", "101 description")
.withString("Category", "Hybrid Power Polymer Cutter")
.withString("Make", "Brand – XYZ")
.withNumber("Price", 50000)
.withString("ProductCategory", "Laser Cutter")
.withBoolean("Availability", true)
.withNull("Qty")
.withList("ItemsRelated", RELItems)
.withMap("Images", photos)
.withMap("Reviews", prodReviews);
// Add item to the table
PutItemOutcome outcome = table.putItem(item);
您还可以查看以下更大的示例。
注意 - 以下示例可能假设之前已创建数据源。在尝试执行之前,请获取支持库并创建必要的数据源(具有所需特征的表或其他引用的源)。
以下示例还使用了 Eclipse IDE、AWS 凭证文件以及 Eclipse AWS Java 项目中的 AWS 工具包。
package com.amazonaws.codesamples.document;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DeleteItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.UpdateItemOutcome;
import com.amazonaws.services.dynamodbv2.document.spec.DeleteItemSpec;
import com.amazonaws.services.dynamodbv2.document.spec.UpdateItemSpec;
import com.amazonaws.services.dynamodbv2.document.utils.NameMap;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.ReturnValue;
public class CreateItemOpSample {
static DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient (
new ProfileCredentialsProvider()));
static String tblName = "ProductList";
public static void main(String[] args) throws IOException {
createItems();
retrieveItem();
// Execute updates
updateMultipleAttributes();
updateAddNewAttribute();
updateExistingAttributeConditionally();
// Item deletion
deleteItem();
}
private static void createItems() {
Table table = dynamoDB.getTable(tblName);
try {
Item item = new Item()
.withPrimaryKey("ID", 303)
.withString("Nomenclature", "Polymer Blaster 4000")
.withStringSet( "Manufacturers",
new HashSet<String>(Arrays.asList("XYZ Inc.", "LMNOP Inc.")))
.withNumber("Price", 50000)
.withBoolean("InProduction", true)
.withString("Category", "Laser Cutter");
table.putItem(item);
item = new Item()
.withPrimaryKey("ID", 313)
.withString("Nomenclature", "Agitatatron 2000")
.withStringSet( "Manufacturers",
new HashSet<String>(Arrays.asList("XYZ Inc,", "CDE Inc.")))
.withNumber("Price", 40000)
.withBoolean("InProduction", true)
.withString("Category", "Agitator");
table.putItem(item);
} catch (Exception e) {
System.err.println("Cannot create items.");
System.err.println(e.getMessage());
}
}
}
DynamoDB - 获取项目
在 DynamoDB 中检索项需要使用 GetItem,并指定表名和项主键。请确保包含完整的主键,而不是省略部分主键。
例如,省略复合键的排序键。
GetItem 的行为符合三个默认值 -
- 它作为最终一致读取执行。
- 它提供所有属性。
- 它没有详细说明其容量单位消耗。
这些参数允许您覆盖默认的 GetItem 行为。
检索项
DynamoDB 通过在多个服务器上维护项的多个副本确保可靠性。每次成功的写入都会创建这些副本,但需要大量时间来执行;这意味着最终一致性。这意味着您不能在写入项后立即尝试读取。
您可以更改 GetItem 的默认最终一致读取,但是,获取更最新数据所需的成本仍然是消耗更多容量单位;具体来说,是两倍。请注意,DynamoDB 通常在一秒钟内实现所有副本之间的一致性。
您可以使用 GUI 控制台、Java 或其他工具来执行此任务。
使用 Java 检索项
在项检索操作中使用 Java 需要创建 DynamoDB 类实例、Table 类实例,并调用 Table 实例的 getItem 方法。然后指定项的主键。
您可以查看以下示例 −
DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient(
new ProfileCredentialsProvider()));
Table table = dynamoDB.getTable("ProductList");
Item item = table.getItem("IDnum", 109);
在某些情况下,您需要为此操作指定参数。
以下示例使用.withProjectionExpression和GetItemSpec进行检索规范 -
GetItemSpec spec = new GetItemSpec()
.withPrimaryKey("IDnum", 122)
.withProjectionExpression("IDnum, EmployeeName, Department")
.withConsistentRead(true);
Item item = table.getItem(spec);
System.out.println(item.toJSONPretty());
您还可以查看以下更大的示例以更好地理解。
注意 - 以下示例可能假设之前已创建数据源。在尝试执行之前,请获取支持库并创建必要的数据源(具有所需特征的表或其他引用的源)。
此示例还使用了 Eclipse IDE、AWS 凭证文件以及 Eclipse AWS Java 项目中的 AWS 工具包。
package com.amazonaws.codesamples.document;
import java.io.IOException
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DeleteItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.UpdateItemOutcome;
import com.amazonaws.services.dynamodbv2.document.spec.DeleteItemSpec;
import com.amazonaws.services.dynamodbv2.document.spec.UpdateItemSpec;
import com.amazonaws.services.dynamodbv2.document.utils.NameMap;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.ReturnValue;
public class GetItemOpSample {
static DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient(
new ProfileCredentialsProvider()));
static String tblName = "ProductList";
public static void main(String[] args) throws IOException {
createItems();
retrieveItem();
// Execute updates
updateMultipleAttributes();
updateAddNewAttribute();
updateExistingAttributeConditionally();
// Item deletion
deleteItem();
}
private static void createItems() {
Table table = dynamoDB.getTable(tblName);
try {
Item item = new Item()
.withPrimaryKey("ID", 303)
.withString("Nomenclature", "Polymer Blaster 4000")
.withStringSet( "Manufacturers",
new HashSet<String>(Arrays.asList("XYZ Inc.", "LMNOP Inc.")))
.withNumber("Price", 50000)
.withBoolean("InProduction", true)
.withString("Category", "Laser Cutter");
table.putItem(item);
item = new Item()
.withPrimaryKey("ID", 313)
.withString("Nomenclature", "Agitatatron 2000")
.withStringSet( "Manufacturers",
new HashSet<String>(Arrays.asList("XYZ Inc,", "CDE Inc.")))
.withNumber("Price", 40000)
.withBoolean("InProduction", true)
.withString("Category", "Agitator");
table.putItem(item);
} catch (Exception e) {
System.err.println("Cannot create items.");
System.err.println(e.getMessage());
}
}
private static void retrieveItem() {
Table table = dynamoDB.getTable(tableName);
try {
Item item = table.getItem("ID", 303, "ID, Nomenclature, Manufacturers", null);
System.out.println("Displaying retrieved items...");
System.out.println(item.toJSONPretty());
} catch (Exception e) {
System.err.println("Cannot retrieve items.");
System.err.println(e.getMessage());
}
}
}
DynamoDB - 更新项目
在 DynamoDB 中更新项主要包括为项指定完整的主键和表名。它需要您要修改的每个属性的新值。此操作使用UpdateItem,它修改现有项或在发现缺少项时创建它们。
在更新中,您可能希望通过在操作之前和之后显示原始值和新值来跟踪更改。UpdateItem 使用ReturnValues参数来实现此目的。
注意 - 此操作不报告容量单位消耗,但您可以使用ReturnConsumedCapacity参数。
可以使用 GUI 控制台、Java 或任何其他工具来执行此任务。
如何使用 GUI 工具更新项?
导航到控制台。在左侧的导航窗格中,选择表。选择所需的表,然后选择项选项卡。
选择要更新的项,然后选择操作 | 编辑。
在编辑项窗口中修改任何必要的属性或值。
使用 Java 更新项
在项更新操作中使用 Java 需要创建一个 Table 类实例,并调用其updateItem方法。然后您指定项的主键,并提供一个UpdateExpression,详细说明属性修改。
以下是相同的示例 -
DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient(
new ProfileCredentialsProvider()));
Table table = dynamoDB.getTable("ProductList");
Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#M", "Make");
expressionAttributeNames.put("#P", "Price
expressionAttributeNames.put("#N", "ID");
Map<String, Object> expressionAttributeValues = new HashMap<String, Object>();
expressionAttributeValues.put(":val1",
new HashSet<String>(Arrays.asList("Make1","Make2")));
expressionAttributeValues.put(":val2", 1); //Price
UpdateItemOutcome outcome = table.updateItem(
"internalID", // key attribute name
111, // key attribute value
"add #M :val1 set #P = #P - :val2 remove #N", // UpdateExpression
expressionAttributeNames,
expressionAttributeValues);
updateItem方法还允许指定条件,这可以在以下示例中看到 -
Table table = dynamoDB.getTable("ProductList");
Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#P", "Price");
Map<String, Object> expressionAttributeValues = new HashMap<String, Object>();
expressionAttributeValues.put(":val1", 44); // change Price to 44
expressionAttributeValues.put(":val2", 15); // only if currently 15
UpdateItemOutcome outcome = table.updateItem (new PrimaryKey("internalID",111),
"set #P = :val1", // Update
"#P = :val2", // Condition
expressionAttributeNames,
expressionAttributeValues);
使用计数器更新项
DynamoDB 允许原子计数器,这意味着使用 UpdateItem 增加/减少属性值而不影响其他请求;此外,计数器始终更新。
以下是一个解释如何执行此操作的示例。
注意 - 以下示例可能假设之前已创建数据源。在尝试执行之前,请获取支持库并创建必要的数据源(具有所需特征的表或其他引用的源)。
此示例还使用了 Eclipse IDE、AWS 凭证文件以及 Eclipse AWS Java 项目中的 AWS 工具包。
package com.amazonaws.codesamples.document;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DeleteItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.UpdateItemOutcome;
import com.amazonaws.services.dynamodbv2.document.spec.DeleteItemSpec;
import com.amazonaws.services.dynamodbv2.document.spec.UpdateItemSpec;
import com.amazonaws.services.dynamodbv2.document.utils.NameMap;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.ReturnValue;
public class UpdateItemOpSample {
static DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient(
new ProfileCredentialsProvider()));
static String tblName = "ProductList";
public static void main(String[] args) throws IOException {
createItems();
retrieveItem();
// Execute updates
updateMultipleAttributes();
updateAddNewAttribute();
updateExistingAttributeConditionally();
// Item deletion
deleteItem();
}
private static void createItems() {
Table table = dynamoDB.getTable(tblName);
try {
Item item = new Item()
.withPrimaryKey("ID", 303)
.withString("Nomenclature", "Polymer Blaster 4000")
.withStringSet( "Manufacturers",
new HashSet<String>(Arrays.asList("XYZ Inc.", "LMNOP Inc.")))
.withNumber("Price", 50000)
.withBoolean("InProduction", true)
.withString("Category", "Laser Cutter");
table.putItem(item);
item = new Item()
.withPrimaryKey("ID", 313)
.withString("Nomenclature", "Agitatatron 2000")
.withStringSet( "Manufacturers",
new HashSet<String>(Arrays.asList("XYZ Inc,", "CDE Inc.")))
.withNumber("Price", 40000)
.withBoolean("InProduction", true)
.withString("Category", "Agitator");
table.putItem(item);
} catch (Exception e) {
System.err.println("Cannot create items.");
System.err.println(e.getMessage());
}
}
private static void updateAddNewAttribute() {
Table table = dynamoDB.getTable(tableName);
try {
Map<String, String> expressionAttributeNames = new HashMap<String, String>();
expressionAttributeNames.put("#na", "NewAttribute");
UpdateItemSpec updateItemSpec = new UpdateItemSpec()
.withPrimaryKey("ID", 303)
.withUpdateExpression("set #na = :val1")
.withNameMap(new NameMap()
.with("#na", "NewAttribute"))
.withValueMap(new ValueMap()
.withString(":val1", "A value"))
.withReturnValues(ReturnValue.ALL_NEW);
UpdateItemOutcome outcome = table.updateItem(updateItemSpec);
// Confirm
System.out.println("Displaying updated item...");
System.out.println(outcome.getItem().toJSONPretty());
} catch (Exception e) {
System.err.println("Cannot add an attribute in " + tableName);
System.err.println(e.getMessage());
}
}
}
DynamoDB - 删除项目
在 DynamoDB 中删除项只需要提供表名和项键。还强烈建议使用条件表达式,这对于避免删除错误的项将是必要的。
像往常一样,您可以使用 GUI 控制台、Java 或任何其他所需的工具来执行此任务。
使用 GUI 控制台删除项
导航到控制台。在左侧的导航窗格中,选择表。然后选择表名和项选项卡。
选择要删除的项,然后选择操作 | 删除。
然后会出现一个删除项目(s)对话框,如下面的屏幕截图所示。选择“删除”以确认。
如何使用 Java 删除项目?
在项目删除操作中使用 Java 只需创建 DynamoDB 客户端实例,并通过使用项目的键调用deleteItem方法。
您可以查看以下示例,其中已详细说明。
DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient(
new ProfileCredentialsProvider()));
Table table = dynamoDB.getTable("ProductList");
DeleteItemOutcome outcome = table.deleteItem("IDnum", 151);
您还可以指定参数以防止错误删除。只需使用ConditionExpression。
例如 −
Map<String,Object> expressionAttributeValues = new HashMap<String,Object>();
expressionAttributeValues.put(":val", false);
DeleteItemOutcome outcome = table.deleteItem("IDnum",151,
"Ship = :val",
null, // doesn't use ExpressionAttributeNames
expressionAttributeValues);
以下是一个更大的示例,以便更好地理解。
注意 - 以下示例可能假设之前已创建数据源。在尝试执行之前,请获取支持库并创建必要的数据源(具有所需特征的表或其他引用的源)。
此示例还使用了 Eclipse IDE、AWS 凭证文件以及 Eclipse AWS Java 项目中的 AWS 工具包。
package com.amazonaws.codesamples.document;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DeleteItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.UpdateItemOutcome;
import com.amazonaws.services.dynamodbv2.document.spec.DeleteItemSpec;
import com.amazonaws.services.dynamodbv2.document.spec.UpdateItemSpec;
import com.amazonaws.services.dynamodbv2.document.utils.NameMap;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.ReturnValue;
public class DeleteItemOpSample {
static DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient(
new ProfileCredentialsProvider()));
static String tblName = "ProductList";
public static void main(String[] args) throws IOException {
createItems();
retrieveItem();
// Execute updates
updateMultipleAttributes();
updateAddNewAttribute();
updateExistingAttributeConditionally();
// Item deletion
deleteItem();
}
private static void createItems() {
Table table = dynamoDB.getTable(tblName);
try {
Item item = new Item()
.withPrimaryKey("ID", 303)
.withString("Nomenclature", "Polymer Blaster 4000")
.withStringSet( "Manufacturers",
new HashSet<String>(Arrays.asList("XYZ Inc.", "LMNOP Inc.")))
.withNumber("Price", 50000)
.withBoolean("InProduction", true)
.withString("Category", "Laser Cutter");
table.putItem(item);
item = new Item()
.withPrimaryKey("ID", 313)
.withString("Nomenclature", "Agitatatron 2000")
.withStringSet( "Manufacturers",
new HashSet<String>(Arrays.asList("XYZ Inc,", "CDE Inc.")))
.withNumber("Price", 40000)
.withBoolean("InProduction", true)
.withString("Category", "Agitator");
table.putItem(item);
} catch (Exception e) {
System.err.println("Cannot create items.");
System.err.println(e.getMessage());
}
}
private static void deleteItem() {
Table table = dynamoDB.getTable(tableName);
try {
DeleteItemSpec deleteItemSpec = new DeleteItemSpec()
.withPrimaryKey("ID", 303)
.withConditionExpression("#ip = :val")
.withNameMap(new NameMap()
.with("#ip", "InProduction"))
.withValueMap(new ValueMap()
.withBoolean(":val", false))
.withReturnValues(ReturnValue.ALL_OLD);
DeleteItemOutcome outcome = table.deleteItem(deleteItemSpec);
// Confirm
System.out.println("Displaying deleted item...");
System.out.println(outcome.getItem().toJSONPretty());
} catch (Exception e) {
System.err.println("Cannot delete item in " + tableName);
System.err.println(e.getMessage());
}
}
}
DynamoDB - 批量写入
批量写入通过创建或删除多个项目来操作多个项目。这些操作利用BatchWriteItem,其限制为不超过 16MB 写入和 25 个请求。每个项目都遵守 400KB 的大小限制。批量写入也不能执行项目更新。
什么是批量写入?
批量写入可以操作多个表中的项目。每个单独的请求都会发生操作调用,这意味着操作不会相互影响,并且允许异构混合;例如,在一个批次中有一个PutItem和三个DeleteItem请求,而 PutItem 请求的失败不会影响其他请求。失败的请求会导致操作返回与每个失败请求相关的信息(键和数据)。
注意 - 如果 DynamoDB 返回任何未经处理的项目,请重试;但是,使用回退方法以避免基于过载的另一个请求失败。
当以下一个或多个语句被证明为真时,DynamoDB 会拒绝批量写入操作 -
请求超过了预置的吞吐量。
请求尝试使用BatchWriteItems更新项目。
请求对单个项目执行多个操作。
请求的表不存在。
请求中的项目属性与目标不匹配。
请求超过大小限制。
批量写入需要某些RequestItem参数 -
删除操作需要DeleteRequest键子元素,即属性名称和值。
PutRequest项目需要一个Item 子元素,即属性和属性值映射。
响应 - 成功操作将导致 HTTP 200 响应,这表示诸如消耗的容量单位、表处理指标和任何未处理的项目等特征。
使用 Java 进行批量写入
通过创建 DynamoDB 类实例、描述所有操作的TableWriteItems类实例以及调用batchWriteItem方法来使用 TableWriteItems 对象来执行批量写入。
注意 - 必须为批量写入到多个表中的每个表创建一个 TableWriteItems 实例。此外,检查您的请求响应中是否存在任何未处理的请求。
您可以查看以下批量写入示例 -
DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient(
new ProfileCredentialsProvider()));
TableWriteItems forumTableWriteItems = new TableWriteItems("Forum")
.withItemsToPut(
new Item()
.withPrimaryKey("Title", "XYZ CRM")
.withNumber("Threads", 0));
TableWriteItems threadTableWriteItems = new TableWriteItems(Thread)
.withItemsToPut(
new Item()
.withPrimaryKey("ForumTitle","XYZ CRM","Topic","Updates")
.withHashAndRangeKeysToDelete("ForumTitle","A partition key value",
"Product Line 1", "A sort key value"));
BatchWriteItemOutcome outcome = dynamoDB.batchWriteItem (
forumTableWriteItems, threadTableWriteItems);
以下程序是另一个更大的示例,以便更好地了解如何使用 Java 进行批量写入。
注意 - 以下示例可能假设存在先前创建的数据源。在尝试执行之前,请获取支持库并创建必要的数据源(具有所需特征的表或其他引用的源)。
此示例还使用 Eclipse IDE、AWS 凭证文件以及 Eclipse AWS Java 项目中的 AWS 工具包。
package com.amazonaws.codesamples.document;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.BatchWriteItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.TableWriteItems;
import com.amazonaws.services.dynamodbv2.model.WriteRequest;
public class BatchWriteOpSample {
static DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient(
new ProfileCredentialsProvider()));
static String forumTableName = "Forum";
static String threadTableName = "Thread";
public static void main(String[] args) throws IOException {
batchWriteMultiItems();
}
private static void batchWriteMultiItems() {
try {
// Place new item in Forum
TableWriteItems forumTableWriteItems = new TableWriteItems(forumTableName)
//Forum
.withItemsToPut(new Item()
.withPrimaryKey("Name", "Amazon RDS")
.withNumber("Threads", 0));
// Place one item, delete another in Thread
// Specify partition key and range key
TableWriteItems threadTableWriteItems = new TableWriteItems(threadTableName)
.withItemsToPut(new Item()
.withPrimaryKey("ForumName","Product
Support","Subject","Support Thread 1")
.withString("Message", "New OS Thread 1 message")
.withHashAndRangeKeysToDelete("ForumName","Subject", "Polymer Blaster",
"Support Thread 100"));
System.out.println("Processing request...");
BatchWriteItemOutcome outcome = dynamoDB.batchWriteItem (
forumTableWriteItems, threadTableWriteItems);
do {
// Confirm no unprocessed items
Map<String, List<WriteRequest>> unprocessedItems
= outcome.getUnprocessedItems();
if (outcome.getUnprocessedItems().size() == 0) {
System.out.println("All items processed.");
} else {
System.out.println("Gathering unprocessed items...");
outcome = dynamoDB.batchWriteItemUnprocessed(unprocessedItems);
}
} while (outcome.getUnprocessedItems().size() > 0);
} catch (Exception e) {
System.err.println("Could not get items: ");
e.printStackTrace(System.err);
}
}
}
DynamoDB - 批量检索
批量检索操作返回一个或多个项目的属性。这些操作通常包括使用主键来识别所需的项目(s)。BatchGetItem操作受单个操作的限制及其自身唯一约束的限制。
批量检索操作中的以下请求会导致拒绝 -
- 请求超过 100 个项目。
- 发出超过吞吐量的请求。
批量检索操作对可能超出限制的请求执行部分处理。
例如 - 检索大小足以超出限制的多个项目的请求会导致部分请求处理,以及一条说明未处理部分的错误消息。在返回未处理的项目时,创建回退算法解决方案来管理此问题,而不是限制表。
BatchGet操作最终使用一致读取执行,需要修改才能使其成为强一致读取。它们还并行执行检索。
注意 - 返回项目的顺序。DynamoDB 不会对项目进行排序。它也不会指示请求的项目不存在。此外,这些请求会消耗容量单位。
所有 BatchGet 操作都需要RequestItems参数,例如读取一致性、属性名称和主键。
响应 - 成功操作将导致 HTTP 200 响应,这表示诸如消耗的容量单位、表处理指标和任何未处理的项目等特征。
使用 Java 进行批量检索
在 BatchGet 操作中使用 Java 需要创建一个 DynamoDB 类实例、描述项目的 Primary Key 值列表的TableKeysAndAttributes类实例,并将 TableKeysAndAttributes 对象传递给BatchGetItem方法。
以下是一个 BatchGet 操作的示例 -
DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient (
new ProfileCredentialsProvider()));
TableKeysAndAttributes forumTableKeysAndAttributes = new TableKeysAndAttributes
(forumTableName);
forumTableKeysAndAttributes.addHashOnlyPrimaryKeys (
"Title",
"Updates",
"Product Line 1"
);
TableKeysAndAttributes threadTableKeysAndAttributes = new TableKeysAndAttributes (
threadTableName);
threadTableKeysAndAttributes.addHashAndRangePrimaryKeys (
"ForumTitle",
"Topic",
"Product Line 1",
"P1 Thread 1",
"Product Line 1",
"P1 Thread 2",
"Product Line 2",
"P2 Thread 1"
);
BatchGetItemOutcome outcome = dynamoDB.batchGetItem (
forumTableKeysAndAttributes, threadTableKeysAndAttributes);
for (String tableName : outcome.getTableItems().keySet()) {
System.out.println("Table items " + tableName);
List<Item> items = outcome.getTableItems().get(tableName);
for (Item item : items) {
System.out.println(item);
}
}
您可以查看以下更大的示例。
注意 - 以下程序可能假设存在先前创建的数据源。在尝试执行之前,请获取支持库并创建必要的数据源(具有所需特征的表或其他引用的源)。
此程序还使用 Eclipse IDE、AWS 凭证文件以及 Eclipse AWS Java 项目中的 AWS 工具包。
package com.amazonaws.codesamples.document;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.BatchGetItemOutcome;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.TableKeysAndAttributes;
import com.amazonaws.services.dynamodbv2.model.KeysAndAttributes;
public class BatchGetOpSample {
static DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient (
new ProfileCredentialsProvider()));
static String forumTableName = "Forum";
static String threadTableName = "Thread";
public static void main(String[] args) throws IOException {
retrieveMultipleItemsBatchGet();
}
private static void retrieveMultipleItemsBatchGet() {
try {
TableKeysAndAttributes forumTableKeysAndAttributes =
new TableKeysAndAttributes(forumTableName);
//Create partition key
forumTableKeysAndAttributes.addHashOnlyPrimaryKeys (
"Name",
"XYZ Melt-O-tron",
"High-Performance Processing"
);
TableKeysAndAttributes threadTableKeysAndAttributes =
new TableKeysAndAttributes(threadTableName);
//Create partition key and sort key
threadTableKeysAndAttributes.addHashAndRangePrimaryKeys (
"ForumName",
"Subject",
"High-Performance Processing",
"HP Processing Thread One",
"High-Performance Processing",
"HP Processing Thread Two",
"Melt-O-Tron",
"MeltO Thread One"
);
System.out.println("Processing...");
BatchGetItemOutcome outcome = dynamoDB.batchGetItem(forumTableKeysAndAttributes,
threadTableKeysAndAttributes);
Map<String, KeysAndAttributes> unprocessed = null;
do {
for (String tableName : outcome.getTableItems().keySet()) {
System.out.println("Table items for " + tableName);
List<Item> items = outcome.getTableItems().get(tableName);
for (Item item : items) {
System.out.println(item.toJSONPretty());
}
}
// Confirm no unprocessed items
unprocessed = outcome.getUnprocessedKeys();
if (unprocessed.isEmpty()) {
System.out.println("All items processed.");
} else {
System.out.println("Gathering unprocessed items...");
outcome = dynamoDB.batchGetItemUnprocessed(unprocessed);
}
} while (!unprocessed.isEmpty());
} catch (Exception e) {
System.err.println("Could not get items.");
System.err.println(e.getMessage());
}
}
}
DynamoDB - 查询
查询通过主键查找项目或二级索引。执行查询需要分区键和特定值,或排序键和值;可以选择使用比较进行过滤。查询的默认行为包括返回与提供的 Primary Key 关联的每个项目的每个属性。但是,您可以使用ProjectionExpression参数指定所需的属性。
查询利用KeyConditionExpression参数选择项目,这需要以等式条件的形式提供分区键名称和值。您还可以选择为任何存在的排序键提供附加条件。
排序键条件的一些示例如下 -
| 序号 | 条件和描述 |
|---|---|
| 1 | x = y 如果属性 x 等于 y,则其计算结果为真。 |
| 2 | x < y 如果 x 小于 y,则其计算结果为真。 |
| 3 | x <= y 如果 x 小于或等于 y,则其计算结果为真。 |
| 4 | x > y 如果 x 大于 y,则其计算结果为真。 |
| 5 | x >= y 如果 x 大于或等于 y,则其计算结果为真。 |
| 6 | x BETWEEN y AND z 如果 x 大于或等于 y 且小于或等于 z,则其计算结果为真。 |
DynamoDB 还支持以下函数:begins_with (x, substr)
如果属性 x 以指定的字符串开头,则其计算结果为真。
以下条件必须符合某些要求 -
属性名称必须以 a-z 或 A-Z 集合中的字符开头。
属性名称的第二个字符必须位于 a-z、A-Z 或 0-9 集合中。
属性名称不能使用保留字。
不符合上述约束的属性名称可以定义一个占位符。
查询通过按排序键顺序执行检索以及使用任何存在的条件和过滤器表达式来处理。查询始终返回结果集,如果没有任何匹配项,则返回空结果集。
结果始终按排序键顺序返回,并根据数据类型进行排序,可修改的默认值为升序。
使用 Java 进行查询
Java 中的查询允许您查询表和二级索引。它们需要指定分区键和等式条件,可以选择指定排序键和条件。
Java 中查询的一般必需步骤包括创建 DynamoDB 类实例、目标表的 Table 类实例,以及调用 Table 实例的 query 方法以接收查询对象。
查询的响应包含一个ItemCollection对象,该对象提供所有返回的项目。
以下示例演示了详细的查询 -
DynamoDB dynamoDB = new DynamoDB (
new AmazonDynamoDBClient(new ProfileCredentialsProvider()));
Table table = dynamoDB.getTable("Response");
QuerySpec spec = new QuerySpec()
.withKeyConditionExpression("ID = :nn")
.withValueMap(new ValueMap()
.withString(":nn", "Product Line 1#P1 Thread 1"));
ItemCollection<QueryOutcome> items = table.query(spec);
Iterator<Item> iterator = items.iterator();
Item item = null;
while (iterator.hasNext()) {
item = iterator.next();
System.out.println(item.toJSONPretty());
}
query 方法支持各种可选参数。以下示例演示了如何利用这些参数 -
Table table = dynamoDB.getTable("Response");
QuerySpec spec = new QuerySpec()
.withKeyConditionExpression("ID = :nn and ResponseTM > :nn_responseTM")
.withFilterExpression("Author = :nn_author")
.withValueMap(new ValueMap()
.withString(":nn", "Product Line 1#P1 Thread 1")
.withString(":nn_responseTM", twoWeeksAgoStr)
.withString(":nn_author", "Member 123"))
.withConsistentRead(true);
ItemCollection<QueryOutcome> items = table.query(spec);
Iterator<Item> iterator = items.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().toJSONPretty());
}
您还可以查看以下更大的示例。
注意 - 以下程序可能假设存在先前创建的数据源。在尝试执行之前,请获取支持库并创建必要的数据源(具有所需特征的表或其他引用的源)。
此示例还使用 Eclipse IDE、AWS 凭证文件以及 Eclipse AWS Java 项目中的 AWS 工具包。
package com.amazonaws.codesamples.document;
import java.text.SimpleDateFormat;
import java.util.Date;
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.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
import com.amazonaws.services.dynamodbv2.document.Page;
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;
public class QueryOpSample {
static DynamoDB dynamoDB = new DynamoDB(
new AmazonDynamoDBClient(new ProfileCredentialsProvider()));
static String tableName = "Reply";
public static void main(String[] args) throws Exception {
String forumName = "PolyBlaster";
String threadSubject = "PolyBlaster Thread 1";
getThreadReplies(forumName, threadSubject);
}
private static void getThreadReplies(String forumName, String threadSubject) {
Table table = dynamoDB.getTable(tableName);
String replyId = forumName + "#" + threadSubject;
QuerySpec spec = new QuerySpec()
.withKeyConditionExpression("Id = :v_id")
.withValueMap(new ValueMap()
.withString(":v_id", replyId));
ItemCollection<QueryOutcome> items = table.query(spec);
System.out.println("\ngetThreadReplies results:");
Iterator<Item> iterator = items.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().toJSONPretty());
}
}
}
DynamoDB - 扫描
扫描操作读取所有表项目或二级索引。其默认功能会导致返回索引或表中所有项目的所有数据属性。使用ProjectionExpression参数过滤属性。
每次扫描都会返回结果集,即使在找不到匹配项的情况下也会返回结果集,这会导致返回空集。扫描最多检索 1MB,可以选择过滤数据。
注意 - 扫描的参数和过滤也适用于查询。
扫描操作的类型
过滤 - 扫描操作通过过滤器表达式提供精细过滤,这些表达式在扫描或查询之后修改数据;在返回结果之前。表达式使用比较运算符。它们的语法类似于条件表达式,除了键属性,过滤器表达式不允许键属性。您不能在过滤器表达式中使用分区键或排序键。
注意 - 1MB 限制适用于任何过滤应用之前。
吞吐量规范 - 扫描会消耗吞吐量,但是,消耗重点在于项目大小而不是返回的数据。无论您请求每个属性还是仅请求几个属性,消耗都保持不变,使用或不使用过滤器表达式也不会影响消耗。
分页 - DynamoDB 对结果进行分页,导致结果被划分为特定的页面。1MB 限制适用于返回的结果,当您超过它时,需要进行另一次扫描以收集其余数据。LastEvaluatedKey值允许您执行此后续扫描。只需将该值应用于ExclusiveStartkey。当LastEvaluatedKey值变为 null 时,操作已完成所有数据页面。但是,非 null 值并不自动意味着还有更多数据。只有 null 值表示状态。
Limit 参数 - limit 参数管理结果大小。DynamoDB 使用它来建立在返回数据之前要处理的项目数量,并且不超出范围。如果您设置了 x 值,DynamoDB 将返回前 x 个匹配项目。
LastEvaluatedKey 值也适用于 limit 参数产生部分结果的情况。使用它来完成扫描。
结果计数 - 查询和扫描的响应还包括与ScannedCount和 Count 相关的信息,这些信息量化了扫描/查询的项目和返回的项目。如果您不进行过滤,则它们的值相同。当您超过 1MB 时,计数仅表示已处理的部分。
一致性 - 查询结果和扫描结果是一致读取,但是,您也可以设置强一致读取。使用ConsistentRead参数更改此设置。
注意 - 一致读取设置会通过在设置为强一致时使用两倍的容量单位来影响消耗。
性能 - 查询比扫描提供更好的性能,因为扫描会爬取整个表或二级索引,导致响应缓慢和吞吐量消耗过大。扫描最适合小型表和过滤器较少的搜索,但是,您可以通过遵循一些最佳实践来设计精简扫描,例如避免突然加速的读取活动并利用并行扫描。
查询查找满足给定条件的特定键范围,其性能由它检索的数据量而不是键的数量决定。操作的参数和匹配项的数量会特别影响性能。
并行扫描
扫描操作默认情况下按顺序执行处理。然后,它们以 1MB 部分返回数据,这会提示应用程序获取下一部分。这会导致大型表和索引的扫描时间过长。
这种特性也意味着扫描可能无法始终充分利用可用的吞吐量。DynamoDB 将表数据分布在多个分区中;并且由于其单分区操作,扫描吞吐量仍然限制在单个分区。
此问题的解决方案来自将表或索引逻辑地划分为段。然后,“工作器”并行(并发)扫描段。它使用 Segment 和 **TotalSegments** 参数来指定某些工作器扫描的段,并指定处理的段的总数。
工作器数量
您必须尝试使用工作器值(Segment 参数)以实现最佳的应用程序性能。
**注意** - 使用大量工作器进行并行扫描会通过可能消耗所有吞吐量来影响吞吐量。使用 Limit 参数管理此问题,您可以使用它来阻止单个工作器消耗所有吞吐量。
以下是深度扫描示例。
注意 - 以下程序可能假设存在先前创建的数据源。在尝试执行之前,请获取支持库并创建必要的数据源(具有所需特征的表或其他引用的源)。
此示例还使用 Eclipse IDE、AWS 凭证文件以及 Eclipse AWS Java 项目中的 AWS 工具包。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
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.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
import com.amazonaws.services.dynamodbv2.document.ScanOutcome;
import com.amazonaws.services.dynamodbv2.document.Table;
public class ScanOpSample {
static DynamoDB dynamoDB = new DynamoDB(
new AmazonDynamoDBClient(new ProfileCredentialsProvider()));
static String tableName = "ProductList";
public static void main(String[] args) throws Exception {
findProductsUnderOneHun(); //finds products under 100 dollars
}
private static void findProductsUnderOneHun() {
Table table = dynamoDB.getTable(tableName);
Map<String, Object> expressionAttributeValues = new HashMap<String, Object>();
expressionAttributeValues.put(":pr", 100);
ItemCollection<ScanOutcome> items = table.scan (
"Price < :pr", //FilterExpression
"ID, Nomenclature, ProductCategory, Price", //ProjectionExpression
null, //No ExpressionAttributeNames
expressionAttributeValues);
System.out.println("Scanned " + tableName + " to find items under $100.");
Iterator<Item> iterator = items.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().toJSONPretty());
}
}
}
DynamoDB - 索引
DynamoDB 使用索引来访问主键属性以改进访问。它们可以加速应用程序访问和数据检索,并通过减少应用程序延迟来支持更好的性能。
二级索引
二级索引包含属性子集和备用键。您可以通过针对索引的查询或扫描操作来使用它。
其内容包括您投影或复制的属性。在创建过程中,您为索引定义备用键,以及您希望在索引中投影的任何属性。然后,DynamoDB 将属性复制到索引中,包括来自表的自主键属性。执行这些任务后,您只需像在表上执行一样使用查询/扫描即可。
DynamoDB 自动维护所有二级索引。在项目操作(例如添加或删除)上,它会更新目标表上的任何索引。
DynamoDB 提供两种类型的二级索引 -
**全局二级索引** - 此索引包含分区键和排序键,它们可能与源表不同。由于索引上的查询/扫描能够跨越所有表数据和所有分区,因此它使用“全局”标签。
**本地二级索引** - 此索引与表共享分区键,但使用不同的排序键。它的“本地”性质源于其所有分区都作用域到具有相同分区键值的表分区。
要使用的最佳索引类型取决于应用程序需求。请考虑下表中介绍的两种类型之间的差异 -
| 质量 | 全局二级索引 | 本地二级索引 |
|---|---|---|
| 键架构 | 它使用简单或复合主键。 | 它始终使用复合主键。 |
| 键属性 | 索引分区键和排序键可以由字符串、数字或二进制表属性组成。 | 索引的分区键是与表分区键共享的属性。排序键可以是字符串、数字或二进制表属性。 |
| 每个分区键值的尺寸限制 | 它们没有尺寸限制。 | 它对与分区键值关联的已索引项目的总大小施加 10GB 的最大限制。 |
| 在线索引操作 | 您可以在表创建时生成它们,将它们添加到现有表中,或删除现有的表。 | 您必须在表创建时创建它们,但不能删除它们或将它们添加到现有表中。 |
| 查询 | 它允许覆盖整个表和每个分区的查询。 | 它们通过查询中提供的分区键值来解决单个分区。 |
| 一致性 | 这些索引的查询仅提供最终一致性选项。 | 这些查询提供最终一致性或强一致性选项。 |
| 吞吐量成本 | 它包括读取和写入的吞吐量设置。查询/扫描消耗索引的容量,而不是表的容量,这也适用于表写入更新。 | 查询/扫描消耗表读取容量。表写入更新本地索引,并消耗表容量单元。 |
| 投影 | 查询/扫描只能请求投影到索引中的属性,而不能检索表属性。 | 查询/扫描可以请求未投影的那些属性;此外,还会自动获取它们。 |
在创建具有二级索引的多个表时,请按顺序进行;这意味着创建一个表并等待它达到 ACTIVE 状态,然后再创建另一个表并再次等待。DynamoDB 不允许并发创建。
每个二级索引都需要某些规范 -
**类型** - 指定本地或全局。
**名称** - 它使用与表相同的命名规则。
**键架构** - 仅允许顶级字符串、数字或二进制类型,索引类型决定其他要求。
**投影属性** - DynamoDB 自动投影它们,并允许任何数据类型。
**吞吐量** - 为全局二级索引指定读取/写入容量。
每个表的索引限制仍然是 5 个全局索引和 5 个本地索引。
您可以使用 **DescribeTable** 访问有关索引的详细信息。它返回名称、大小和项目计数。
**注意** - 这些值每 6 小时更新一次。
在用于访问索引数据的查询或扫描中,提供表和索引名称、结果所需的属性以及任何条件语句。DynamoDB 提供了以升序或降序返回结果的选项。
**注意** - 删除表也会删除所有索引。
DynamoDB - 全局二级索引
需要使用不同属性执行各种查询类型的应用程序可以使用一个或多个全局二级索引来执行这些详细查询。
**例如** - 一个系统跟踪用户、他们的登录状态以及他们的登录时间。先前示例的增长会减慢其数据上的查询速度。
全局二级索引通过组织表中选择的属性来加速查询。它们使用主键对数据进行排序,并且不需要键表属性或与表相同的键架构。
所有全局二级索引都必须包含分区键,可以选择排序键。索引键架构可以与表不同,索引键属性可以使用任何顶级字符串、数字或二进制表属性。
在投影中,您可以使用其他表属性,但是,查询不会从父表中检索。
属性投影
投影由从表复制到二级索引的一组属性组成。投影始终与表分区键和排序键一起发生。在查询中,投影允许 DynamoDB 访问投影的任何属性;它们本质上存在于自己的表中。
在创建二级索引时,您必须指定投影属性。DynamoDB 提供三种执行此任务的方法 -
**KEYS_ONLY** - 所有索引项目都包含表分区和排序键值以及索引键值。这将创建最小的索引。
**INCLUDE** - 它包含 KEYS_ONLY 属性和指定的非键属性。
**ALL** - 它包含所有源表属性,创建最大的索引。
请注意将属性投影到全局二级索引中的权衡,这些权衡与吞吐量和存储成本相关。
请考虑以下几点 -
如果您只需要访问少数几个属性并且延迟很低,则仅投影您需要的属性。这可以降低存储和写入成本。
如果应用程序经常访问某些非键属性,请投影它们,因为存储成本与扫描消耗相比微不足道。
您可以投影经常访问的大量属性集,但是,这会产生高昂的存储成本。
对于不频繁的表查询和频繁的写入/更新,请使用 KEYS_ONLY。这可以控制大小,但仍然可以在查询上提供良好的性能。
全局二级索引查询和扫描
您可以利用查询访问索引中的单个或多个项目。您必须指定索引和表名称、所需的属性以及条件;可以选择以升序或降序返回结果。
您还可以利用扫描获取所有索引数据。它需要表和索引名称。您可以使用筛选器表达式来检索特定数据。
表和索引数据同步
DynamoDB 自动执行索引与其父表的同步。项目上的每个修改操作都会导致异步更新,但是,应用程序不会直接写入索引。
您需要了解 DynamoDB 维护对索引的影响。在创建索引时,您指定键属性和数据类型,这意味着在写入时,这些数据类型必须与键架构数据类型匹配。
在项目创建或删除时,索引会以最终一致的方式更新,但是,数据更新会在几分之一秒内传播(除非发生某种类型的系统故障)。您必须在应用程序中考虑此延迟。
**全局二级索引中的吞吐量注意事项** - 多个全局二级索引会影响吞吐量。索引创建需要容量单元规范,这些规范独立于表存在,导致操作消耗索引容量单元而不是表单元。
如果查询或写入超过预配的吞吐量,这可能会导致节流。使用 **DescribeTable** 查看吞吐量设置。
**读取容量** - 全局二级索引提供最终一致性。在查询中,DynamoDB 执行与表相同的预配计算,唯一的区别是使用索引条目大小而不是项目大小。查询返回的限制仍然是 1MB,其中包括每个返回项目中属性名称的大小和值。
写入容量
当写入操作发生时,受影响的索引会消耗写入单元。写入吞吐量成本是表写入中消耗的写入容量单元和索引更新中消耗的单元的总和。成功的写入操作需要足够的容量,否则会导致节流。
写入成本也仍然取决于某些因素,其中一些如下 -
定义索引属性的新项目或定义未定义索引属性的项目更新使用单个写入操作将项目添加到索引中。
更改索引键属性值的更新使用两次写入来删除项目并写入新项目。
触发索引属性删除的表写入使用单个写入来擦除索引中的旧项目投影。
在更新操作之前和之后索引中不存在的项目不使用写入。
仅更改索引键架构中投影属性值而不是索引键属性值的更新使用一次写入将投影属性的值更新到索引中。
所有这些因素都假设项目大小小于或等于 1KB。
全局二级索引存储
在项目写入时,DynamoDB 会自动将正确的属性集复制到必须存在属性的任何索引中。这会影响您的帐户,因为它会对表项目存储和属性存储收费。使用的空间是这些数量的总和 -
- 表主键的字节大小
- 索引键属性的字节大小
- 投影属性的字节大小
- 每个索引项的开销为 100 字节。
您可以通过估算平均项目大小并乘以具有全局二级索引键属性的表项目的数量来估算存储需求。
DynamoDB 不会为表项写入数据,其中未定义的属性被定义为索引分区或排序键。
全局二级索引 CRUD
使用 **CreateTable** 操作与 **GlobalSecondaryIndexes** 参数配对,创建具有全局二级索引的表。您必须指定一个属性作为索引分区键,或使用另一个属性作为索引排序键。所有索引键属性必须是字符串、数字或二进制标量。您还必须提供吞吐量设置,包括 **ReadCapacityUnits** 和 **WriteCapacityUnits**。
使用 **UpdateTable** 通过再次使用 GlobalSecondaryIndexes 参数向现有表添加全局二级索引。
在此操作中,您必须提供以下输入 -
- 索引名称
- 键架构
- 投影属性
- 吞吐量设置
添加全局二级索引可能会在大型表中花费大量时间,因为项目数量、投影属性数量、写入容量和写入活动。使用 **CloudWatch** 指标监控此过程。
使用 **DescribeTable** 获取全局二级索引的状态信息。它为 GlobalSecondaryIndexes 返回四种 **IndexStatus** 之一 -
**CREATING** - 表示索引的构建阶段及其不可用性。
**ACTIVE** - 表示索引已准备好使用。
**UPDATING** - 表示吞吐量设置的更新状态。
**DELETING** - 表示索引的删除状态,以及它永久不可用。
在加载/回填阶段更新全局二级索引的预置吞吐量设置(DynamoDB 将属性写入索引并跟踪添加/删除/更新的项目)。使用 **UpdateTable** 执行此操作。
您应该记住,在回填阶段不能添加/删除其他索引。
使用 UpdateTable 删除全局二级索引。它允许每次操作仅删除一个索引,但是,您可以同时运行多个操作,最多五个。删除过程不会影响父表的读/写活动,但在操作完成之前,您不能添加/删除其他索引。
使用 Java 处理全局二级索引
通过 CreateTable 创建具有索引的表。只需创建一个 DynamoDB 类实例、一个 **CreateTableRequest** 类实例用于请求信息,并将请求对象传递给 CreateTable 方法。
以下程序是一个简短的示例 -
DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient (
new ProfileCredentialsProvider()));
// Attributes
ArrayList<AttributeDefinition> attributeDefinitions = new
ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition()
.withAttributeName("City")
.withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
.withAttributeName("Date")
.withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
.withAttributeName("Wind")
.withAttributeType("N"));
// Key schema of the table
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement()
.withAttributeName("City")
.withKeyType(KeyType.HASH)); //Partition key
tableKeySchema.add(new KeySchemaElement()
.withAttributeName("Date")
.withKeyType(KeyType.RANGE)); //Sort key
// Wind index
GlobalSecondaryIndex windIndex = new GlobalSecondaryIndex()
.withIndexName("WindIndex")
.withProvisionedThroughput(new ProvisionedThroughput()
.withReadCapacityUnits((long) 10)
.withWriteCapacityUnits((long) 1))
.withProjection(new Projection().withProjectionType(ProjectionType.ALL));
ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();
indexKeySchema.add(new KeySchemaElement()
.withAttributeName("Date")
.withKeyType(KeyType.HASH)); //Partition key
indexKeySchema.add(new KeySchemaElement()
.withAttributeName("Wind")
.withKeyType(KeyType.RANGE)); //Sort key
windIndex.setKeySchema(indexKeySchema);
CreateTableRequest createTableRequest = new CreateTableRequest()
.withTableName("ClimateInfo")
.withProvisionedThroughput(new ProvisionedThroughput()
.withReadCapacityUnits((long) 5)
.withWriteCapacityUnits((long) 1))
.withAttributeDefinitions(attributeDefinitions)
.withKeySchema(tableKeySchema)
.withGlobalSecondaryIndexes(windIndex);
Table table = dynamoDB.createTable(createTableRequest);
System.out.println(table.getDescription());
使用 **DescribeTable** 检索索引信息。首先,创建一个 DynamoDB 类实例。然后创建一个 Table 类实例以定位索引。最后,将表传递给 describe 方法。
这是一个简短的示例 -
DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient (
new ProfileCredentialsProvider()));
Table table = dynamoDB.getTable("ClimateInfo");
TableDescription tableDesc = table.describe();
Iterator<GlobalSecondaryIndexDescription> gsiIter =
tableDesc.getGlobalSecondaryIndexes().iterator();
while (gsiIter.hasNext()) {
GlobalSecondaryIndexDescription gsiDesc = gsiIter.next();
System.out.println("Index data " + gsiDesc.getIndexName() + ":");
Iterator<KeySchemaElement> kse7Iter = gsiDesc.getKeySchema().iterator();
while (kseIter.hasNext()) {
KeySchemaElement kse = kseIter.next();
System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType());
}
Projection projection = gsiDesc.getProjection();
System.out.println("\tProjection type: " + projection.getProjectionType());
if (projection.getProjectionType().toString().equals("INCLUDE")) {
System.out.println("\t\tNon-key projected attributes: "
+ projection.getNonKeyAttributes());
}
}
使用 Query 对索引执行查询,就像对表执行查询一样。只需创建一个 DynamoDB 类实例、一个 Table 类实例用于目标索引、一个 Index 类实例用于特定索引,并将索引和查询对象传递给 query 方法。
查看以下代码以更好地理解 -
DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient (
new ProfileCredentialsProvider()));
Table table = dynamoDB.getTable("ClimateInfo");
Index index = table.getIndex("WindIndex");
QuerySpec spec = new QuerySpec()
.withKeyConditionExpression("#d = :v_date and Wind = :v_wind")
.withNameMap(new NameMap()
.with("#d", "Date"))
.withValueMap(new ValueMap()
.withString(":v_date","2016-05-15")
.withNumber(":v_wind",0));
ItemCollection<QueryOutcome> items = index.query(spec);
Iterator<Item> iter = items.iterator();
while (iter.hasNext()) {
System.out.println(iter.next().toJSONPretty());
}
以下程序是一个更大的示例,以便更好地理解 -
注意 - 以下程序可能假设存在先前创建的数据源。在尝试执行之前,请获取支持库并创建必要的数据源(具有所需特征的表或其他引用的源)。
此示例还使用 Eclipse IDE、AWS 凭证文件以及 Eclipse AWS Java 项目中的 AWS 工具包。
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.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.GlobalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
public class GlobalSecondaryIndexSample {
static DynamoDB dynamoDB = new DynamoDB(new AmazonDynamoDBClient (
new ProfileCredentialsProvider()));
public static String tableName = "Bugs";
public static void main(String[] args) throws Exception {
createTable();
queryIndex("CreationDateIndex");
queryIndex("NameIndex");
queryIndex("DueDateIndex");
}
public static void createTable() {
// Attributes
ArrayList<AttributeDefinition> attributeDefinitions = new
ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition()
.withAttributeName("BugID")
.withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
.withAttributeName("Name")
.withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
.withAttributeName("CreationDate")
.withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
.withAttributeName("DueDate")
.withAttributeType("S"));
// Table Key schema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add (new KeySchemaElement()
.withAttributeName("BugID")
.withKeyType(KeyType.HASH)); //Partition key
tableKeySchema.add (new KeySchemaElement()
.withAttributeName("Name")
.withKeyType(KeyType.RANGE)); //Sort key
// Indexes' initial provisioned throughput
ProvisionedThroughput ptIndex = new ProvisionedThroughput()
.withReadCapacityUnits(1L)
.withWriteCapacityUnits(1L);
// CreationDateIndex
GlobalSecondaryIndex creationDateIndex = new GlobalSecondaryIndex()
.withIndexName("CreationDateIndex")
.withProvisionedThroughput(ptIndex)
.withKeySchema(new KeySchemaElement()
.withAttributeName("CreationDate")
.withKeyType(KeyType.HASH), //Partition key
new KeySchemaElement()
.withAttributeName("BugID")
.withKeyType(KeyType.RANGE)) //Sort key
.withProjection(new Projection()
.withProjectionType("INCLUDE")
.withNonKeyAttributes("Description", "Status"));
// NameIndex
GlobalSecondaryIndex nameIndex = new GlobalSecondaryIndex()
.withIndexName("NameIndex")
.withProvisionedThroughput(ptIndex)
.withKeySchema(new KeySchemaElement()
.withAttributeName("Name")
.withKeyType(KeyType.HASH), //Partition key
new KeySchemaElement()
.withAttributeName("BugID")
.withKeyType(KeyType.RANGE)) //Sort key
.withProjection(new Projection()
.withProjectionType("KEYS_ONLY"));
// DueDateIndex
GlobalSecondaryIndex dueDateIndex = new GlobalSecondaryIndex()
.withIndexName("DueDateIndex")
.withProvisionedThroughput(ptIndex)
.withKeySchema(new KeySchemaElement()
.withAttributeName("DueDate")
.withKeyType(KeyType.HASH)) //Partition key
.withProjection(new Projection()
.withProjectionType("ALL"));
CreateTableRequest createTableRequest = new CreateTableRequest()
.withTableName(tableName)
.withProvisionedThroughput( new ProvisionedThroughput()
.withReadCapacityUnits( (long) 1)
.withWriteCapacityUnits( (long) 1))
.withAttributeDefinitions(attributeDefinitions)
.withKeySchema(tableKeySchema)
.withGlobalSecondaryIndexes(creationDateIndex, nameIndex, dueDateIndex);
System.out.println("Creating " + tableName + "...");
dynamoDB.createTable(createTableRequest);
// Pause for active table state
System.out.println("Waiting for ACTIVE state of " + tableName);
try {
Table table = dynamoDB.getTable(tableName);
table.waitForActive();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void queryIndex(String indexName) {
Table table = dynamoDB.getTable(tableName);
System.out.println
("\n*****************************************************\n");
System.out.print("Querying index " + indexName + "...");
Index index = table.getIndex(indexName);
ItemCollection<QueryOutcome> items = null;
QuerySpec querySpec = new QuerySpec();
if (indexName == "CreationDateIndex") {
System.out.println("Issues filed on 2016-05-22");
querySpec.withKeyConditionExpression("CreationDate = :v_date and begins_with
(BugID, :v_bug)")
.withValueMap(new ValueMap()
.withString(":v_date","2016-05-22")
.withString(":v_bug","A-"));
items = index.query(querySpec);
} else if (indexName == "NameIndex") {
System.out.println("Compile error");
querySpec.withKeyConditionExpression("Name = :v_name and begins_with
(BugID, :v_bug)")
.withValueMap(new ValueMap()
.withString(":v_name","Compile error")
.withString(":v_bug","A-"));
items = index.query(querySpec);
} else if (indexName == "DueDateIndex") {
System.out.println("Items due on 2016-10-15");
querySpec.withKeyConditionExpression("DueDate = :v_date")
.withValueMap(new ValueMap()
.withString(":v_date","2016-10-15"));
items = index.query(querySpec);
} else {
System.out.println("\nInvalid index name");
return;
}
Iterator<Item> iterator = items.iterator();
System.out.println("Query: getting result...");
while (iterator.hasNext()) {
System.out.println(iterator.next().toJSONPretty());
}
}
}
DynamoDB - 本地二级索引
某些应用程序仅使用主键执行查询,但某些情况下会从备用排序键中受益。通过创建单个或多个本地二级索引,允许您的应用程序进行选择。
复杂的数据访问需求,例如组合数百万个项目,使得执行更有效的查询/扫描成为必要。本地二级索引为分区键值提供备用排序键。它们还保存所有或某些表属性的副本。它们按表分区键组织数据,但使用不同的排序键。
使用本地二级索引无需进行整个表扫描,并允许使用排序键进行简单快速的查询。
所有本地二级索引必须满足某些条件 -
- 与源表分区键相同的分区键。
- 仅一个标量属性的排序键。
- 作为非键属性的源表排序键的投影。
所有本地二级索引都自动保存来自父表的分区和排序键。在查询中,这意味着有效地收集投影属性,以及检索未投影的属性。
本地二级索引的存储限制为每个分区键值 10GB,其中包括所有表项以及共享分区键值的索引项。
投影属性
某些操作由于复杂性而需要额外的读取/获取。这些操作可能会消耗大量的吞吐量。通过隔离这些属性,投影允许您避免代价高昂的获取并执行丰富的查询。请记住,投影包括复制到二级索引中的属性。
创建二级索引时,您会指定投影的属性。回想一下 DynamoDB 提供的三个选项:**KEYS_ONLY、INCLUDE 和 ALL**。
在选择投影中的某些属性时,请考虑相关的成本权衡 -
如果您仅投影一小部分必要的属性,则会大幅降低存储成本。
如果您投影经常访问的非键属性,则可以通过存储成本抵消扫描成本。
如果您投影大多数或所有非键属性,这将最大限度地提高灵活性并降低吞吐量(无需检索);但是,存储成本会上升。
如果您为频繁写入/更新和不频繁查询投影 KEYS_ONLY,它可以最小化大小,但保持查询准备。
本地二级索引创建
使用 CreateTable 的 **LocalSecondaryIndex** 参数创建单个或多个本地二级索引。您必须为排序键指定一个非键属性。在表创建时,您创建本地二级索引。在删除时,您删除这些索引。
具有本地二级索引的表必须遵守每个分区键值 10GB 的大小限制,但可以存储任意数量的项目。
本地二级索引查询和扫描
当索引中的多个项目共享排序键值时,本地二级索引上的查询操作将返回所有具有匹配分区键值的项目。匹配的项目不会以特定顺序返回。本地二级索引的查询使用最终一致性或强一致性,强一致性读取提供最新值。
扫描操作返回所有本地二级索引数据。扫描要求您提供表和索引名称,并允许使用筛选表达式来丢弃数据。
项目写入
在创建本地二级索引时,您会指定排序键属性及其数据类型。当您写入项目时,如果项目定义了索引键的属性,则其类型必须与键架构的数据类型匹配。
DynamoDB 对表项和本地二级索引项没有施加一对一关系的要求。具有多个本地二级索引的表的写入成本高于具有较少索引的表。
本地二级索引中的吞吐量注意事项
查询的读取容量消耗取决于数据访问的性质。查询使用最终一致性或强一致性,强一致性读取使用一个单元,而最终一致性读取使用半个单元。
结果限制包括最大 1MB 的大小。结果大小来自匹配索引项目大小(四舍五入到最接近的 4KB)和匹配表项目大小(也四舍五入到最接近的 4KB)的总和。
写入容量消耗保持在预置单元内。通过查找表写入中消耗的单元和更新索引中消耗的单元的总和来计算总预置成本。
您还可以考虑影响成本的关键因素,其中一些可能是 -
当您写入定义索引属性的项目或更新项目以定义未定义的索引属性时,会发生一次写入操作。
当表更新更改索引键属性值时,会发生两次写入以删除然后添加项目。
当写入导致索引属性的删除时,会发生一次写入以删除旧的项目投影。
当项目在更新之前或之后不存在于索引中时,不会发生写入。
本地二级索引存储
在表项写入时,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 类实例、一个查询对象,并使用 query 方法。
示例
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 工具包。
示例
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());
}
}
}
}
DynamoDB - 聚合
DynamoDB 不提供聚合函数。您必须创造性地使用查询、扫描、索引和各种工具来执行这些任务。在所有这些操作中,这些操作中查询/扫描的吞吐量开销可能很大。
您还可以选择为首选的 DynamoDB 编码语言使用库和其他工具。在使用之前,请确保它们与 DynamoDB 兼容。
计算最大值或最小值
利用结果的升序/降序存储顺序、Limit 参数以及设置顺序的任何参数来查找最高和最低值。
例如 −
Map<String, AttributeValue> eaval = new HashMap<>();
eaval.put(":v1", new AttributeValue().withS("hashval"));
queryExpression = new DynamoDBQueryExpression<Table>()
.withIndexName("yourindexname")
.withKeyConditionExpression("HK = :v1")
.withExpressionAttributeValues(values)
.withScanIndexForward(false); //descending order
queryExpression.setLimit(1);
QueryResultPage<Lookup> res =
dynamoDBMapper.queryPage(Table.class, queryExpression);
计算计数
使用 **DescribeTable** 获取表项的计数,但是,请注意它提供的是陈旧数据。此外,使用 Java 的 **getScannedCount 方法**。
利用 **LastEvaluatedKey** 确保它提供所有结果。
例如 −
ScanRequest scanRequest = new ScanRequest().withTableName(yourtblName);
ScanResult yourresult = client.scan(scanRequest);
System.out.println("#items:" + yourresult.getScannedCount());
计算平均值和总和
利用索引和查询/扫描在处理之前检索和筛选值。然后,只需通过对象对这些值进行操作。
DynamoDB - 访问控制
DynamoDB 使用您提供的凭证来验证请求。这些凭证是必需的,并且必须包含对 AWS 资源访问的权限。这些权限几乎涵盖了 DynamoDB 的各个方面,直至操作或功能的细微功能。
权限类型
在本节中,我们将讨论 DynamoDB 中的各种权限和资源访问。
验证用户
在注册时,您提供了密码和电子邮件,它们用作根凭证。DynamoDB 将此数据与您的 AWS 账户关联,并使用它来授予对所有资源的完全访问权限。
AWS 建议您仅将根凭证用于创建管理账户。这使您可以创建具有较少权限的 IAM 账户/用户。IAM 用户是由 IAM 服务生成的其它账户。它们的访问权限/特权包括访问安全页面和某些自定义权限,例如表修改。
访问密钥提供了另一个用于添加账户和访问权限的选项。使用它们来授予访问权限,并在某些情况下避免手动授予访问权限。联合用户通过允许通过身份提供商进行访问提供了另一个选项。
管理
AWS 资源仍然属于某个账户所有。权限策略控制授予的创建或访问资源的权限。管理员将权限策略与 IAM 标识相关联,即角色、组、用户和服务。他们还将权限附加到资源。
权限指定用户、资源和操作。请注意,管理员仅仅是拥有管理员权限的账户。
操作和资源
表仍然是 DynamoDB 中的主要资源。子资源用作其他资源,例如流和索引。这些资源使用唯一的名称,其中一些在以下表格中提到 -
| 类型 | ARN(Amazon 资源名称) |
|---|---|
| 流 | arn:aws:dynamodb:region:account-id:table/table-name/stream/stream-label |
| 索引 | arn:aws:dynamodb:region:account-id:table/table-name/index/index-name |
| 表 | arn:aws:dynamodb:region:account-id:table/table-name |
所有权
资源所有者定义为创建该资源的 AWS 账户,或负责在资源创建中进行请求身份验证的主体实体账户。考虑一下它在 DynamoDB 环境中的工作方式 -
在使用根凭证创建表时,您的账户仍然是资源所有者。
在创建 IAM 用户并授予该用户创建表的权限时,您的账户仍然是资源所有者。
在创建 IAM 用户并授予该用户以及任何能够承担该角色的用户创建表的权限时,您的账户仍然是资源所有者。
管理资源访问
访问管理主要需要关注描述用户和资源访问权限的权限策略。您可以将策略与 IAM 标识或资源关联。但是,DynamoDB 仅支持 IAM/身份策略。
基于身份(IAM)的策略允许您通过以下方式授予权限 -
- 将权限附加到用户或组。
- 将权限附加到角色以进行跨账户权限。
其他 AWS 允许基于资源的策略。这些策略允许访问诸如 S3 存储桶之类的内容。
策略元素
策略定义操作、效果、资源和主体;并授予执行这些操作的权限。
注意 - API 操作可能需要多个操作的权限。
仔细查看以下策略元素 -
资源 - ARN 标识此内容。
操作 - 关键字标识这些资源操作,以及是允许还是拒绝。
效果 - 它指定用户对操作请求的效果,即允许或拒绝,其中拒绝为默认值。
主体 - 这标识附加到策略的用户。
条件
在授予权限时,您可以指定策略何时生效的条件,例如在特定日期。使用条件键表达条件,其中包括 AWS 范围内的键和 DynamoDB 键。这些键将在本教程的后面详细讨论。
控制台权限
用户需要某些基本权限才能使用控制台。他们还需要其他标准服务中的控制台权限 -
- CloudWatch
- 数据管道
- 身份和访问管理
- 通知服务
- Lambda
如果 IAM 策略过于限制,则用户无法有效地使用控制台。此外,您无需担心仅调用 CLI 或 API 的用户的用户权限。
常用 IAM 策略
AWS 使用独立的 IAM 托管策略涵盖权限中的常见操作。它们提供关键权限,允许您避免深入调查必须授予的内容。
其中一些如下 -
AmazonDynamoDBReadOnlyAccess - 它通过控制台提供只读访问权限。
AmazonDynamoDBFullAccess - 它通过控制台提供完全访问权限。
AmazonDynamoDBFullAccesswithDataPipeline - 它通过控制台提供完全访问权限,并允许使用数据管道进行导出/导入。
您当然也可以创建自定义策略。
授予权限:使用 Shell
您可以使用 Javascript shell 授予权限。以下程序显示了一个典型的权限策略 -
{
"Version": "2016-05-22",
"Statement": [
{
"Sid": "DescribeQueryScanToolsTable",
"Effect": "Deny",
"Action": [
"dynamodb:DescribeTable",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": "arn:aws:dynamodb:us-west-2:account-id:table/Tools"
}
]
}
您可以查看以下三个示例 -
阻止用户执行任何表操作。
{
"Version": "2016-05-23",
"Statement": [
{
"Sid": "AllAPIActionsOnTools",
"Effect": "Deny",
"Action": "dynamodb:*",
"Resource": "arn:aws:dynamodb:us-west-2:155556789012:table/Tools"
}
]
}
阻止访问表及其索引。
{
"Version": "2016-05-23",
"Statement": [
{
"Sid": "AccessAllIndexesOnTools",
"Effect": "Deny",
"Action": [
"dynamodb:*"
],
"Resource": [
"arn:aws:dynamodb:us-west-2:155556789012:table/Tools",
"arn:aws:dynamodb:us-west-2:155556789012:table/Tools/index/*"
]
}
]
}
阻止用户进行预留容量购买。
{
"Version": "2016-05-23",
"Statement": [
{
"Sid": "BlockReservedCapacityPurchases",
"Effect": "Deny",
"Action": "dynamodb:PurchaseReservedCapacityOfferings",
"Resource": "arn:aws:dynamodb:us-west-2:155556789012:*"
}
]
}
授予权限:使用 GUI 控制台
您还可以使用 GUI 控制台创建 IAM 策略。首先,从导航窗格中选择表。在表列表中,选择目标表并按照以下步骤操作。
步骤 1 - 选择访问控制选项卡。
步骤 2 - 选择身份提供商、操作和策略属性。输入所有设置后,选择创建策略。
步骤 3 - 选择附加策略说明,并完成每个必需步骤以将策略与相应的 IAM 角色关联。
DynamoDB - 权限 API
DynamoDB API 提供大量需要权限的操作。在设置权限时,您必须确定允许的操作、允许的资源以及每个条件。
您可以在策略的 Action 字段中指定操作。在策略的 Resource 字段中指定资源值。但请确保使用包含 Dynamodb: 前缀和 API 操作的正确语法。
例如 - dynamodb:CreateTable
您还可以使用条件键来过滤权限。
权限和 API 操作
仔细查看以下表格中给出的 API 操作和关联权限 -
| API 操作 | 必要权限 |
|---|---|
| BatchGetItem | dynamodb:BatchGetItem |
| BatchWriteItem | dynamodb:BatchWriteItem |
| CreateTable | dynamodb:CreateTable |
| DeleteItem | dynamodb:DeleteItem |
| DeleteTable | dynamodb:DeleteTable |
| DescribeLimits | dynamodb:DescribeLimits |
| DescribeReservedCapacity | dynamodb:DescribeReservedCapacity |
| DescribeReservedCapacityOfferings | dynamodb:DescribeReservedCapacityOfferings |
| DescribeStream | dynamodb:DescribeStream |
| DescribeTable | dynamodb:DescribeTable |
| GetItem | dynamodb:GetItem |
| GetRecords | dynamodb:GetRecords |
| GetShardIterator | dynamodb:GetShardIterator |
| ListStreams | dynamodb:ListStreams |
| ListTables | dynamodb:ListTables |
| PurchaseReservedCapacityOfferings | dynamodb:PurchaseReservedCapacityOfferings |
| PutItem | dynamodb:PutItem |
| Query | dynamodb:Query |
| Scan | dynamodb:Scan |
| UpdateItem | dynamodb:UpdateItem |
| UpdateTable | dynamodb:UpdateTable |
资源
在以下表格中,您可以查看与每个允许的 API 操作关联的资源 -
| API 操作 | 资源 |
|---|---|
| BatchGetItem | arn:aws:dynamodb:region:account-id:table/table-name |
| BatchWriteItem | arn:aws:dynamodb:region:account-id:table/table-name |
| CreateTable | arn:aws:dynamodb:region:account-id:table/table-name |
| DeleteItem | arn:aws:dynamodb:region:account-id:table/table-name |
| DeleteTable | arn:aws:dynamodb:region:account-id:table/table-name |
| DescribeLimits | arn:aws:dynamodb:region:account-id:* |
| DescribeReservedCapacity | arn:aws:dynamodb:region:account-id:* |
| DescribeReservedCapacityOfferings | arn:aws:dynamodb:region:account-id:* |
| DescribeStream | arn:aws:dynamodb:region:account-id:table/table-name/stream/stream-label |
| DescribeTable | arn:aws:dynamodb:region:account-id:table/table-name |
| GetItem | arn:aws:dynamodb:region:account-id:table/table-name |
| GetRecords | arn:aws:dynamodb:region:account-id:table/table-name/stream/stream-label |
| GetShardIterator | arn:aws:dynamodb:region:account-id:table/table-name/stream/stream-label |
| ListStreams | arn:aws:dynamodb:region:account-id:table/table-name/stream/* |
| ListTables | * |
| PurchaseReservedCapacityOfferings | arn:aws:dynamodb:region:account-id:* |
| PutItem | arn:aws:dynamodb:region:account-id:table/table-name |
| Query | arn:aws:dynamodb:region:account-id:table/table-name 或 arn:aws:dynamodb:region:account-id:table/table-name/index/index-name |
| Scan | arn:aws:dynamodb:region:account-id:table/table-name 或 arn:aws:dynamodb:region:account-id:table/table-name/index/index-name |
| UpdateItem | arn:aws:dynamodb:region:account-id:table/table-name |
| UpdateTable | arn:aws:dynamodb:region:account-id:table/table-name |
DynamoDB - 条件
在授予权限时,DynamoDB 允许通过具有条件键的详细 IAM 策略为其指定条件。这支持诸如访问特定项目和属性之类的设置。
注意 - DynamoDB 不支持任何标签。
详细控制
一些条件允许具体到项目和属性,例如根据用户账户授予特定项目的只读访问权限。使用条件 IAM 策略实现此级别的控制,该策略管理安全凭证。然后只需将策略应用于所需的用户的、组和角色。稍后讨论的 Web 身份联合也提供了一种通过 Amazon、Facebook 和 Google 登录来控制用户访问权限的方法。
IAM 策略的 condition 元素实现访问控制。您只需将其添加到策略中即可。其使用示例包括拒绝或允许访问表项目和属性。condition 元素还可以使用条件键来限制权限。
您可以查看以下两个条件键示例 -
dynamodb:LeadingKeys - 它阻止没有与分区键值匹配的 ID 的用户访问项目。
dynamodb:Attributes - 它阻止用户访问或操作未列出的属性之外的属性。
在评估时,IAM 策略会产生真或假值。如果任何部分评估为假,则整个策略将评估为假,这将导致拒绝访问。请确保在条件键中指定所有必需的信息,以确保用户拥有适当的访问权限。
预定义条件键
AWS 提供了一组预定义的条件键,这些键适用于所有服务。它们支持广泛的用途并在检查用户和访问权限时提供精细的细节。
注意 - 条件键区分大小写。
您可以查看以下一些特定于服务的键 -
dynamodb:LeadingKey - 它表示表的第一个键属性;分区键。在条件中使用 ForAllValues 修饰符。
dynamodb:Select - 它表示查询/扫描请求 Select 参数。它必须是 ALL_ATTRIBUTES、ALL_PROJECTED_ATTRIBUTES、SPECIFIC_ATTRIBUTES 或 COUNT 值。
dynamodb:Attributes - 它表示请求中的属性名称列表,或从请求返回的属性。其值及其功能类似于 API 操作参数,例如 BatchGetItem 使用 AttributesToGet。
dynamodb:ReturnValues - 它表示请求的 ReturnValues 参数,并且可以使用以下值:ALL_OLD、UPDATED_OLD、ALL_NEW、UPDATED_NEW 和 NONE。
dynamodb:ReturnConsumedCapacity - 它表示请求的 ReturnConsumedCapacity 参数,并且可以使用以下值:TOTAL 和 NONE。
DynamoDB - Web 身份联合
Web 身份联合允许您简化对大型用户组的身份验证和授权。您可以跳过创建单个账户,并要求用户登录到身份提供商以获取临时凭证或令牌。它使用 AWS 安全令牌服务 (STS) 来管理凭证。应用程序使用这些令牌与服务进行交互。
Web 身份联合还支持其他身份提供商,例如 Amazon、Google 和 Facebook。
功能 - 在使用中,Web 身份联合首先调用身份提供商进行用户和应用程序身份验证,并且提供商返回一个令牌。这会导致应用程序调用 AWS STS 并传递令牌作为输入。STS 授权应用程序并向其授予临时访问凭证,这些凭证允许应用程序使用 IAM 角色并根据策略访问资源。
实施 Web 身份联合
在使用之前,您必须执行以下三个步骤 -
使用受支持的第三方身份提供商注册为开发者。
向提供商注册您的应用程序以获取应用程序 ID。
创建单个或多个 IAM 角色,包括策略附加。您必须为每个提供商每个应用程序使用一个角色。
假设您要使用 Web 身份联合中的一个 IAM 角色。然后,您的应用程序必须执行一个三步过程 -
- 身份验证
- 凭证获取
- 资源访问
在第一步中,您的应用程序使用其自己的界面调用提供商,然后管理令牌过程。
然后第二步管理令牌,并要求您的应用程序向 AWS STS 发送AssumeRoleWithWebIdentity请求。该请求包含第一个令牌、提供商应用程序 ID 和 IAM 角色的 ARN。然后 STS 提供设置为在特定时间段后过期的凭证。
在最后一步中,您的应用程序会收到来自 STS 的响应,其中包含 DynamoDB 资源的访问信息。它包括访问凭证、过期时间、角色和角色 ID。
DynamoDB - 数据管道
数据管道允许将数据导出和导入到/从表、文件或 S3 存储桶。这当然在备份、测试以及类似的需求或场景中非常有用。
在导出中,您使用数据管道控制台,它会创建一个新的管道并启动一个 Amazon EMR(Elastic MapReduce)集群来执行导出。EMR 从 DynamoDB 读取数据并写入目标。我们将在本教程的后面详细讨论 EMR。
在导入操作中,您使用数据管道控制台,它会创建一个管道并启动 EMR 来执行导入。它从源读取数据并写入目标。
注意 − 由于使用了 EMR 和 S3 等服务,导出/导入操作会产生费用。
使用数据管道
使用数据管道时,必须指定操作和资源权限。您可以利用 IAM 角色或策略来定义它们。执行导入/导出操作的用户应注意,他们需要一个有效的访问密钥 ID 和密钥。
数据管道的 IAM 角色
使用数据管道需要两个 IAM 角色 −
DataPipelineDefaultRole − 它包含您允许管道为您执行的所有操作。
DataPipelineDefaultResourceRole − 它包含您允许管道为您配置的资源。
如果您是数据管道的新手,则必须分别创建每个角色。所有以前的用户都拥有这些角色,因为它们是现有角色。
使用 IAM 控制台为数据管道创建 IAM 角色,并执行以下四个步骤 −
步骤 1 − 登录位于 https://console.aws.amazon.com/iam/ 的 IAM 控制台
步骤 2 − 从仪表板中选择角色。
步骤 3 − 选择创建新角色。然后在角色名称字段中输入 DataPipelineDefaultRole,并选择下一步。在角色类型面板中的AWS 服务角色列表中,导航到数据管道,然后选择选择。在审查面板中选择创建角色。
步骤 4 − 选择创建新角色。
DynamoDB - 数据备份
利用数据管道的导入/导出功能执行备份。执行备份的方式取决于您是使用 GUI 控制台还是直接使用数据管道(API)。如果使用控制台,则为每个表创建单独的管道;如果使用直接选项,则在一个管道中导入/导出多个表。
导出和导入数据
在执行导出之前,必须创建一个 Amazon S3 存储桶。您可以从一个或多个表中导出数据。
执行以下四个步骤过程来执行导出 −
步骤 1 − 登录 AWS 管理控制台并打开位于 https://console.aws.amazon.com/datapipeline/ 的数据管道控制台
步骤 2 − 如果您在使用的 AWS 区域中没有管道,请选择立即开始。如果您有一个或多个管道,请选择创建新管道。
步骤 3 − 在创建页面上,输入管道名称。为源参数选择使用模板构建。从列表中选择将 DynamoDB 表导出到 S3。在源 DynamoDB 表名称字段中输入源表。
使用以下格式在输出 S3 文件夹文本框中输入目标 S3 存储桶:s3://nameOfBucket/region/nameOfFolder。在日志的 S3 位置文本框中输入日志文件的 S3 目标。
步骤 4 − 输入所有设置后,选择激活。
管道可能需要几分钟才能完成创建过程。使用控制台监控其状态。通过查看导出的文件,在 S3 控制台中确认处理成功。
导入数据
只有在满足以下条件时,导入才能成功:您创建了一个目标表,目标和源使用相同的名称,以及目标和源使用相同的键架构。
您可以使用已填充的目标表,但是,导入会替换与源项共享键的数据项,并将多余的项添加到表中。目标还可以使用不同的区域。
虽然您可以导出多个源,但每次操作只能导入一个。您可以通过遵循以下步骤执行导入 −
步骤 1 − 登录 AWS 管理控制台,然后打开数据管道控制台。
步骤 2 − 如果您打算执行跨区域导入,则应选择目标区域。
步骤 3 − 选择创建新管道。
步骤 4 − 在名称字段中输入管道名称。为源参数选择使用模板构建,并在模板列表中选择从 S3 导入 DynamoDB 备份数据。
在输入 S3 文件夹文本框中输入源文件的位置。在目标 DynamoDB 表名称字段中输入目标表名称。然后在日志的 S3 位置文本框中输入日志文件的位置。
步骤 5 − 输入所有设置后,选择激活。
管道创建后立即开始导入。管道可能需要几分钟才能完成创建过程。
错误
发生错误时,数据管道控制台会将管道状态显示为错误。单击出现错误的管道将带您到其详细信息页面,该页面显示了流程的每个步骤以及发生故障的点。内部日志文件也提供了一些见解。
您可以查看错误的常见原因,如下所示 −
导入的目标表不存在,或者与源的键架构不相同。
S3 存储桶不存在,或者您没有对其进行读/写权限。
管道超时。
您没有必要的导出/导入权限。
您的 AWS 账户已达到其资源限制。
DynamoDB - 监控
Amazon 提供 CloudWatch 用于通过 CloudWatch 控制台、命令行或 CloudWatch API 聚合和分析性能。您还可以使用它来设置警报和执行任务。它在某些事件上执行指定的动作。
CloudWatch 控制台
通过访问管理控制台并打开位于 https://console.aws.amazon.com/cloudwatch/ 的 CloudWatch 控制台来使用 CloudWatch。
然后您可以执行以下步骤 −
从导航窗格中选择指标。
在按类别划分的 CloudWatch 指标窗格中的 DynamoDB 指标下,选择表指标。
使用上窗格向下滚动并检查表指标的完整列表。查看列表提供了指标选项。
在结果界面中,您可以通过选中资源名称和指标旁边的复选框来选择/取消选择每个指标。然后您将能够查看每个项目的图表。
API 集成
您可以使用查询访问 CloudWatch。使用指标值执行 CloudWatch 操作。请注意,DynamoDB 不会发送值为零的指标。它只是跳过这些指标在该值保持不变的时间段。
以下是一些最常用的指标 −
ConditionalCheckFailedRequests − 它跟踪条件写入(例如条件 PutItem 写入)失败尝试的数量。在评估为假时,失败的写入会将此指标增加 1。它还会抛出 HTTP 400 错误。
ConsumedReadCapacityUnits − 它量化了在特定时间段内使用的容量单位。您可以使用它来检查单个表和索引的消耗。
ConsumedWriteCapacityUnits − 它量化了在特定时间段内使用的容量单位。您可以使用它来检查单个表和索引的消耗。
ReadThrottleEvents − 它量化了表/索引读取中超过预配容量单位的请求。它在每次节流时都会递增,包括具有多个节流的批处理操作。
ReturnedBytes − 它量化了在特定时间段内检索操作中返回的字节数。
ReturnedItemCount − 它量化了在特定时间段内 Query 和 Scan 操作中返回的项目数。它仅解决返回的项目,而不是评估的项目,这些项目通常是完全不同的数字。
注意 − 还有许多其他指标存在,并且大多数指标允许您计算平均值、总和、最大值、最小值和计数。
DynamoDB - CloudTrail
DynamoDB 包括 CloudTrail 集成。它捕获账户中来自或针对 DynamoDB 的低级 API 请求,并将日志文件发送到指定的 S3 存储桶。它针对控制台或 API 的调用。您可以使用这些数据来确定所做的请求及其来源、用户、时间戳等。
启用后,它会在日志文件中跟踪操作,其中包括其他服务记录。它支持八个操作和两个流 −
八个操作如下 −
- CreateTable
- DeleteTable
- DescribeTable
- ListTables
- UpdateTable
- DescribeReservedCapacity
- DescribeReservedCapacityOfferings
- PurchaseReservedCapacityOfferings
而两个流是 −
- DescribeStream
- ListStreams
所有日志都包含有关发出请求的账户的信息。您可以确定详细的信息,例如根用户或 IAM 用户是否发出了请求,或者是否使用临时凭证或联合身份验证。
日志文件将保留在存储中您指定的时间,并设置存档和删除。默认情况下会创建加密日志。您可以为新日志设置警报。您还可以将跨区域和账户的多个日志组织到一个存储桶中。
解释日志文件
每个文件包含一个或多个条目。每个条目都包含多个 JSON 格式的事件。一个条目表示一个请求,并包含相关信息;不保证顺序。
您可以查看以下示例日志文件 −
{"Records": [
{
"eventVersion": "5.05",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AKTTIOSZODNN8SAMPLE:jane",
"arn": "arn:aws:sts::155522255533:assumed-role/users/jane",
"accountId": "155522255533",
"accessKeyId": "AKTTIOSZODNN8SAMPLE",
"sessionContext": {
"attributes": {
"mfaAuthenticated": "false",
"creationDate": "2016-05-11T19:01:01Z"
},
"sessionIssuer": {
"type": "Role",
"principalId": "AKTTI44ZZ6DHBSAMPLE",
"arn": "arn:aws:iam::499955777666:role/admin-role",
"accountId": "499955777666",
"userName": "jill"
}
}
},
"eventTime": "2016-05-11T14:33:20Z",
"eventSource": "dynamodb.amazonaws.com",
"eventName": "DeleteTable",
"awsRegion": "us-west-2",
"sourceIPAddress": "192.0.2.0",
"userAgent": "console.aws.amazon.com",
"requestParameters": {"tableName": "Tools"},
"responseElements": {"tableDescription": {
"tableName": "Tools",
"itemCount": 0,
"provisionedThroughput": {
"writeCapacityUnits": 25,
"numberOfDecreasesToday": 0,
"readCapacityUnits": 25
},
"tableStatus": "DELETING",
"tableSizeBytes": 0
}},
"requestID": "4D89G7D98GF7G8A7DF78FG89AS7GFSO5AEMVJF66Q9ASUAAJG",
"eventID": "a954451c-c2fc-4561-8aea-7a30ba1fdf52",
"eventType": "AwsApiCall",
"apiVersion": "2013-04-22",
"recipientAccountId": "155522255533"
}
]}
DynamoDB - MapReduce
Amazon 的 Elastic MapReduce (EMR) 允许您快速有效地处理大数据。EMR 在 EC2 实例上运行 Apache Hadoop,但简化了流程。您可以利用 Apache Hive 通过 HiveQL(一种类似于 SQL 的查询语言)查询 MapReduce 作业流。Apache Hive 作为优化查询和应用程序的一种方式。
您可以使用管理控制台的 EMR 选项卡、EMR CLI、API 或 SDK 来启动作业流。您还可以选择交互式运行 Hive 或使用脚本。
EMR 的读/写操作会影响吞吐量的消耗,但是,在大型请求中,它会使用后退算法的保护进行重试。此外,与其他操作和任务同时运行 EMR 可能会导致节流。
DynamoDB/EMR 集成不支持二进制和二进制集属性。
DynamoDB/EMR 集成先决条件
在使用 EMR 之前,请查看此必要项目清单 −
- 一个 AWS 账户
- 一个在 EMR 操作中使用的相同账户下的已填充表
- 具有 DynamoDB 连接性的自定义 Hive 版本
- DynamoDB 连接性支持
- 一个 S3 存储桶(可选)
- 一个 SSH 客户端(可选)
- 一个 EC2 密钥对(可选)
Hive 设置
在使用 EMR 之前,创建一个密钥对以交互模式运行 Hive。密钥对允许连接到作业流的 EC2 实例和主节点。
您可以按照以下步骤执行此操作 −
登录管理控制台,并打开位于 https://console.aws.amazon.com/ec2/ 的 EC2 控制台
在控制台的右上角选择一个区域。确保该区域与 DynamoDB 区域匹配。
在导航窗格中,选择密钥对。
选择创建密钥对。
在密钥对名称字段中,输入名称并选择创建。
下载生成的私钥文件,该文件使用以下格式:filename.pem。
注意 - 如果没有密钥对,则无法连接到 EC2 实例。
Hive 集群
创建一个支持 Hive 的集群来运行 Hive。它构建了 Hive 到 DynamoDB 连接所需的环境和基础设施。
您可以通过以下步骤执行此任务:
访问 EMR 控制台。
选择创建集群。
在创建屏幕中,设置集群配置,为集群指定描述性名称,为终止保护选择是,并选中日志记录的已启用,为日志文件夹 S3 位置选择 S3 目标,并为调试选择已启用。
在软件配置屏幕中,确保字段包含 Hadoop 发行版的Amazon,AMI 版本的最新版本,要安装的应用程序 - Hive 的默认 Hive 版本,以及要安装的应用程序 - Pig 的默认 Pig 版本。
在硬件配置屏幕中,确保字段包含网络的启动到 EC2-Classic,EC2 可用区的无偏好,主节点 - Amazon EC2 实例类型的默认值,请求 Spot 实例的未选中状态,核心节点 - Amazon EC2 实例类型的默认值,计数为2,请求 Spot 实例的未选中状态,任务节点 - Amazon EC2 实例类型的默认值,计数为0,以及请求 Spot 实例的未选中状态。
务必设置一个限制,提供足够的容量来防止集群故障。
在安全和访问屏幕中,确保字段包含 EC2 密钥对中的密钥对,IAM 用户访问中的无其他 IAM 用户,以及 IAM 角色中的无需角色继续。
查看引导操作屏幕,但不要修改它。
查看设置,完成后选择创建集群。
集群启动时会显示一个摘要窗格。
激活 SSH 会话
您需要一个活动的 SSH 会话才能连接到主节点并执行 CLI 操作。通过在 EMR 控制台中选择集群来查找主节点。它将主节点列为主公有 DNS 名称。
如果您没有 PuTTY,请安装它。然后启动 PuTTYgen 并选择加载。选择您的 PEM 文件并打开它。PuTTYgen 会通知您导入成功。选择保存私钥以保存为 PuTTY 私钥格式 (PPK),并选择是以在不使用密码短语的情况下保存。然后为 PuTTY 密钥输入一个名称,点击保存,然后关闭 PuTTYgen。
使用 PuTTY 通过首先启动 PuTTY 来与主节点建立连接。从类别列表中选择会话。在主机名字段中输入 hadoop@DNS。在类别列表中展开连接 > SSH,然后选择身份验证。在控制选项屏幕中,为身份验证选择浏览私钥文件。然后选择您的私钥文件并打开它。选择是以确认安全警报弹出窗口。
连接到主节点后,将出现 Hadoop 命令提示符,这意味着您可以开始交互式 Hive 会话。
Hive 表
Hive 充当数据仓库工具,允许使用HiveQL在 EMR 集群上执行查询。之前的设置为您提供了一个可用的提示符。只需输入“hive”,然后输入您想要的任何命令即可交互式地运行 Hive 命令。有关Hive的更多信息,请参阅我们的 Hive 教程。
DynamoDB - 表活动
DynamoDB 流使您能够跟踪和响应表项更改。利用此功能创建响应更改的应用程序,从而更新跨源的信息。同步大型多用户系统中数千用户的的数据。使用它向用户发送更新通知。它的应用证明了多样性和重要性。DynamoDB 流是实现此功能的主要工具。
流捕获包含表中项修改的时间排序序列。它们最多保存此数据 24 小时。应用程序使用它们来查看原始项和修改后的项,几乎是实时进行的。
为表启用的流捕获所有修改。对于任何 CRUD 操作,DynamoDB 都会创建一个流记录,其中包含已修改项的主键属性。您可以配置流以获取其他信息,例如前后图像。
流提供两种保证:
每个记录在流中只出现一次,并且
每个项修改都会导致流记录的顺序与修改的顺序相同。
所有流都实时处理,以便您可以将它们用于应用程序中的相关功能。
管理流
在创建表时,您可以启用流。现有表允许禁用流或更改设置。流提供异步操作功能,这意味着不会影响表性能。
利用 AWS 管理控制台进行简单的流管理。首先,导航到控制台,然后选择表。在“概述”选项卡中,选择管理流。在窗口中,选择添加到表数据修改时流中的信息。输入所有设置后,选择启用。
如果要禁用任何现有流,请选择管理流,然后选择禁用。
您还可以利用 API CreateTable 和 UpdateTable 来启用或更改流。使用参数 StreamSpecification 配置流。StreamEnabled 指定状态,启用为 true,禁用为 false。
StreamViewType 指定添加到流中的信息:KEYS_ONLY、NEW_IMAGE、OLD_IMAGE 和 NEW_AND_OLD_IMAGES。
流读取
通过连接到端点并发出 API 请求来读取和处理流。每个流都由流记录组成,每个记录都作为一个拥有流的单个修改存在。流记录包含一个序列号,揭示发布顺序。记录属于称为分片的组。分片充当多个记录的容器,并且还保存访问和遍历记录所需的信息。24 小时后,记录会自动删除。
这些分片根据需要生成和删除,并且持续时间不长。它们还会自动划分为多个新分片,通常是响应写入活动峰值。在禁用流时,打开的分片会关闭。分片之间的层次关系意味着应用程序必须优先处理父分片以确保正确的处理顺序。您可以使用 Kinesis 适配器自动执行此操作。
注意 - 导致无更改的操作不会写入流记录。
访问和处理记录需要执行以下任务:
- 确定目标流的 ARN。
- 确定包含目标记录的流的分片。
- 访问分片以检索所需的记录。
注意 - 最多应有两个进程同时读取一个分片。如果超过 2 个进程,则可能会限制源。
可用的流 API 操作包括
- ListStreams
- DescribeStream
- GetShardIterator
- GetRecords
您可以查看以下流读取示例:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBStreamsClient;
import com.amazonaws.services.dynamodbv2.model.AttributeAction;
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.AttributeValueUpdate;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.DescribeStreamRequest;
import com.amazonaws.services.dynamodbv2.model.DescribeStreamResult;
import com.amazonaws.services.dynamodbv2.model.DescribeTableResult;
import com.amazonaws.services.dynamodbv2.model.GetRecordsRequest;
import com.amazonaws.services.dynamodbv2.model.GetRecordsResult;
import com.amazonaws.services.dynamodbv2.model.GetShardIteratorRequest;
import com.amazonaws.services.dynamodbv2.model.GetShardIteratorResult;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
import com.amazonaws.services.dynamodbv2.model.Record;
import com.amazonaws.services.dynamodbv2.model.Shard;
import com.amazonaws.services.dynamodbv2.model.ShardIteratorType;
import com.amazonaws.services.dynamodbv2.model.StreamSpecification;
import com.amazonaws.services.dynamodbv2.model.StreamViewType;
import com.amazonaws.services.dynamodbv2.util.Tables;
public class StreamsExample {
private static AmazonDynamoDBClient dynamoDBClient =
new AmazonDynamoDBClient(new ProfileCredentialsProvider());
private static AmazonDynamoDBStreamsClient streamsClient =
new AmazonDynamoDBStreamsClient(new ProfileCredentialsProvider());
public static void main(String args[]) {
dynamoDBClient.setEndpoint("InsertDbEndpointHere");
streamsClient.setEndpoint("InsertStreamEndpointHere");
// table creation
String tableName = "MyTestingTable";
ArrayList<AttributeDefinition> attributeDefinitions =
new ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition()
.withAttributeName("ID")
.withAttributeType("N"));
ArrayList<KeySchemaElement> keySchema = new
ArrayList<KeySchemaElement>();
keySchema.add(new KeySchemaElement()
.withAttributeName("ID")
.withKeyType(KeyType.HASH)); //Partition key
StreamSpecification streamSpecification = new StreamSpecification();
streamSpecification.setStreamEnabled(true);
streamSpecification.setStreamViewType(StreamViewType.NEW_AND_OLD_IMAGES);
CreateTableRequest createTableRequest = new CreateTableRequest()
.withTableName(tableName)
.withKeySchema(keySchema)
.withAttributeDefinitions(attributeDefinitions)
.withProvisionedThroughput(new ProvisionedThroughput()
.withReadCapacityUnits(1L)
.withWriteCapacityUnits(1L))
.withStreamSpecification(streamSpecification);
System.out.println("Executing CreateTable for " + tableName);
dynamoDBClient.createTable(createTableRequest);
System.out.println("Creating " + tableName);
try {
Tables.awaitTableToBecomeActive(dynamoDBClient, tableName);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Get the table's stream settings
DescribeTableResult describeTableResult =
dynamoDBClient.describeTable(tableName);
String myStreamArn = describeTableResult.getTable().getLatestStreamArn();
StreamSpecification myStreamSpec =
describeTableResult.getTable().getStreamSpecification();
System.out.println("Current stream ARN for " + tableName + ": "+ myStreamArn);
System.out.println("Stream enabled: "+ myStreamSpec.getStreamEnabled());
System.out.println("Update view type: "+ myStreamSpec.getStreamViewType());
// Add an item
int numChanges = 0;
System.out.println("Making some changes to table data");
Map<String, AttributeValue> item = new HashMap<String, AttributeValue>();
item.put("ID", new AttributeValue().withN("222"));
item.put("Alert", new AttributeValue().withS("item!"));
dynamoDBClient.putItem(tableName, item);
numChanges++;
// Update the item
Map<String, AttributeValue> key = new HashMap<String, AttributeValue>();
key.put("ID", new AttributeValue().withN("222"));
Map<String, AttributeValueUpdate> attributeUpdates =
new HashMap<String, AttributeValueUpdate>();
attributeUpdates.put("Alert", new AttributeValueUpdate()
.withAction(AttributeAction.PUT)
.withValue(new AttributeValue().withS("modified item")));
dynamoDBClient.updateItem(tableName, key, attributeUpdates);
numChanges++;
// Delete the item
dynamoDBClient.deleteItem(tableName, key);
numChanges++;
// Get stream shards
DescribeStreamResult describeStreamResult =
streamsClient.describeStream(new DescribeStreamRequest()
.withStreamArn(myStreamArn));
String streamArn =
describeStreamResult.getStreamDescription().getStreamArn();
List<Shard> shards =
describeStreamResult.getStreamDescription().getShards();
// Process shards
for (Shard shard : shards) {
String shardId = shard.getShardId();
System.out.println("Processing " + shardId + " in "+ streamArn);
// Get shard iterator
GetShardIteratorRequest getShardIteratorRequest = new
GetShardIteratorRequest()
.withStreamArn(myStreamArn)
.withShardId(shardId)
.withShardIteratorType(ShardIteratorType.TRIM_HORIZON);
GetShardIteratorResult getShardIteratorResult =
streamsClient.getShardIterator(getShardIteratorRequest);
String nextItr = getShardIteratorResult.getShardIterator();
while (nextItr != null && numChanges > 0) {
// Read data records with iterator
GetRecordsResult getRecordsResult =
streamsClient.getRecords(new GetRecordsRequest().
withShardIterator(nextItr));
List<Record> records = getRecordsResult.getRecords();
System.out.println("Pulling records...");
for (Record record : records) {
System.out.println(record);
numChanges--;
}
nextItr = getRecordsResult.getNextShardIterator();
}
}
}
}
DynamoDB - 错误处理
如果请求处理失败,DynamoDB 会抛出一个错误。每个错误都包含以下组件:HTTP 状态代码、异常名称和消息。错误管理取决于您的 SDK(它会传播错误)或您自己的代码。
代码和消息
异常属于不同的 HTTP 标头状态代码。4xx 和 5xx 包含与请求问题和 AWS 相关的错误。
HTTP 4xx 类别中的一些异常如下:
AccessDeniedException - 客户端未能正确签名请求。
ConditionalCheckFailedException - 条件评估为 false。
IncompleteSignatureException - 请求包含不完整的签名。
HTTP 5xx 类别中的异常如下:
- 内部服务器错误
- 服务不可用
重试和回退算法
错误来自各种来源,例如服务器、交换机、负载均衡器以及其他结构和系统组件。常见的解决方案包括简单的重试,它支持可靠性。所有 SDK 都自动包含此逻辑,您可以设置重试参数以满足您的应用程序需求。
例如 - Java 提供了一个 maxErrorRetry 值来停止重试。
除了重试之外,Amazon 建议使用回退解决方案来控制流量。这包括逐步增加重试之间的等待时间,并在相当短的时间后最终停止。请注意,SDK 会执行自动重试,但不会执行指数回退。
以下程序是重试回退的示例:
public enum Results {
SUCCESS,
NOT_READY,
THROTTLED,
SERVER_ERROR
}
public static void DoAndWaitExample() {
try {
// asynchronous operation.
long token = asyncOperation();
int retries = 0;
boolean retry = false;
do {
long waitTime = Math.min(getWaitTime(retries), MAX_WAIT_INTERVAL);
System.out.print(waitTime + "\n");
// Pause for result
Thread.sleep(waitTime);
// Get result
Results result = getAsyncOperationResult(token);
if (Results.SUCCESS == result) {
retry = false;
} else if (Results.NOT_READY == result) {
retry = true;
} else if (Results.THROTTLED == result) {
retry = true;
} else if (Results.SERVER_ERROR == result) {
retry = true;
} else {
// stop on other error
retry = false;
}
} while (retry && (retries++ < MAX_RETRIES));
}
catch (Exception ex) {
}
}
public static long getWaitTime(int retryCount) {
long waitTime = ((long) Math.pow(3, retryCount) * 100L);
return waitTime;
}
DynamoDB - 最佳实践
在处理各种来源和元素时,某些实践可以优化代码、防止错误并最大程度地降低吞吐量成本。
以下是 DynamoDB 中一些最重要且最常用的最佳实践。
表
表的分布意味着最佳方法是将读/写活动均匀地分布到所有表项中。
针对表项实现统一的数据访问。最佳吞吐量使用取决于主键选择和项工作负载模式。将工作负载均匀地分布到分区键值中。避免少量高使用量的分区键值之类的情况。选择更好的选择,例如大量不同的分区键值。
了解分区行为。估计 DynamoDB 自动分配的分区。
DynamoDB 提供突发吞吐量使用,它会为“突发”功率保留未使用的吞吐量。避免大量使用此选项,因为突发会快速消耗大量吞吐量;此外,它并非可靠的资源。
在上传时,分布数据以获得更好的性能。通过同时上传到所有分配的服务器来实现此目的。
缓存常用项以将读取活动从数据库转移到缓存。
项
节流、性能、大小和访问成本仍然是项方面最大的问题。选择一对多表。删除属性并划分表以匹配访问模式。通过这种简单的方法,您可以显着提高效率。
在存储大型值之前对其进行压缩。利用标准压缩工具。对大型属性值(如 S3)使用备用存储。您可以在 S3 中存储对象,并在项中存储标识符。
通过虚拟项片段将大型属性分布到多个项中。这为项大小的限制提供了解决方案。
查询和扫描
查询和扫描主要面临吞吐量消耗挑战。避免突发,突发通常是由切换到强一致性读取之类的情况引起的。以低资源的方式使用并行扫描(即没有节流的后台函数)。此外,仅在大型表中使用它们,并且在您未充分利用吞吐量或扫描操作性能较差的情况下使用它们。
本地二级索引
索引在吞吐量和存储成本以及查询效率方面存在问题。除非您经常查询属性,否则避免建立索引。在投影中谨慎选择,因为它们会使索引膨胀。仅选择那些大量使用的索引。
利用稀疏索引,这意味着排序键不会出现在所有表项中的索引。它们有利于查询大多数表项中不存在的属性。
注意项集合(所有表项及其索引)的扩展。添加/更新操作会导致表和索引都增长,集合限制为 10GB。
全局二级索引
索引在吞吐量、存储成本和查询效率方面存在问题。建议选择键属性散列,类似于表中的读/写散列,提供工作负载均匀性。选择能够均匀散列数据的属性。此外,利用稀疏索引。
利用全局二级索引来快速搜索查询中请求少量数据的场景。