

# 使用 DynamoDB 中的项目和属性
<a name="WorkingWithItems"></a>

在 Amazon DynamoDB 中，*项目*是属性的集合。每个属性都有各自的名称和值。属性值可以为标量、集或文档类型。有关更多信息，请参阅 [Amazon DynamoDB：工作原理](HowItWorks.md)。

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

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

除了四项基本 CRUD 操作之外，DynamoDB 还提供了以下操作：
+ `BatchGetItem` — 从一个或多个表中读取多达 100 个项目。
+ `BatchWriteItem` — 在一个或多个表中创建或删除多达 25 个项目。

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

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

**Topics**
+ [DynamoDB 项目大小和格式](CapacityUnitCalculations.md)
+ [读取项目](#WorkingWithItems.ReadingData)
+ [写入项目](#WorkingWithItems.WritingData)
+ [返回值](#WorkingWithItems.ReturnValues)
+ [分批操作](#WorkingWithItems.BatchOperations)
+ [原子计数器](#WorkingWithItems.AtomicCounters)
+ [有条件写入](#WorkingWithItems.ConditionalUpdate)
+ [在 DynamoDB 中使用表达式](Expressions.md)
+ [在 DynamoDB 中使用生存时间（TTL）](TTL.md)
+ [在 DynamoDB 中查询表](Query.md)
+ [在 DynamoDB 中扫描表](Scan.md)
+ [PartiQL – 用于 Amazon DynamoDB 的 SQL 兼容语言](ql-reference.md)
+ [处理项目：Java](JavaDocumentAPIItemCRUD.md)
+ [使用项目：.NET](LowLevelDotNetItemCRUD.md)

## 读取项目
<a name="WorkingWithItems.ReadingData"></a>

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

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

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

**注意**  
使用 `GetItem` 时，您必须指定*整个* 主键，而不仅仅是部分主键。例如，如果某个表具有复合主键（分区键和排序键），您必须为分区键和排序键分别提供一个值。

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

`GetItem` 返回项目的所有属性。您可以使用*投影表达式* 来仅返回一部分属性。有关更多信息，请参阅 [在 DynamoDB 中使用投影表达式](Expressions.ProjectionExpressions.md)。

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

**Example**  
以下 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
```

## 写入项目
<a name="WorkingWithItems.WritingData"></a>

要创建、更新或删除 DynamoDB 表中的项目，请使用以下操作之一：
+ `PutItem`
+ `UpdateItem`
+ `DeleteItem`

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

要返回其中任何操作占用的写入容量单位数，请将 `ReturnConsumedCapacity` 参数设置为以下项之一：
+ `TOTAL` — 返回占用的写入容量单位总数。
+ `INDEXES` — 返回占用的写入容量单位总数，其中包含表的小计和受该操作影响的任何二级索引。
+ `NONE` — 不返回任何写入容量详细信息。（这是默认值。）

### PutItem
<a name="WorkingWithItems.WritingData.PutItem"></a>

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

**Example**  
将新项目写入 `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
<a name="WorkingWithItems.WritingData.UpdateItem"></a>

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

您可使用*更新表达式* 指定要修改的属性及其新值。有关更多信息，请参阅 [在 DynamoDB 中使用更新表达式](Expressions.UpdateExpressions.md)。

在更新表达式内，您可使用表达式属性值作为实际值的占位符。有关更多信息，请参阅 [在 DynamoDB 中使用表达式属性值](Expressions.ExpressionAttributeValues.md)。

**Example**  
修改 `Thread` 项目中的各种属性。可选 `ReturnValues` 参数按更新后的情况显示项目。有关更多信息，请参阅 [返回值](#WorkingWithItems.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
<a name="WorkingWithItems.WritingData.DeleteItem"></a>

`DeleteItem` 删除带指定键的项目。

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

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

## 返回值
<a name="WorkingWithItems.ReturnValues"></a>

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

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

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

### PutItem
<a name="WorkingWithItems.ReturnValues.PutItem"></a>
+ `ReturnValues`: `ALL_OLD`
  + 如果您覆盖了现有项目，`ALL_OLD` 将按覆盖前的情况返回整个项目。
  + 如果您写入了不存在的项目，则 `ALL_OLD` 无效。

### UpdateItem
<a name="WorkingWithItems.ReturnValues.UpdateItem"></a>

`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
<a name="WorkingWithItems.ReturnValues.DeleteItem"></a>
+ `ReturnValues`: `ALL_OLD`
  + 如果您删除了现有项目，`ALL_OLD` 将按删除前情况返回整个项目。
  + 如果您删除了不存在的项目，`ALL_OLD` 不会返回任何数据。

## 分批操作
<a name="WorkingWithItems.BatchOperations"></a>

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

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

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

### BatchGetItem
<a name="WorkingWithItems.BatchOperations.BatchGetItem"></a>

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

**Example**  
从 `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
<a name="WorkingWithItems.BatchOperations.BatchWriteItem"></a>

`BatchWriteItem` 操作可包含多达 25 个单独的 `PutItem` 和 `DeleteItem` 请求且最多可写入 16 MB 的数据。（单个项目的最大大小为 400 KB。） 此外，一个 `BatchWriteItem` 操作可在多个表中放置或删除项目。

**注意**  
`BatchWriteItem` 不支持 `UpdateItem` 请求。

**Example**  
它向 `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" }
                }
            }
        }
    ]
}
```

## 原子计数器
<a name="WorkingWithItems.AtomicCounters"></a>

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

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

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

有关更多信息，请参阅 [对数值属性进行加减](Expressions.UpdateExpressions.md#Expressions.UpdateExpressions.SET.IncrementAndDecrement)。

**Example**  
以下 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
```

## 有条件写入
<a name="WorkingWithItems.ConditionalUpdate"></a>

默认情况下，DynamoDB 写入操作（`PutItem`、`DeleteItem`）是*无条件的*：每项操作都会覆盖带指定主键的现有项目。

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

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

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

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

![用户 Alice 和 Bob 尝试修改 ID 为 1 的项目，这表明需要有条件写入。](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/update-no-condition.png)


假设 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 之前的更新丢失了。

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

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

![阻止用户 Bob 的更新覆盖用户 Alice 对同一项目的更改的有条件写入。](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/update-yes-condition.png)


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 示例](Expressions.ConditionExpressions.md)。

### 带条件写入幂等性
<a name="WorkingWithItems.ConditionalWrites.Idempotence"></a>

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

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

### 带条件写入占用的容量单位
<a name="WorkingWithItems.ConditionalWrites.ReturnConsumedCapacity"></a>

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

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

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

要返回有条件写入过程中占用的写入容量单位的数量，请使用 `ReturnConsumedCapacity` 参数：
+ `TOTAL` — 返回占用的写入容量单位总数。
+ `INDEXES` — 返回占用的写入容量单位总数，其中包含表的小计和受该操作影响的任何二级索引。
+ `NONE` — 不返回任何写入容量详细信息。（这是默认值。）

  

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