使用 DynamoDB 中的项目和属性 - Amazon DynamoDB

使用 DynamoDB 中的项目和属性

在 Amazon DynamoDB 中,项目是属性的集合。每个属性都有各自的名称和值。属性值可以为标量、集或文档类型。有关更多信息,请参阅 Amazon DynamoDB:工作原理

DynamoDB 提供了用于基本的创建、读取、更新和删除 (CRUD) 功能的四项操作。所有这些操作都是原子操作。

  • PutItem — 创建项目。

  • GetItem — 读取项目。

  • UpdateItem — 更新项目。

  • DeleteItem — 删除项目。

其中每项操作均需要您指定要处理的项目的主键。例如,要使用 GetItem 读取项目,您必须指定该项目的分区键和排序键(如果适用)。

除了四项基本 CRUD 操作之外,DynamoDB 还提供了以下操作:

  • BatchGetItem — 从一个或多个表中读取多达 100 个项目。

  • BatchWriteItem — 在一个或多个表中创建或删除多达 25 个项目。

这些批处理操作可将多项 CRUD 操作组合成一个请求。此外,批处理操作还可并行读取和写入项目以最大程度地减少响应延迟。

本节将介绍如何使用这些操作并包含了相关主题,例如有条件更新和原子计数器。本节还包括使用 AWS SDK 的示例代码。

读取项目

要从 DynamoDB 表中读取项目,请使用 GetItem 操作。必需提供表的名称和所需项目的主键。

以下 AWS CLI 示例将演示如何从 ProductCatalog 表读取项目。

aws dynamodb get-item \ --table-name ProductCatalog \ --key '{"Id":{"N":"1"}}'
注意

使用 GetItem 时,您必须指定整个 主键,而不仅仅是部分主键。例如,如果某个表具有复合主键(分区键和排序键),您必须为分区键和排序键分别提供一个值。

默认情况下,GetItem 请求将执行最终一致性读取。您可以改用 ConsistentRead 参数来请求强一致性读取。(这会占用额外的读取容量单位,但会返回该项目的最新版本。)

GetItem 返回项目的所有属性。您可以使用投影表达式 来仅返回一部分属性。有关更多信息,请参阅 在 DynamoDB 中使用投影表达式

要返回由 GetItem 占用的读取容量单位数,请将 ReturnConsumedCapacity 参数设置为 TOTAL

以下 AWS Command Line Interface (AWS CLI) 示例将演示一些可选的 GetItem 参数。

aws dynamodb get-item \ --table-name ProductCatalog \ --key '{"Id":{"N":"1"}}' \ --consistent-read \ --projection-expression "Description, Price, RelatedItems" \ --return-consumed-capacity TOTAL

写入项目

要创建、更新或删除 DynamoDB 表中的项目,请使用以下操作之一:

  • PutItem

  • UpdateItem

  • DeleteItem

对于这些操作中的每一项,您必须指定完整的主键,而不仅仅是部分主键。例如,如果某个表具有复合主键(分区键和排序键),您必须为分区键和排序键分别提供一个值。

要返回其中任何操作占用的写入容量单位数,请将 ReturnConsumedCapacity 参数设置为以下项之一:

  • TOTAL — 返回占用的写入容量单位总数。

  • INDEXES — 返回占用的写入容量单位总数,其中包含表的小计和受该操作影响的任何二级索引。

  • NONE — 不返回任何写入容量详细信息。(这是默认值。)

PutItem

PutItem 创建新项目。如果表中已存在具有相同键的项目,它将被替换为新项目。

将新项目写入 Thread 表。Thread 的主键包含 ForumName(分区键)和 Subject(排序键)。

aws dynamodb put-item \ --table-name Thread \ --item file://item.json

--item 的参数存储在 item.json 文件中。

{ "ForumName": {"S": "Amazon DynamoDB"}, "Subject": {"S": "New discussion thread"}, "Message": {"S": "First post in this thread"}, "LastPostedBy": {"S": "fred@example.com"}, "LastPostDateTime": {"S": "201603190422"} }

UpdateItem

如果带指定键的项目不存在,则 UpdateItem 会创建一个新项目。否则,它会修改现有项目的属性。

您可使用更新表达式 指定要修改的属性及其新值。有关更多信息,请参阅 在 DynamoDB 中使用更新表达式

在更新表达式内,您可使用表达式属性值作为实际值的占位符。有关更多信息,请参阅 在 DynamoDB 中使用表达式属性值

修改 Thread 项目中的各种属性。可选 ReturnValues 参数按更新后的情况显示项目。有关更多信息,请参阅 返回值

aws dynamodb update-item \ --table-name Thread \ --key file://key.json \ --update-expression "SET Answered = :zero, Replies = :zero, LastPostedBy = :lastpostedby" \ --expression-attribute-values file://expression-attribute-values.json \ --return-values ALL_NEW

--key 的参数存储在 key.json 文件中。

{ "ForumName": {"S": "Amazon DynamoDB"}, "Subject": {"S": "New discussion thread"} }

--expression-attribute-values 的参数存储在 expression-attribute-values.json 文件中。

{ ":zero": {"N":"0"}, ":lastpostedby": {"S":"barney@example.com"} }

DeleteItem

DeleteItem 删除带指定键的项目。

下面的 AWS CLI 示例说明如何删除 Thread 表。

aws dynamodb delete-item \ --table-name Thread \ --key file://key.json

返回值

在某些情况下,您可能希望 DynamoDB 按您修改特定属性值之前或之后的情况返回这些值。PutItemUpdateItemDeleteItem 操作均具有一个 ReturnValues 参数,您可使用该参数返回属性在修改前或修改后的值。

ReturnValues 的默认值为 NONE,这表示 DynamoDB 不会返回有关已修改属性的任何信息。

下面是 ReturnValues 的其他有效设置,这些设置按照 DynamoDB API 操作排列。

PutItem

  • ReturnValues: ALL_OLD

    • 如果您覆盖了现有项目,ALL_OLD 将按覆盖前的情况返回整个项目。

    • 如果您写入了不存在的项目,则 ALL_OLD 无效。

UpdateItem

UpdateItem 的最常见用途是更新现有项目。但是,UpdateItem 实际上会执行 upsert 操作,这意味着,如果项目尚不存在,upsert 将自动创建项目。

  • ReturnValues: ALL_OLD

    • 如果您更新了现有项目,ALL_OLD 将按更新前的情况返回整个项目。

    • 如果您更新了不存在的项目 (upsert),则 ALL_OLD 无效。

  • ReturnValues: ALL_NEW

    • 如果您更新了现有项目,ALL_NEW 将按更新后的情况返回整个项目。

    • 如果您更新了不存在的项目 (upsert),ALL_NEW 将返回整个项目。

  • ReturnValues: UPDATED_OLD

    • 如果您更新了现有项目,UPDATED_OLD 将仅返回已更新的属性(按更新前的情况)。

    • 如果您更新了不存在的项目 (upsert),则 UPDATED_OLD 无效。

  • ReturnValues: UPDATED_NEW

    • 如果您更新了现有项目,UPDATED_NEW 将仅返回受影响的属性(按更新后的情况)。

    • 如果您更新了不存在的项目(upsert),UPDATED_NEW 将仅返回已更新的属性(按更新后的情况)。

DeleteItem

  • ReturnValues: ALL_OLD

    • 如果您删除了现有项目,ALL_OLD 将按删除前情况返回整个项目。

    • 如果您删除了不存在的项目,ALL_OLD 不会返回任何数据。

分批操作

对于需要读取和写入多个项目的应用程序,DynamoDB 提供了 BatchGetItemBatchWriteItem 操作。使用这些操作可减少从您的应用程序到 DynamoDB 的网络往返行程数。此外,DynamoDB 还可并行执行各个读取或写入操作。您的应用程序将受益于这种并行机制,并且无需管理并发度或线程。

批处理操作本质上是围绕多个读取或写入请求的包装程序。例如,如果一个 BatchGetItem 请求包含五个项目,则 DynamoDB 会代表您执行五次 GetItem 操作。同样,如果一个 BatchWriteItem 请求包含两个放置请求和四个删除请求,则 DynamoDB 会执行两次 PutItem 和四次 DeleteItem 请求。

通常,除非一个批处理操作中的所有 请求都失败,否则批处理操作不会失败。例如,假设您执行了一个 BatchGetItem 操作,但该批处理中的单独的 GetItem 请求之一失败。在这种情况下,BatchGetItem 会返回来自失败的 GetItem 请求的键和数据。该批处理中的其他 GetItem 请求不会受影响。

BatchGetItem

一个 BatchGetItem 操作可包含多达 100 个单独的 GetItem 请求且可检索多达 16 MB 的数据。此外,一个 BatchGetItem 操作可从多个表中检索项目。

Thread 表中检索两个项目,并使用投影表达式仅返回一部分属性。

aws dynamodb batch-get-item \ --request-items file://request-items.json

--request-items 的参数存储在 request-items.json 文件中。

{ "Thread": { "Keys": [ { "ForumName":{"S": "Amazon DynamoDB"}, "Subject":{"S": "DynamoDB Thread 1"} }, { "ForumName":{"S": "Amazon S3"}, "Subject":{"S": "S3 Thread 1"} } ], "ProjectionExpression":"ForumName, Subject, LastPostedDateTime, Replies" } }

BatchWriteItem

BatchWriteItem 操作可包含多达 25 个单独的 PutItemDeleteItem 请求且最多可写入 16 MB 的数据。(单个项目的最大大小为 400 KB。) 此外,一个 BatchWriteItem 操作可在多个表中放置或删除项目。

注意

BatchWriteItem 不支持 UpdateItem 请求。

它向 ProductCatalog 表中写入两个项目。

aws dynamodb batch-write-item \ --request-items file://request-items.json

--request-items 的参数存储在 request-items.json 文件中。

{ "ProductCatalog": [ { "PutRequest": { "Item": { "Id": { "N": "601" }, "Description": { "S": "Snowboard" }, "QuantityOnHand": { "N": "5" }, "Price": { "N": "100" } } } }, { "PutRequest": { "Item": { "Id": { "N": "602" }, "Description": { "S": "Snow shovel" } } } } ] }

原子计数器

您可以使用 UpdateItem 操作来实施原子计数器,一种无条件递增的数字属性,不会干扰其他写入请求。(所有写入请求的应用顺序跟接收顺序相同。) 使用原子计数器时,更新不是幂等的。换言之,该数值在您每次调用 UpdateItem 时递增或者递减。如果用于更新原子计数器的增量值为正,则可能导致计数偏多。如果该增量值为负,则可能导致计数偏少。

您可使用原子计数器跟踪网站的访问者的数量。在这种情况下,您的应用程序将以某个数字值递增,无论其当前值如何。如果 UpdateItem 操作失败,该应用程序只需重试该操作即可。这会产生更新两次计数器的风险,但您可能能够容忍对网站访问者的计数稍微偏多或偏少。

在无法容忍计数偏多或偏少的情况下(例如,在银行应用程序中),原子计数器将不适用。在此情况下,使用有条件更新比使用原子计数器更安全。

有关更多信息,请参阅 对数值属性进行加减

以下 AWS CLI 示例以 5 为递增量提高产品的 Price。在本示例中,更新计数器之前,已知该项目存在。(由于 UpdateItem 不是幂等的,Price 在您每次运行此代码时增加。)

aws dynamodb update-item \ --table-name ProductCatalog \ --key '{"Id": { "N": "601" }}' \ --update-expression "SET Price = Price + :incr" \ --expression-attribute-values '{":incr":{"N":"5"}}' \ --return-values UPDATED_NEW

带条件写入

默认情况下,DynamoDB 写入操作(PutItemUpdateItemDeleteItem)是无条件的:其中每项操作都会覆盖带指定主键的现有项目。

DynamoDB 可以选择性地对这些操作支持有条件写入。有条件写入仅在项目属性满足一个或多个预期条件时才会成功。否则,它会返回错误。

条件写入会根据项目的最新更新版本检查其条件。请注意,如果该项目以前不存在,或者最近对该项目成功执行的操作是删除,则条件写入将找不到以前的项目。

有条件写入在很多情况下很有用。例如,您可能希望 PutItem 操作仅在尚不存在具有相同主键的项目时成功。或者,如果某个项目的其中一个属性具有一个特定值,您可以阻止 UpdateItem 操作修改该项目。

有条件写入在多个用户尝试修改同一项目的情况下很有用。请考虑下图,其中两位用户(Alice 和 Bob)正在处理 DynamoDB 表中的同一项目。

用户 Alice 和 Bob 尝试修改 ID 为 1 的项目,这表明需要有条件写入。

假设 Alice 使用 AWS CLI 将 Price 属性更新为 8。

aws dynamodb update-item \ --table-name ProductCatalog \ --key '{"Id":{"N":"1"}}' \ --update-expression "SET Price = :newval" \ --expression-attribute-values file://expression-attribute-values.json

--expression-attribute-values 的参数存储在文件 expression-attribute-values.json 中:

{ ":newval":{"N":"8"} }

现在假设 Bob 稍后发出一个相似的 UpdateItem 请求,但将 Price 更改为 12。对于 Bob,--expression-attribute-values 参数类似于以下形式。

{ ":newval":{"N":"12"} }

Bob 的请求成功,但 Alice 之前的更新丢失了。

要请求有条件 PutItemDeleteItemUpdateItem,请指定一个条件表达式。条件表达式 是一个包含属性名称、条件运算符和内置函数的字符串。整个表达式的求值结果必须为 true。否则,该操作将失败。

现在考虑下图,该图展示了有条件写入将如何阻止 Alice 的更新被覆盖。

阻止用户 Bob 的更新覆盖用户 Alice 对同一项目的更改的有条件写入。

Alice 首次尝试将 Price 更新为 8,但仅在当前 Price 为 10 时才执行此操作。

aws dynamodb update-item \ --table-name ProductCatalog \ --key '{"Id":{"N":"1"}}' \ --update-expression "SET Price = :newval" \ --condition-expression "Price = :currval" \ --expression-attribute-values file://expression-attribute-values.json

--expression-attribute-values 的参数存储在 expression-attribute-values.json 文件中。

{ ":newval":{"N":"8"}, ":currval":{"N":"10"} }

由于条件的计算结果为 true,Alice 的更新成功了。

接下来,Bob 尝试将 Price 更新为 12,但仅在当前 Price 为 10 时才执行此操作。对于 Bob,--expression-attribute-values 参数类似于以下形式。

{ ":newval":{"N":"12"}, ":currval":{"N":"10"} }

由于 Alice 之前已将 Price 更改为 8,因此条件表达式的计算结果为 false,Bob 的更新失败。

有关更多信息,请参阅 DynamoDB 条件表达式 CLI 示例

带条件写入幂等性

如果条件检查位于同一个要更新的属性上,则条件写入可以是幂等 的。这意味着,仅当项目中的某些属性值与您在请求时期望它们具有的值匹配时,DynamoDB 才执行给定的写入请求。

例如,假设您发出一个 UpdateItem 请求来以 3 为递增量提高某个项目的 Price,但仅在 Price 当前为 20 时才执行此操作。在已发送该请求但尚未获得返回的结果之间的时间内,网络出现了错误,您不知道该请求是否成功。由于此条件写入是幂等的,您可以重试同一 UpdateItem 请求,而 DynamoDB 将仅在 Price 当前为 20 时更新项目。

带条件写入占用的容量单位

即使 ConditionExpression 在条件写入过程中计算结果为 false,DynamoDB 仍消耗表的写入容量。消耗量取决于现有项目的大小(或最少为 1 个)。例如,如果现有项目为 300kb,而您尝试创建或更新的新项目为 310kb,则当条件失败时,消耗的写入容量单位将为 300,当条件成功时,消耗的写入容量单位将为 310。如果这是新项目(没有现有项目),当条件失败时,消耗的写入容量单位将为 1;当条件成功时,则消耗的写入容量单位为 310。

注意

写入操作仅占用写入容量单位。它们从不占用读取容量单位。

失败的条件写入将返回 ConditionalCheckFailedException。发生这种情况时,您不会在响应中收到有关所消耗写入容量的任何信息。

要返回有条件写入过程中占用的写入容量单位的数量,请使用 ReturnConsumedCapacity 参数:

  • TOTAL — 返回占用的写入容量单位总数。

  • INDEXES — 返回占用的写入容量单位总数,其中包含表的小计和受该操作影响的任何二级索引。

  • NONE — 不返回任何写入容量详细信息。(这是默认值。)

注意

与全局二级索引不同的是,本地二级索引与其表共享其预调配的吞吐容量。对本地二级索引执行的读取和写入活动会占用表的预置的吞吐容量。