

# 使用 DynamoDB 进行设计和架构的最佳实践
<a name="best-practices"></a>

使用此节可以快速找到使用 DynamoDB 尽可能提高性能和降低吞吐量成本的建议。

**Topics**
+ [适用于 DynamoDB 的 NoSQL 设计](bp-general-nosql-design.md)
+ [使用 DynamoDB Well-Architected Lens 优化您的 DynamoDB 工作负载](bp-wal.md)
+ [在 DynamoDB 中设计并有效使用分区键的最佳实践](bp-partition-key-design.md)
+ [在 DynamoDB 中使用排序键整理数据的最佳实践](bp-sort-keys.md)
+ [在 DynamoDB 中使用二级索引的最佳实践](bp-indexes.md)
+ [在 DynamoDB 中存储大型项目和属性的最佳实践](bp-use-s3-too.md)
+ [在 DynamoDB 中处理时间序列数据的最佳实践](bp-time-series.md)
+ [在 DynamoDB 表中管理多对多关系的最佳实践](bp-adjacency-graphs.md)
+ [在 DynamoDB 中查询和扫描数据的最佳实践](bp-query-scan.md)
+ [DynamoDB 表设计的最佳实践](bp-table-design.md)
+ [使用 DynamoDB 全局表](bp-global-table-design.md)
+ [在 DynamoDB 中管理控制面板的最佳实践](bp-control-plane.md)
+ [在 DynamoDB 中使用批量数据操作的最佳实践](BestPractices_BulkDataOperations.md)
+ [在 DynamoDB 中处理并发更新的最佳实践](BestPractices_ImplementingVersionControl.md)
+ [了解 DynamoDB 中的 Amazon 账单和使用情况报告的最佳实践](bp-understanding-billing.md)
+ [将 DynamoDB 表从一个账户迁移到另一个账户](bp-migrating-table-between-accounts.md)
+ [将 DAX 与 DynamoDB 应用程序集成的规范性指南](dax-prescriptive-guidance.md)
+ [使用适用于 Amazon DynamoDB 的 Amazon PrivateLink 时的注意事项](privatelink-interface-endpoints.md#privatelink-considerations) 

# 适用于 DynamoDB 的 NoSQL 设计
<a name="bp-general-nosql-design"></a>

NoSQL 数据库系统（如 Amazon DynamoDB）使用其他数据管理模型，如键-值对或文档存储。从关系数据库管理系统转向 NoSQL 数据库系统（如 DynamoDB）时，务必了解重要区别和特定设计方法。

**Topics**
+ [关系数据设计和 NoSQL 之间的区别](#bp-general-nosql-design-vs-relational)
+ [NoSQL 设计的两个关键概念](#bp-general-nosql-design-concepts)
+ [了解 NoSQL 设计](#bp-general-nosql-design-approach)
+ [NoSQL Workbench for DynamoDB](#bp-general-nosql-workbench)

## 关系数据设计和 NoSQL 之间的区别
<a name="bp-general-nosql-design-vs-relational"></a>

关系数据库系统 (RDBMS) 和 NoSQL 数据库各有优劣：
+ 在 RDBMS 中，可以灵活查询数据，但查询成本相对较高，不能很好地扩展适应高流量情况（参见 [在 DynamoDB 中为关系数据建模的初始步骤](bp-modeling-nosql.md)）。
+ 在 NoSQL 数据库（如 DynamoDB）中，高效查询数据的方式有限，此外查询成本高且速度慢。

这些区别使得这两个系统的数据库设计不同：
+ 在 RDBMS 中，针对灵活性设计，不必担心实现细节或性能。查询优化通常不会影响架构设计，但标准化很重要。
+ 在 DynamoDB 中，对架构进行专门设计，尽可能快速低成本实现最常见和最重要的查询。根据业务使用案例的具体要求定制数据结构。

## NoSQL 设计的两个关键概念
<a name="bp-general-nosql-design-concepts"></a>

NoSQL 设计需要不同于 RDBMS 设计的思维模式。对于 RDBMS，可以创建标准化数据模型，不考虑访问模式。以后出现新问题和查询要求后，进行扩展。可以将每种类型的数据整理到各自的表中。

**NoSQL 设计的不同之处**
+ 相比之下，应该先了解需要解答的问题，然后再开始设计 DynamoDB 架构。事先了解业务问题和应用程序使用案例十分重要。
+ 应在 DynamoDB 应用程序中保留尽可能少的表。使用较少的表可以提高事物的可扩展性，减少权限管理需求，并降低 DynamoDB 应用程序的开销。此外还可以帮助降低总体备份成本。

## 了解 NoSQL 设计
<a name="bp-general-nosql-design-approach"></a>

*设计 DynamoDB 应用程序的第一步是确定系统必须满足的具体查询模式。*

具体来说，开始前务必了解应用程序访问模式的三个基本属性：
+ **数据大小**：了解一次存储和请求的数据量将有助于确定对数据进行分区的最有效方法。
+ **数据形状**：NoSQL 数据库处理查询时不会改变数据形状（RDBMS 系统会这样做），而是整理数据，使数据库中的数据形状与查询内容对应。这是加快速度并增强可扩展性的一个关键因素。
+ **数据速度**：DynamoDB 通过增加可用于处理查询的物理分区数量，并在这些分区高效分布数据来进行扩展。事先了解峰值查询负载可能有助于确定数据分区方式，从而最高效地使用 I/O 容量。

确定具体查询要求后，可以根据管理性能的一般原则整理数据：
+ **将相关数据放在一起。**   研究表明，将相关数据保存在一个地方的“引用局部性”原则是提高 NoSQL 系统性能和缩短响应时间的关键因素，就像许多年前人们发现它对优化路由表很重要一样。

  通常应在 DynamoDB 应用程序中保留尽可能少的表。

  例外情况是指涉及大量时间序列数据，或数据集具有明显不同访问模式的情况。具有反向索引的单个表通常支持简单查询创建和检索应用程序所需的复杂层次数据结构。
+ **使用排序顺序。**   如果键设计能够一起排序，可以将相关项目组织在一起，高效查询。这是一个重要的 NoSQL 设计策略。
+ **分布查询。**   大量查询不能集中在数据库的某个部分，这样可能超出 I/O 容量，这一点也很重要。应设计数据键，在尽可能多的分区均匀分布流量，从而避免“热点”。
+ **使用全局二级索引。**   通过创建特定全局二级索引，可以实现与主表支持的查询不同的查询，并且仍然快速，成本低。

这些通用原则转化为一些常见设计模式，用于在 DynamoDB 中高效建模数据。

## NoSQL Workbench for DynamoDB
<a name="bp-general-nosql-workbench"></a>

 [NoSQL Workbench for DynamoDB](workbench.md) 是一个跨平台的客户端 GUI 应用程序，可用于现代数据库开发和运营。它适用于 Windows、macOS 和 Linux 系统。NoSQL Workbench 是一个可视化开发工具，提供数据建模、数据可视化、示例数据生成和查询开发功能，可帮助您设计、创建、查询和管理 DynamoDB 表。通过 NoSQL Workbench for DynamoDB，您可以构建新数据模型，或根据现有模型设计符合应用程序数据访问模式的模型。您还可以在过程结束时导入和导出设计的数据模型。有关更多信息，请参阅 [使用 NoSQL Workbench 构建数据模型](workbench.Modeler.md)。

# 使用 DynamoDB Well-Architected Lens 优化您的 DynamoDB 工作负载
<a name="bp-wal"></a>

本节介绍了 Amazon DynamoDB Well-Architected Lens，这是一组用于设计架构完善的 DynamoDB 工作负载的设计原则和指南。

# 优化 DynamoDB 表的成本
<a name="bp-cost-optimization"></a>

本节介绍有关如何优化现有 DynamoDB 表的成本的最佳实践。您应该查看以下策略，看看哪种成本优化策略最适合您的需求，并以迭代方式处理它们。每种策略都将概述可能影响您的成本的因素、要寻找的迹象以及如何降低成本的规范性指南。

**Topics**
+ [在表级别评估您的成本](CostOptimization_TableLevelCostAnalysis.md)
+ [评估 DynamoDB 表的容量模式](CostOptimization_TableCapacityMode.md)
+ [评估 DynamoDB 表的自动扩缩设置](CostOptimization_AutoScalingSettings.md)
+ [评估您的 DynamoDB 表类选择](CostOptimization_TableClass.md)
+ [确定 DynamoDB 中未使用的资源](CostOptimization_UnusedResources.md)
+ [评估您的 DynamoDB 表使用模式](CostOptimization_TableUsagePatterns.md)
+ [评估 DynamoDB 流使用情况](CostOptimization_StreamsUsage.md)
+ [评估预置容量以在 DynamoDB 表中预置合适的容量大小](CostOptimization_RightSizedProvisioning.md)

# 在表级别评估您的成本
<a name="CostOptimization_TableLevelCostAnalysis"></a>

 利用 Amazon Web Services 管理控制台 中提供的 Cost Explorer 工具，您可以查看按类型细分的成本，例如读取、写入、存储和备份费用。您还可以查看按时段（例如月或日）汇总的这些成本。

管理员可能面临的一个挑战是，只需要审核一个特定表的成本。其中一些数据可通过 DynamoDB 控制台或通过调用 `DescribeTable` API 获得，但默认情况下，Cost Explorer 不允许您按与特定表关联的成本进行筛选或分组。此部分将向您展示如何在 Cost Explorer 中，使用标签执行单个表成本分析。

**Topics**
+ [如何查看单个 DynamoDB 表的成本](#CostOptimization_TableLevelCostAnalysis_ViewInfo)
+ [Cost Explorer 的默认视图](#CostOptimization_TableLevelCostAnalysis_CostExplorer)
+ [如何在 Cost Explorer 中使用和应用表标签](#CostOptimization_TableLevelCostAnalysis_Tagging)

## 如何查看单个 DynamoDB 表的成本
<a name="CostOptimization_TableLevelCostAnalysis_ViewInfo"></a>

Amazon DynamoDB Amazon Web Services 管理控制台 和 `DescribeTable` API 都将向您显示有关单个表的信息，包括主键架构、表中的任意索引以及表和任意索引的大小和项目计数。可以使用表的大小加上索引的大小来计算表的每月存储成本。例如，在 us-east-1 区域中为每 GB 0.25 美元。

如果表采用预置容量模式，则还会返回当前 RCU 和 WCU 设置。这些数据可以用来计算表的当前读取和写入成本，但是这些成本可能会发生变化，尤其是在表配置了自动扩缩功能的情况下。

**注意**  
如果表采用按需容量模式，则 `DescribeTable` 无助于估算吞吐量成本，因为在任意时段内，这些成本是根据实际使用量而不是预置使用量计费的。

## Cost Explorer 的默认视图
<a name="CostOptimization_TableLevelCostAnalysis_CostExplorer"></a>

Cost Explorer 的默认视图提供显示吞吐量和存储等已用资源成本的图表。您可以选择按时段对成本进行分组，例如按月或按日列出总值。存储、读取、写入和其他功能的成本也可以进行细分和比较。

![\[Cost Explorer 成本管理服务的默认视图，显示按使用类型分组的已使用资源的成本。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/CostExplorerView.png)


## 如何在 Cost Explorer 中使用和应用表标签
<a name="CostOptimization_TableLevelCostAnalysis_Tagging"></a>

默认情况下，Cost Explorer 不会提供任何一个特定表的成本汇总，因为它会将多个表的成本合并为一个总额。不过，您可以使用 [Amazon 资源标记](https://docs.amazonaws.cn/general/latest/gr/aws_tagging.html)，通过元数据标签来标识各个表。标签是可用于多种用途的键/值对，例如标识属于某个项目或部门的所有资源。在本示例中，我们假设您有一个名为 **MyTable** 的表。

1. 使用键 **table\$1name** 和值 **MyTable** 设置一个标签。

1. [在 Cost Explorer 中激活标签](https://docs.amazonaws.cn/awsaccountbilling/latest/aboutv2/activating-tags.html)，然后筛选标签值，以便更清楚地了解每个表的成本。

**注意**  
标签可能需要一两天的时间才会开始显示在 Cost Explorer 中

您可以在控制台中设置自己的元数据标签，也可以通过 Amazon CLI 或 Amazon SDK 等自动化方法来设置。考虑一下，在您组织的新表创建流程中要求设置 **table\$1name** 标签。对于现有表，有一个 Python 实用程序，可用于在您账户的某个特定区域中，对所有现有表查找和应用这些标签。有关更多详细信息，请参阅 [GitHub 上的同名表标记器](https://github.com/awslabs/amazon-dynamodb-tools#eponymous-table-tagger-tool)。

# 评估 DynamoDB 表的容量模式
<a name="CostOptimization_TableCapacityMode"></a>

本部分概述如何为 DynamoDB 表选择合适的容量模式。每种模式都经过优化，以满足不同工作负载对吞吐量变化的响应能力以及使用量计费方式的需求。在制定决策时，您必须平衡这些因素。

**Topics**
+ [有哪些表容量模式可用](#CostOptimization_TableCapacityMode_Overview)
+ [何时选择按需容量模式](#CostOptimization_TableCapacityMode_OnDemand)
+ [何时选择预置容量模式](#CostOptimization_TableCapacityMode_Provisioned)
+ [选择表容量模式时需要考虑的其他因素](#CostOptimization_TableCapacityMode_AdditionalFactors)

## 有哪些表容量模式可用
<a name="CostOptimization_TableCapacityMode_Overview"></a>

创建 DynamoDB 表时，您必须选择按需容量模式或预置容量模式。

在 24 小时滚动窗口内，表最多可以从预置容量模式切换到按需模式四次。您可以随时将表从按需模式切换到预置容量模式。

**按需容量模式**  
[按需容量模式](on-demand-capacity-mode.md)旨在消除规划或预置 DynamoDB 表容量的需求。在此模式下，您的表会即时适应对表的请求数，无需扩展或缩减任何资源（最高为表之前峰值吞吐量的两倍）。

DynamoDB 按需模式针对读取和写入请求提供按请求支付定价，您只需为使用的资源付费。

**预置容量模式**  
[预置容量](provisioned-capacity-mode.md)模式是一种更为传统的模式，在这种模式下，您必须定义表可供请求使用的容量，此容量可以是直接供请求使用的，也可以是借助自动扩缩功能供请求使用的。由于在任何给定时间表都为表预置了特定的容量，因此，将基于预置的总容量而不是使用的请求数量来进行计费。而且，如果请求数超出分配的容量，就会导致表拒绝请求，降低应用程序用户的体验。

预置容量模式要求持续监控，以便在不过度预置表容量与避免预置容量不足之间找到平衡点，从而尽量避免节流并优化成本。

## 何时选择按需容量模式
<a name="CostOptimization_TableCapacityMode_OnDemand"></a>

在优化成本时，如果工作负载类似于下面的各图，则按需模式是您的最佳选择。

以下因素会导致此类工作负载：
+ 随时间推移而演变的流量模式 
+ 请求数量变动（由批量工作负载导致）
+ 不可预测的请求时机（导致流量峰值）
+ 在给定时段内降至零或峰值的 30% 以下 

![\[这些图显示了不可预测、可变的工作负载，包括峰值和活动量较低的时段。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/choose-on-demand-1.png)![\[这些图显示了不可预测、可变的工作负载，包括峰值和活动量较低的时段。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/choose-on-demand-2.png)


对于具有上述因素的工作负载，使用自动扩缩功能在表上维持足够的容量来应对流量峰值时，可能会导致过度预置表容量而超出必要的成本，或者表容量预置不足，请求受到不必要的限制。按需容量模式是更好的选择，因为它可以处理波动的流量，而无需您预测或调整容量。

使用按需模式的按请求付费定价模式，您不必担心闲置的容量，因为您只需为您实际使用的吞吐量付费。将按所消耗的读取或写入请求向您收费，因此成本直接反映了实际使用量，从而可以轻松平衡成本和性能。或者，您还可以为各个按需表和全局二级索引配置每秒最大读取和/或写入吞吐量，来协助限制成本和用量。有关更多信息，请参阅[按需表的最大吞吐量](on-demand-capacity-mode-max-throughput.md)。

## 何时选择预置容量模式
<a name="CostOptimization_TableCapacityMode_Provisioned"></a>

如下图所示，预置容量模式的理想工作负载是具有更稳定和更可预测的使用模式的工作负载。

**注意**  
建议在对预置容量采取行动之前，在精细的时段（例如 14 天）查看指标。

以下因素会导致此类工作负载：
+ 给定时段或一天内稳定、可预测和周期性流量
+ 短期内流量爆发有限

![\[该图描绘了可预测的周期性工作负载，且流量峰值受限。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/choose-provisioned-1.png)


由于给定时段或一天内的流量更加稳定，因此，您可以将表的预置容量设置为相对接近于表的实际使用容量。预置容量表的成本优化过程归根到底是一个练习的过程，即在不增加表上 `ThrottledRequests` 的情况下，让预置容量（蓝线）尽可能靠近使用容量（橙线）。两条线之间的空间既是容量浪费，又是避免节流导致用户体验欠佳的保障。如果您可以预测应用程序的吞吐量需求，并且更喜欢成本可预测性（控制读取和写入容量），那么您可能需要继续使用预置表。

DynamoDB 为预置容量表提供自动扩缩功能，此功能会代表您自动进行平衡。这样，您便可以跟踪全天使用的容量，并根据一些变量来设置表的容量。使用自动扩缩时，表将被过度预置，您需要微调限制数量与过度预置的容量单位之间的比率，以满足工作负载需要。

![\[DynamoDB 控制台。预置容量，且已启用自动扩缩。目标利用率设置为 70。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/TableCapacityModeAutoScaling.png)


**最小容量单位**  
您可以设置表的最小容量来限制节流的情况，但这不会降低表的成本。如果您的表具有低使用量时段，然后突然出现高使用量，则设置最小值可以防止自动扩缩操作将表容量设置得过低。

**最大容量单位**  
您可以设置表的最大容量，以限制将表扩大到超出预期值。对于无需大规模负载测试的开发或测试表，请考虑应用最大值。您可以为任意表设置最大值，但是在生产中使用该表时，请务必定期根据表的使用基准评估此设置，以防止意外节流。

**目标利用率**  
对于预置容量表，设置表的目标利用率是优化其成本的主要方法。将此值设置为较低的百分比会增加过度预置的表容量，进而增加成本，但可以降低出现节流的风险。将此值设置为较高的百分比会减少过度预置的表容量，但会增加出现节流的风险。

## 选择表容量模式时需要考虑的其他因素
<a name="CostOptimization_TableCapacityMode_AdditionalFactors"></a>

在两种模式之间做出选择时，还需要考虑另外一些因素。

**预置容量利用率**  
要确定按需模式的成本何时会低于预置容量，查看预置容量利用率会有所帮助，这是指所分配（或“预置”）的资源的使用效率。对于平均预置容量利用率低于 35% 的工作负载，按需模式的成本更低。在许多情况下，即使对于预置容量利用率高于 35% 的工作负载，使用按需模式也可能更具成本效益，尤其是在工作负载处于低活动期但偶尔会出现峰值的情况下。

**预留容量**  
对于预置容量表，DynamoDB 允许您为读取和写入容量购买预留容量（复制写入容量单位 (rWCU, Replicated Write Capacity Unit) 和标准-IA 表目前不符合条件）。与标准预置容量定价相比，预留容量可享受大幅折扣。

 在两种表模式之间做出选择时，请考虑这种额外折扣对表的成本有多大的影响。在某些情况下，在过度预置的预置容量表上运行相对不可预测的工作负载时，采用预留容量可以实现更低的成本。

**提高工作负载的可预测性**  
在某些情况下，工作负载可能会同时具有可预测和不可预测的模式。虽然按需表可以轻松支持这种模式，但如果能够改善工作负载中不可预测的模式，则可能会获得更好的成本。

造成这些模式的最常见原因之一是批量导入。这种类型的流量通常会超过表的基准容量，以至于在运行此操作时表会出现节流。要在预置容量表上确保此类工作负载的运行，请考虑以下选项：
+ 如果批处理按照预定时间进行，则可以在运行之前，计划增加自动扩缩容量。
+ 如果批处理随机进行，请考虑尝试延长运行时间，而不是尽快执行
+ 为导入操作添加一个加速期，在此期间，开始时导入的速度较小，但会在几分钟内缓慢增加，直到自动扩缩功能有机会开始调整表容量。

# 评估 DynamoDB 表的自动扩缩设置
<a name="CostOptimization_AutoScalingSettings"></a>

本节概述如何评估 DynamoDB 表上的自动扩缩设置。[Amazon DynamoDB Auto Scaling](AutoScaling.md) 是根据您的应用程序流量和目标利用率指标，来管理表和全局二级索引 (GSI) 吞吐量的一项功能。这将确保您的表或 GSI 具有应用程序模式所需的容量。

Amazon Auto Scaling 服务将监控您当前的表利用率，并与目标利用率值作比较：`TargetValue`。当需要增加或减少分配的容量时，它会通知您。

**Topics**
+ [了解您的自动扩缩设置](#CostOptimization_AutoScalingSettings_UnderProvisionedTables)
+ [如何识别具有低目标利用率 (<=50%) 的表](#CostOptimization_AutoScalingSettings_IdentifyLowUtilization)
+ [如何处理具有季节性差异的工作负载](#CostOptimization_AutoScalingSettings_SeasonalVariance)
+ [如何处理具有未知模式的尖峰工作负载](#CostOptimization_AutoScalingSettings_UnknownPatterns)
+ [如何处理具有关联应用程序的工作负载](#CostOptimization_AutoScalingSettings_BetweenRanges)

## 了解您的自动扩缩设置
<a name="CostOptimization_AutoScalingSettings_UnderProvisionedTables"></a>

为目标利用率、初始步骤和最终值定义正确的值是一项需要运营团队参与的活动。这允许您根据应用程序的使用历史来适当地定义值，以便触发 Amazon 自动扩缩策略。利用率目标是总容量的百分比值，需要在一段时间内达到后，才会应用自动扩缩规则。

当您设置**高利用率目标（大约 90%）**时，意味着流量在一段时间内高于 90% 后才会触发自动扩缩策略。除非您的应用程序非常稳定且不会收到高峰流量，否则不应使用高利用率目标。

当您设置非常**低的利用率目标（低于 50%）**时，意味着您的应用程序需要达到预置容量的 50% 后才会触发自动扩缩策略。除非您的应用程序流量以非常激进的速度增长，否则这通常会造成未用的容量和浪费的资源。

## 如何识别具有低目标利用率 (<=50%) 的表
<a name="CostOptimization_AutoScalingSettings_IdentifyLowUtilization"></a>

您可以使用 Amazon CLI 或 Amazon Web Services 管理控制台 来监控和识别 DynamoDB 资源中有关自动扩缩策略的 `TargetValues`：

------
#### [ Amazon CLI ]

1. 通过运行以下命令返回整个资源列表：

   ```
   aws application-autoscaling describe-scaling-policies --service-namespace dynamodb
   ```

   此命令将返回发布给任何 DynamoDB 资源的自动扩缩策略的完整列表。如果您只想检索来自特定表的资源，则可以添加 `–resource-id parameter`。例如：

   ```
   aws application-autoscaling describe-scaling-policies --service-namespace dynamodb --resource-id "table/<table-name>”
   ```

1. 通过运行以下命令仅返回特定 GSI 的自动扩缩策略

   ```
   aws application-autoscaling describe-scaling-policies --service-namespace dynamodb --resource-id "table/<table-name>/index/<gsi-name>”
   ```

   我们感兴趣的自动扩缩策略的值在下面突出显示。我们希望确保目标值大于 50%，以避免过度配置。您应该得到类似如下的结果：

   ```
   {
       "ScalingPolicies": [
           {
               "PolicyARN": "arn:aws:autoscaling:<region>:<account-id>:scalingPolicy:<uuid>:resource/dynamodb/table/<table-name>/index/<index-name>:policyName/$<full-gsi-name>-scaling-policy",
               "PolicyName": $<full-gsi-name>”,
               "ServiceNamespace": "dynamodb",
               "ResourceId": "table/<table-name>/index/<index-name>",
               "ScalableDimension": "dynamodb:index:WriteCapacityUnits",
               "PolicyType": "TargetTrackingScaling",
               "TargetTrackingScalingPolicyConfiguration": {
                   "TargetValue": 70.0,
                   "PredefinedMetricSpecification": {
                       "PredefinedMetricType": "DynamoDBWriteCapacityUtilization"
                   }
               },
               "Alarms": [
                   ...
               ],
               "CreationTime": "2022-03-04T16:23:48.641000+10:00"
           },
           {
               "PolicyARN": "arn:aws:autoscaling:<region>:<account-id>:scalingPolicy:<uuid>:resource/dynamodb/table/<table-name>/index/<index-name>:policyName/$<full-gsi-name>-scaling-policy",
               "PolicyName":$<full-gsi-name>”,
               "ServiceNamespace": "dynamodb",
               "ResourceId": "table/<table-name>/index/<index-name>",
               "ScalableDimension": "dynamodb:index:ReadCapacityUnits",
               "PolicyType": "TargetTrackingScaling",
               "TargetTrackingScalingPolicyConfiguration": {
                   "TargetValue": 70.0,
                   "PredefinedMetricSpecification": {
                       "PredefinedMetricType": "DynamoDBReadCapacityUtilization"
                   }
               },
               "Alarms": [
                   ...
               ],
               "CreationTime": "2022-03-04T16:23:47.820000+10:00"
           }
       ]
   }
   ```

------
#### [ Amazon Web Services 管理控制台 ]

1. 登录 Amazon Web Services 管理控制台，并打开 DynamoDB 控制台：[https://console.aws.amazon.com/dynamodb/](https://console.amazonaws.cn/dynamodb/)。

   根据需要选择合适的 Amazon Web Services 区域。

1. 在左侧导航栏上，选择**表**。在**表**页面上，选择表的**名称**。

1. 在*表详细信息*页面上，选择**其他设置**，然后查看表的自动扩缩设置。  
![\[包含自动扩缩设置的 DynamoDB 表详细信息页面。查看预调配的容量利用率并根据需要进行调整。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/AutoScalingSettings1.png)

   对于索引，请展开**索引容量**部分，查看索引的自动扩缩设置。  
![\[DynamoDB 控制台的索引容量部分。查看和管理索引的自动扩缩设置。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/AutoScalingSettings2.png)

------

如果您的目标利用率值小于或等于 50%，则应浏览您的表利用率指标，看看这些指标是[配置不足还是过度配置](CostOptimization_RightSizedProvisioning.md)。

## 如何处理具有季节性差异的工作负载
<a name="CostOptimization_AutoScalingSettings_SeasonalVariance"></a>

考虑以下场景：您的应用程序大部分时间都在最低平均值下运行，但是利用率目标较低，所以您的应用程序可以对一天中特定时间发生的事件做出快速反应，并且您有足够的容量来避免节流。当您的应用程序在正常办公时间（上午 9 点到下午 5 点）非常繁忙，但在下班后处于基本工作水平时，这种场景很常见。因为一些用户会在上午 9 点前开始连接，所以应用程序使用这个低阈值来实现快速爬坡，以便在高峰时段达到*所需*容量。

此场景可能是这样的：
+ 下午 5 点到上午 9 点之间，`ConsumedWriteCapacity` 单位保持在 90 到 100 之间
+ 用户在上午 9 点之前开始连接到应用程序，容量单位显著增加（您看到的最大值为 1500WCU）
+ 平均下来，您的应用程序在工作时间内的使用量在 800 到 1200 之间变化

如果前面的场景适用于您，可考虑使用[定时自动扩缩](https://docs.amazonaws.cn/autoscaling/application/userguide/examples-scheduled-actions.html)，在这种情况下，您的表仍可以配置应用程序自动扩缩规则，但目标利用率不那么激进，只是在您需要的特定间隔预置了额外容量。

您可以使用 Amazon CLI 执行以下步骤，创建基于一天中的某个时间和一周中的星期几来执行的定时自动扩缩规则，

1. 在 Application Auto Scaling 中将您的 DynamoDB 表或 GSI 注册为可扩展目标。可扩展目标是 Application Auto Scaling 可以扩大或缩小的资源。

   ```
   aws application-autoscaling register-scalable-target \
       --service-namespace dynamodb \
       --scalable-dimension dynamodb:table:WriteCapacityUnits \
       --resource-id table/<table-name> \
       --min-capacity 90 \
       --max-capacity 1500
   ```

1. 根据您的要求设置预定操作。

   我们需要两条规则来覆盖此场景：一条规则用来扩大，一条规则用来缩小。第一条规则扩大预定操作：

   ```
   aws application-autoscaling put-scheduled-action \
       --service-namespace dynamodb \
       --scalable-dimension dynamodb:table:WriteCapacityUnits \
       --resource-id table/<table-name> \
       --scheduled-action-name my-8-5-scheduled-action \
       --scalable-target-action MinCapacity=800,MaxCapacity=1500 \
       --schedule "cron(45 8 ? * MON-FRI *)" \
       --timezone "Australia/Brisbane"
   ```

   第二条规则缩小预定操作：

   ```
   aws application-autoscaling put-scheduled-action \
       --service-namespace dynamodb \
       --scalable-dimension dynamodb:table:WriteCapacityUnits \
       --resource-id table/<table-name> \
       --scheduled-action-name my-5-8-scheduled-down-action \
       --scalable-target-action MinCapacity=90,MaxCapacity=1500 \
       --schedule "cron(15 17 ? * MON-FRI *)" \
       --timezone "Australia/Brisbane"
   ```

1. 运行以下命令来验证两条规则是否已激活：

   ```
   aws application-autoscaling describe-scheduled-actions --service-namespace dynamodb
   ```

   应得到类似如下的结果：

   ```
   {
       "ScheduledActions": [
           {
               "ScheduledActionName": "my-5-8-scheduled-down-action",
               "ScheduledActionARN": "arn:aws:autoscaling:<region>:<account>:scheduledAction:<uuid>:resource/dynamodb/table/<table-name>:scheduledActionName/my-5-8-scheduled-down-action",
               "ServiceNamespace": "dynamodb",
               "Schedule": "cron(15 17 ? * MON-FRI *)",
               "Timezone": "Australia/Brisbane",
               "ResourceId": "table/<table-name>",
               "ScalableDimension": "dynamodb:table:WriteCapacityUnits",
               "ScalableTargetAction": {
                   "MinCapacity": 90,
                   "MaxCapacity": 1500
               },
               "CreationTime": "2022-03-15T17:30:25.100000+10:00"
           },
           {
               "ScheduledActionName": "my-8-5-scheduled-action",
               "ScheduledActionARN": "arn:aws:autoscaling:<region>:<account>:scheduledAction:<uuid>:resource/dynamodb/table/<table-name>:scheduledActionName/my-8-5-scheduled-action",
               "ServiceNamespace": "dynamodb",
               "Schedule": "cron(45 8 ? * MON-FRI *)",
               "Timezone": "Australia/Brisbane",
               "ResourceId": "table/<table-name>",
               "ScalableDimension": "dynamodb:table:WriteCapacityUnits",
               "ScalableTargetAction": {
                   "MinCapacity": 800,
                   "MaxCapacity": 1500
               },
               "CreationTime": "2022-03-15T17:28:57.816000+10:00"
           }
       ]
   }
   ```

下图显示了一个始终保持 70% 目标利用率的示例工作负载。请注意，自动扩缩规则仍然适用，并且吞吐量不会减少。

![\[即使自动扩缩规则会调整容量，但表的吞吐量的目标利用率仍保持在 70%。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/AutoScalingSettings3.png)


放大图片，我们可以看到，应用程序中有一个高峰，它触发了 70% 自动扩缩阈值，迫使自动扩缩启动，来为表提供所需的额外容量。预定的自动扩缩操作将影响最大值和最小值，设置这些值是您的责任。

![\[DynamoDB 表的吞吐量激增，启动自动扩缩可提供所需的额外容量。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/AutoScalingSettings4.png)


![\[DynamoDB 表的自动扩缩配置：目标利用率以及最小和最大容量值。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/AutoScalingSettings5.png)


## 如何处理具有未知模式的尖峰工作负载
<a name="CostOptimization_AutoScalingSettings_UnknownPatterns"></a>

在这种场景中，应用程序使用非常低的利用率目标，因为您尚不清楚应用程序的模式，而您需要确保工作负载不被节流。

可考虑改用[按需容量模式](capacity-mode.md#capacity-mode-on-demand)。按需模式表非常适合您不知道流量模式的尖峰工作负载。在按需容量模式下，您按请求为应用程序在表上执行的数据读取和写入付费。您无需指定应用程序预计将执行多少读写吞吐量，因为 DynamoDB 会根据工作负载的增减来即时做出调整。

## 如何处理具有关联应用程序的工作负载
<a name="CostOptimization_AutoScalingSettings_BetweenRanges"></a>

在这种场景中，应用程序依赖其他系统，例如在批处理场景中，根据应用程序逻辑中的事件，您会遇到非常大的流量尖峰。

可考虑开发自定义自动扩缩逻辑，以便对那些您可以根据自己的具体需求增加表容量和 `TargetValues` 的事件作出反应。您可以受益于 Amazon EventBridge，并使用像 Lambda 和 Step 函数这样的 Amazon 服务组合来对特定应用程序需求作出反应。

# 评估您的 DynamoDB 表类选择
<a name="CostOptimization_TableClass"></a>

本部分概述如何为 DynamoDB 表选择合适的表类。随着标准 - 不频繁访问（标准-IA）表类的推出，您现在可以优化表以降低存储成本或降低吞吐量成本。

**Topics**
+ [有哪些表类可用](#CostOptimization_TableClass_Overview)
+ [何时选择 DynamoDB Standard 表类](#CostOptimization_TableClass_Standard)
+ [何时选择 DynamoDB Standard-IA 表类](#CostOptimization_TableClass_StandardIA)
+ [选择表类时需要考虑的其他因素](#CostOptimization_TableClass_AdditionalFactors)

## 有哪些表类可用
<a name="CostOptimization_TableClass_Overview"></a>

创建 DynamoDB 表时，必须为表类选择 DynamoDB 标准或 DynamoDB 标准-IA。表类在 30 天的时段内可以更改两次，因此您以后可以随时对其进行更改。选择任何一个表类都不会影响表的性能、可用性、可靠性或持久性。

![\[DynamoDB 表类选项。在此图中，选择了 DynamoDB 标准 – IA 表类。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/TableClassOptions.png)


**标准表类**  
标准表类是新建表时的默认选项。此选项保留了 DynamoDB 的原始计费平衡，为包含经常访问数据的表提供了平衡的吞吐量和存储成本。

**标准-IA 表类**  
对于需要长期存储不经常更新或读取数据的工作负载，标准-IA 表类可提供更低的存储成本（降低约 60%）。由于该类针对不频繁访问进行了优化，因此读取和写入的计费成本将略高于标准表类（大约高出 25%）。

## 何时选择 DynamoDB Standard 表类
<a name="CostOptimization_TableClass_Standard"></a>

DynamoDB 标准表类最适合存储成本约占表每月总成本 50% 或以下的表。此成本平衡表明工作负载频繁访问或更新的项目已存储在 DynamoDB 中。

## 何时选择 DynamoDB Standard-IA 表类
<a name="CostOptimization_TableClass_StandardIA"></a>

DynamoDB 标准-IA 表类最适合存储成本约占表每月总成本 50% 或以上的表。此成本平衡表明工作负载每月创建或读取的项目少于保存在存储中的项目。

 标准-IA 表类的一个常见用途是将访问频率较低的数据移动到单个标准-IA 表中。有关更多信息，请参阅[使用 Amazon DynamoDB 标准-IA 表类优化工作负载的存储成本](https://www.amazonaws.cn/blogs/database/optimize-the-storage-costs-of-your-workloads-with-amazon-dynamodb-standard-ia-table-class/)。

## 选择表类时需要考虑的其他因素
<a name="CostOptimization_TableClass_AdditionalFactors"></a>

在两个表类之间做出选择时，还需要考虑另外一些因素。

**预留容量**  
目前不支持为使用标准-IA 表类的表购买预留容量。从具有预留容量的标准表转向没有预留容量的标准-IA 表时，您可能看不到成本优势。

# 确定 DynamoDB 中未使用的资源
<a name="CostOptimization_UnusedResources"></a>

本部分概述如何定期评估未使用资源。随着应用程序需求的演变，您应确保没有未使用的资源，并且不会导致不必要的 Amazon DynamoDB 成本。下面介绍的过程将使用 Amazon CloudWatch 指标来确定未使用的资源，帮助您识别这些资源并对其采取措施以降低成本。

您可以使用 CloudWatch 监控 DynamoDB，此工具可以从 DynamoDB 收集原始数据，处理为可读取的近实时指标。这些统计数据会保留一段时间，这样您便可以访问历史信息，更好地了解使用情况。默认情况下，DynamoDB 指标数据自动发送到 CloudWatch。有关更多信息，请参阅《Amazon CloudWatch 用户指南》**中的[什么是 Amazon CloudWatch？](https://docs.amazonaws.cn/AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html)和[指标保留](https://docs.amazonaws.cn/AmazonCloudWatch/latest/monitoring/cloudwatch_concepts.html#metrics-retention)。

**Topics**
+ [如何确定未使用的资源](#CostOptimization_UnusedResources_Identifying)
+ [确定未使用的表资源](#CostOptimization_UnusedResources_Tables)
+ [清理未使用的表资源](#CostOptimization_UnusedResources_Tables_Cleanup)
+ [确定未使用的 GSI 资源](#CostOptimization_UnusedResources_GSI)
+ [清理未使用的 GSI 资源](#CostOptimization_UnusedResources_GSI_Cleanup)
+ [清理未使用的全局表](#CostOptimization_UnusedResources_GlobalTables)
+ [清理未使用的备份或时间点故障恢复（PITR）](#CostOptimization_UnusedResources_Backups)

## 如何确定未使用的资源
<a name="CostOptimization_UnusedResources_Identifying"></a>

为了确定未使用的表或索引，我们将查看以下 30 天内的 CloudWatch 指标，以了解是否对表进行了任何有效的读取或写入，或者全局二级索引 (GSI, Global Secondary Index) 上是否有任何读取操作：

**[已使用读取容量单位](metrics-dimensions.md#ConsumedReadCapacityUnits)**  
在指定时间段内使用的读取容量单位数，这样便可以跟踪您在已占用容量中使用的容量。可以检索表及其所有全局二级索引或特定全局二级索引占用的总读取容量。

**[已使用写入容量单位](metrics-dimensions.md#ConsumedWriteCapacityUnits)**  
在指定时间段内使用的写入容量单位数，这样便可以跟踪您在已占用容量中使用的容量。可以检索表及其所有全局二级索引或特定全局二级索引占用的总写入容量。

## 确定未使用的表资源
<a name="CostOptimization_UnusedResources_Tables"></a>

Amazon CloudWatch 是一项监控和可观察性服务，提供可用于确定未使用资源的 DynamoDB 表指标。CloudWatch 指标可以通过 Amazon Web Services 管理控制台 以及 Amazon Command Line Interface 查看。

------
#### [ Amazon Command Line Interface ]

要通过 Amazon Command Line Interface 查看表中的指标，可以使用以下命令。

1. 首先，评估您的表的读取数：

   ```
   aws cloudwatch get-metric-statistics --metric-name
   ConsumedReadCapacityUnits --start-time <start-time> --end-time <end-
   time> --period <period> --namespace AWS/DynamoDB --statistics Sum --
   dimensions Name=TableName,Value=<table-name>
   ```

   为了避免错误地将表标识为未使用，请评估较长时段内的指标。选择合适的开始时间和结束时间范围（例如 **30 天**）以及合适的时间段（例如 **86400**）。

   在返回的数据中，任何大于 **0** 的 **Sum**（合计）都表示您所评估的表在该时间段内收到了读取流量。

   以下结果显示表在评估时段内收到了读取流量：

   ```
           {
               "Timestamp": "2022-08-25T19:40:00Z",
               "Sum": 36023355.0,
               "Unit": "Count"
           },
           {
               "Timestamp": "2022-08-12T19:40:00Z",
               "Sum": 38025777.5,
               "Unit": "Count"
           },
   ```

   以下结果显示表在评估时段内未收到读取流量：

   ```
           {
               "Timestamp": "2022-08-01T19:50:00Z",
               "Sum": 0.0,
               "Unit": "Count"
           },
           {
               "Timestamp": "2022-08-20T19:50:00Z",
               "Sum": 0.0,
               "Unit": "Count"
           },
   ```

1. 接下来，评估表的写入数量：

   ```
   aws cloudwatch get-metric-statistics --metric-name
   ConsumedWriteCapacityUnits --start-time <start-time> --end-time <end-
   time> --period <period> --namespace AWS/DynamoDB --statistics Sum --
   dimensions Name=TableName,Value=<table-name>
   ```

   为了避免错误地将表标识为未使用，您需要评估较长时段内的指标。选择合适的开始时间和结束时间范围（例如 **30 天**）以及合适的时间段，例如 **86400**。

   在返回的数据中，任何大于 **0** 的 **Sum**（合计）都表示您所评估的表在该时间段内收到了读取流量。

   以下结果显示表在评估时段内收到了写入流量：

   ```
           {
               "Timestamp": "2022-08-19T20:15:00Z",
               "Sum": 41014457.0,
               "Unit": "Count"
           },
           {
               "Timestamp": "2022-08-18T20:15:00Z",
               "Sum": 40048531.0,
               "Unit": "Count"
           },
   ```

   以下结果显示表在评估时段内未收到写入流量：

   ```
           {
               "Timestamp": "2022-07-31T20:15:00Z",
               "Sum": 0.0,
               "Unit": "Count"
           },
           {
               "Timestamp": "2022-08-19T20:15:00Z",
               "Sum": 0.0,
               "Unit": "Count"
           },
   ```

------
#### [ Amazon Web Services 管理控制台 ]

利用以下步骤，您可以通过 Amazon Web Services 管理控制台 评估资源利用率。

1. 登录 Amazon 控制台并导航到 CloudWatch 服务页面，网址为 [https://console.aws.amazon.com/cloudwatch/](https://console.amazonaws.cn/cloudwatch/)。如有必要，请在控制台右上角选择相应的 Amazon 区域。

1. 在左侧导航栏中，找到“指标”部分，然后选择**全部指标**。

1. 以上操作将打开一个控制面板，其中包含两个面板。在顶部面板中，您将看到以图表表示的当前指标。在底部，您可选择用于绘制图表的指标。在底部面板中选择 DynamoDB。

1. 在 DynamoDB 指标选择面板中，选择**表指标**类别以显示当前区域中表的指标。

1. 向下滚动菜单来标识您的表名，然后选择表的指标 `ConsumedReadCapacityUnits` 和 `ConsumedWriteCapacityUnits`。

1. 选择**绘成图表的指标(2)** 选项卡，然后将**统计信息**列调整为**总计**。  
![\[绘成图表的指标选项卡。统计信息设置为总计，以便在控制台中查看资源使用情况数据。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/GraphedMetricsTab.png)

1. 为了避免错误地将表标识为未使用，您需要评估较长时段内的指标。在图形面板顶部选择相应的时间范围来评估表，例如 1 个月。选择**自定义**，然后在下拉菜单中选择 **1 个月**并选择**应用**。  
![\[CloudWatch 控制台。选择了 1 个月的自定义时间范围来评估指标。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/OneMonthTimeFrame.png)

1. 评估您的表的绘成图表的指标，以确定是否使用了该表。大于 **0** 的指标表示在评估时间段内使用了表。读取和写入均为 **0** 的空白图指示未使用表。

   下图显示了具有读取流量的表：  
![\[该图显示了 DynamoDB 表的 ConsumedReadCapacityUnits，表明该表正在使用中。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/TableWithReadTraffic.png)

   下图显示了没有读取流量的表：  
![\[该图显示 DynamoDB 表没有读取活动，表明该表未在使用中。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/TableWithoutReadTraffic.png)

------

## 清理未使用的表资源
<a name="CostOptimization_UnusedResources_Tables_Cleanup"></a>

如果您已确定未使用的表资源，则可以通过以下方式降低其持续成本。

**注意**  
如果您已确定未使用的表，但仍希望将其保持可用状态，以防将来需要访问该表，请考虑将其切换到按需模式。否则，您可以考虑备份并彻底删除该表。

**容量模式**  
DynamoDB 对 DynamoDB 表收取读取、写入和存储数据费用。

DynamoDB 具有按需和预置这[两种容量模式](capacity-mode.md)，它们对表上处理的读取和写入采用特定的计费选项。读/写容量模式控制对读写吞吐量收费的方式以及管理容量的方式。

对于按需模式表，您无需指定预期应用程序执行的读写吞吐量。DynamoDB 按照读取请求单位和写入请求单位对应用程序在表上执行的读取和写入操作收费。如果表/索引上没有活动，则您无需为吞吐量付费，但仍会产生存储费用。

**表类**  
DynamoDB 还提供了[两个表类](HowItWorks.TableClasses.md)，旨在帮助您优化成本。“DynamoDB 标准”表类是原定设置，建议用于大多数工作负载。“DynamoDB 标准-不经常访问 (DynamoDB Standard-IA)”表类别针对存储占据主要成本的表进行优化。

如果您的表或索引上没有活动，则主要成本可能是存储成本，更改表类将节省大量费用。

**删除表**  
如果您发现了一个未使用的表并想将其删除，则可能需要先备份或导出数据。

通过 Amazon Backup 执行的备份可以利用冷存储分层，从而进一步降低成本。有关如何启用通过 [将 Amazon Backup 与 DynamoDB 结合使用](backuprestore_HowItWorksAWS.md) Backup 进行备份的信息，请参阅 Amazon 文档；有关如何使用生命周期将备份转移到冷存储的信息，请参阅[管理备份计划](https://docs.amazonaws.cn/aws-backup/latest/devguide/about-backup-plans)文档。

或者，您可以选择将表的数据导出到 S3。为此，请参阅[导出至 Amazon S3](S3DataExport.HowItWorks.md) 文档。导出数据后，如果您希望利用 S3 Glacier Instant Retrieval、S3 Glacier Flexile Retrieval 或 S3 Glacier Deep Archive 进一步降低成本，请参阅[管理存储生命周期](https://docs.amazonaws.cn/AmazonS3/latest/userguide/object-lifecycle-mgmt)。

备份表后，您可以选择通过 Amazon Web Services 管理控制台 或通过 Amazon Command Line Interface 将其删除。

## 确定未使用的 GSI 资源
<a name="CostOptimization_UnusedResources_GSI"></a>

确定未使用的全局二级索引的步骤与确定未使用的表的步骤类似。在写入基表的项目中包含用作 GSI 分区键的属性时，由于 DynamoDB 会将此类项目复制到 GSI，如果基表在使用，则未使用的 GSI 仍有可能具有大于 0 的 `ConsumedWriteCapacityUnits`。因此，您将仅评估 `ConsumedReadCapacityUnits` 指标来确定 GSI 是否未使用。

要通过 Amazon Amazon CLI 查看 GSI 指标，您可以使用以下命令评估表的读取数：

```
aws cloudwatch get-metric-statistics --metric-name
ConsumedReadCapacityUnits --start-time <start-time> --end-time <end-
time> --period <period> --namespace AWS/DynamoDB --statistics Sum --
dimensions Name=TableName,Value=<table-name>
Name=GlobalSecondaryIndexName,Value=<index-name>
```

为了避免错误地将表标识为未使用，您需要评估较长时段内的指标。选择合适的开始时间和结束时间范围（例如 30 天）以及合适的时间段，例如 86400。

在返回的数据中，任何大于 0 的 Sum（合计）都表示您所评估的表在该时间段内收到了读取流量。

以下结果显示 GSI 在评估时段内收到了读取流量：

```
        {
          "Timestamp": "2022-08-17T21:20:00Z",
          "Sum": 36319167.0,
          "Unit": "Count"
        },
        {
          "Timestamp": "2022-08-11T21:20:00Z",
          "Sum": 1869136.0,
          "Unit": "Count"
        },
```

以下结果显示 GSI 在评估时段内收到了极少的读取流量：

```
        {
          "Timestamp": "2022-08-28T21:20:00Z",
          "Sum": 0.0,
          "Unit": "Count"
        },
        {
          "Timestamp": "2022-08-15T21:20:00Z",
          "Sum": 2.0,
          "Unit": "Count"
        },
```

以下结果显示 GSI 在评估时段内未收到读取流量：

```
        {
          "Timestamp": "2022-08-17T21:20:00Z",
          "Sum": 0.0,
          "Unit": "Count"
        },
        {
          "Timestamp": "2022-08-11T21:20:00Z",
          "Sum": 0.0,
          "Unit": "Count"
        },
```

## 清理未使用的 GSI 资源
<a name="CostOptimization_UnusedResources_GSI_Cleanup"></a>

如果您已确定未使用的 GSI，则可以选择将其删除。由于 GSI 中存在的所有数据也存在于基表中，因此在删除 GSI 之前无需进行额外备份。如果以后会再次需要 GSI，则可以将其重新添加到表中。

如果您发现了不常使用的 GSI，则应考虑更改应用程序中的设计，以便将其删除或减少其成本。例如，虽然 DynamoDB 扫描会消耗大量系统资源而应谨慎使用，但如果很少使用其支持的访问模式，则它们可能比 GSI 更具成本效益。

此外，如果需要 GSI 来支持不频繁的访问模式，可以考虑规划一组更有限的属性。尽管后续可能需要对基表进行查询以支持不频繁的访问模式，但它有可能显著降低存储和写入成本。

## 清理未使用的全局表
<a name="CostOptimization_UnusedResources_GlobalTables"></a>

Amazon DynamoDB 全局表 为部署多区域、多活跃数据库提供了完全托管式解决方案，而不必构建和维护您自己的复制解决方案。

全局表非常适合在靠近用户的位置提供对数据的低延迟访问，也可以用作灾难恢复的辅助区域。

如果为某个资源启用了全局表选项以提供低延迟的数据访问，但该选项并未包括在灾难恢复策略中，请通过评估副本的 CloudWatch 指标来验证这两个副本是否正在主动处理读取流量。如果一个副本没有处理读取流量，则它可能是未使用的资源。

如果全局表包括在灾难恢复策略中，则在主动/备用模式下，预计会有一个副本不会收到读取流量。

## 清理未使用的备份或时间点故障恢复（PITR）
<a name="CostOptimization_UnusedResources_Backups"></a>

DynamoDB 提供两种备份方式。时间点故障恢复提供最多 35 天的连续备份，这样您就防止意外的写入或删除，同时利用按需备份就能够创建可长期保存的快照。您可以将恢复期设置为 1 天到 35 天之间的任何值。这两种类型的备份都有相关的成本。

请参阅 [DynamoDB 的备份和还原](Backup-and-Restore.md) 和 [DynamoDB 的时间点备份](Point-in-time-recovery.md) 文档，以确定您的表是否启用了可能不再需要的备份。

# 评估您的 DynamoDB 表使用模式
<a name="CostOptimization_TableUsagePatterns"></a>

本节概述如何评估您使用 DynamoDB 表是否高效。有些使用模式对于 DynamoDB 来说不是最佳的，在性能和成本方面还有优化的空间。

**Topics**
+ [执行更少的强一致性读取操作](#CostOptimization_TableUsagePatterns_StronglyConsistentReads)
+ [执行更少的读取操作事务](#CostOptimization_TableUsagePatterns_Transactions)
+ [执行更少的扫描](#CostOptimization_TableUsagePatterns_Scans)
+ [缩短属性名称](#CostOptimization_TableUsagePatterns_AttributeNames)
+ [启用生存时间（TTL）](#CostOptimization_TableUsagePatterns_TTL)
+ [用跨区域备份替换全局表](#CostOptimization_TableUsagePatterns_GlobalTables)

## 执行更少的强一致性读取操作
<a name="CostOptimization_TableUsagePatterns_StronglyConsistentReads"></a>

DynamoDB 允许您根据每个请求来配置[读取一致性](HowItWorks.ReadConsistency.md)。默认情况下，读取请求为最终一致性的。最终一致性读取的收费标准是 4KB 及以下数据量为 0.5RCU。

大多数分布式工作负载具有灵活性，能够容许最终一致性。但是，也有一些访问模式需要强一致性读取。强一致性读取的收费标准是 4KB 及以下数据量为 1RCU，读取成本基本上翻了一倍。DynamoDB 提供了在同一个表上使用这两种一致性模型的灵活性。

您可以评估一下您的工作负载和应用程序代码，确认是否只在需要时才使用强一致性读取。

## 执行更少的读取操作事务
<a name="CostOptimization_TableUsagePatterns_Transactions"></a>

DynamoDB 允许您采用“全或无”的方式对某些操作进行分组，这意味着您可以使用 DynamoDB 执行 ACID 事务。但是，如同在关系数据库中一样，不必让每个操作都遵循此方法。

一个 4KB 及以下数据量的[事务型读取操作](transaction-apis.md#transaction-capacity-handling.title)会占用 2RCU，而以最终一致性方式读取同样数量的数据会占用默认的 0.5RCU。写入操作的成本是翻倍的，这意味着，1KB 及以下数量的事务型写入将占用 2WCU。

要判断表上的所有操作是否都是事务，可以筛选表的 CloudWatch 指标，直到只剩下事务 API。如果表的 `SuccessfulRequestLatency` 指标下只剩下事务 API 图，则可以确认，每个操作就是该表的一个事务。或者，如果整体容量利用率趋势与事务 API 趋势一致，请考虑重新审视应用程序设计，因为它似乎被事务 API 所主导。

## 执行更少的扫描
<a name="CostOptimization_TableUsagePatterns_Scans"></a>

`Scan` 操作的广泛使用通常源于对 DynamoDB 数据运行分析查询的需求。在大型表上频繁运行 `Scan` 操作既效率低下又成本高昂。

一种更好的替代方法是使用[导出到 S3](S3DataExport.HowItWorks.md#S3DataExport.HowItWorks.title) 功能并选择将表状态导出到 S3 的一个时间点。Amazon 提供了类似 Athena 这样的服务，可以在不占用任何表容量的情况下对数据运行分析查询。

`Scan` 操作的频率可以使用 `Scan` 的 `SuccessfulRequestLatency` 指标下的 `SampleCount` 统计数据来确定。如果 `Scan` 操作确实非常频繁，则应重新评估访问模式和数据模型。

## 缩短属性名称
<a name="CostOptimization_TableUsagePatterns_AttributeNames"></a>

在 DynamoDB 中，一个项目的总大小是其属性名称长度加上值的总和。长的属性名称不仅会增加存储成本，还可能导致 RCU/WCU 占用量增加。建议您选择较短的属性名，而不要选择较长的属性名。短的属性名有助于将项目大小限制在下一个 4KB/1KB 范围内，避免占用额外的 RCU/WCU 来访问数据。

## 启用生存时间（TTL）
<a name="CostOptimization_TableUsagePatterns_TTL"></a>

[生存时间（TTL）](TTL.md#TTL.title)可以发现超过设定的过期时间的项目，并将它们从表中删除。如果您的数据随着时间的推移而增长，并且较旧的数据变得无关紧要，则在表上启用 TTL 可以帮助减少数据并节省存储成本。

 TTL 的另一个用处是，当您的 DynamoDB 流上存在过期的项目时，除了删除它们之外，也可以利用它们，并将其存档到一个成本较低的存储层上。此外，通过 TTL 删除项目无需额外成本，既不占用容量，也不产生设计清理应用程序的开销。

## 用跨区域备份替换全局表
<a name="CostOptimization_TableUsagePatterns_GlobalTables"></a>

[全局表](GlobalTables.md#GlobalTables.title)允许您在不同的区域维护多个活动副本表，这些表都可以接受相互间的写入操作和数据复制。设置副本很容易，而且可以替您管理同步。副本使用最后写入器成功策略来聚合为一致状态。

如果您纯粹将全局表作为故障转移或灾难恢复 (DR) 策略的一部分，则可以用一个跨区域备份副本来替换它们，以满足相对宽松的恢复点目标和恢复时间目标要求。如果您不需要快速的本地访问和五个 9 这样的高可用性，则维护全局表副本或许不是故障转移的最佳方法。

作为替代方案，可以考虑使用 Amazon Backup 来管理 DynamoDB 备份。您可以采用一种比使用全局表更具成本效益的方法，来安排定期备份和跨区域复制，以满足灾难恢复要求。

# 评估 DynamoDB 流使用情况
<a name="CostOptimization_StreamsUsage"></a>

本节概述如何评估您的 DynamoDB Streams 使用情况。有些使用模式对于 DynamoDB 来说不是最佳的，在性能和成本方面还有优化的空间。

对于流式传输和事件驱动的使用场景，您有两个原生流式传输集成：
+ [Amazon DynamoDB Streams](Streams.md) 
+ [Amazon Kinesis Data Streams](Streams.KCLAdapter.md) 

本页仅侧重介绍这些选项的成本优化策略。如果您只是想知道如何在这两个选项之间作出选择，请参阅 [更改数据捕获的流式传输选项](streamsmain.md#streamsmain.choose)。

**Topics**
+ [优化 DynamoDB Streams 的成本](#CostOptimization_StreamsUsage_Options_DDBStreams)
+ [优化 Kinesis Data Streams 的成本](#CostOptimization_StreamsUsage_Options_KDS)
+ [两种 Streams 使用模式的成本优化策略](#CostOptimization_StreamsUsage_GuidanceForBoth)

## 优化 DynamoDB Streams 的成本
<a name="CostOptimization_StreamsUsage_Options_DDBStreams"></a>

正如 DynamoDB Streams 的[定价页面](https://www.amazonaws.cn/dynamodb/pricing/on-demand/)所述，无论表的吞吐能力模式，DynamoDB 都基于对表的 DynamoDB Stream 的读取请求量来收费。对 DynamoDB Stream 的读取请求与对 DynamoDB 表的读取请求不同。

对 Stream 的每个读取请求采用的是 `GetRecords` API 调用形式，响应中可以返回最多 1000 条记录或 1MB 的记录，以先到达者为准。[其他 DynamoDB Stream API](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_Operations_Amazon_DynamoDB_Streams.html) 均不收费，也不对闲置的 DynamoDB Streams 收费。换言之，如果不对 DynamoDB Stream 发出读取请求，则表上启用了 DynamoDB Stream 并不会产生任何费用。

以下是 DynamoDB Streams 的一些使用器应用程序：
+ Amazon Lambda 函数
+ 基于 Amazon Kinesis Data Streams 的应用程序
+ 使用 Amazon SDK 构建的客户使用器应用程序

由 DynamoDB Streams 的基于 Amazon Lambda 的使用器发出的读取请求是免费的，而由任何其他类型的使用器发出的调用都是收费的。每个月，由非 Lambda 使用器发出的前 250 万次读取请求也是免费的。这适用于每个 Amazon 区域的一个 Amazon 账户中对于任何 DynamoDB Streams 发出的所有读取请求。

**监控您的 DynamoDB Streams 使用情况**  
在账单控制台上，Amazon 区域内一个 Amazon 账户中的所有 DynamoDB Streams 费用集中组成一组。目前，暂不支持标记 DynamoDB Streams，因此不能使用成本分配标签来进一步细分 DynamoDB Streams 的成本。可以获得 DynamoDB Stream 级别的 `GetRecords` 调用量来计算每个流的费用。该调用量用 DynamoDB Stream 的 CloudWatch 指标 `SuccessfulRequestLatency` 及其 `SampleCount` 统计数据来表示。该指标还将包括全局表为执行持续复制而进行的 `GetRecords` 调用，以及 Amazon Lambda 使用器进行的调用，两者均不收费。有关 DynamoDB Streams 发布的其他 CloudWatch 指标的信息，请参阅 [DynamoDB 指标与维度](metrics-dimensions.md)。

**使用 Amazon Lambda 作为使用器**  
评估使用 Amazon Lambda 函数作为 DynamoDB Streams 的使用器是否可行，因为这可以消除与读取 DynamoDB Stream 相关的成本。另一方面，对于基于 DynamoDB Streams Kinesis Adapter 或 SDK 的使用器应用程序，将根据它们对 DynamoDB Stream 的 `GetRecords` 调用次数来收费。

将根据标准 Lambda 定价对 Lambda 函数调用收费，但是 DynamoDB Streams 不会产生任何费用。Lambda 将以每秒 4 次的基本频率轮询 DynamoDB Stream 中的分片来获取记录。如果记录可用，Lambda 会调用函数并等待结果。如果处理成功，Lambda 会恢复轮询，直到其收到更多记录。

**调整基于 DynamoDB Streams Kinesis Adapter 的使用器应用程序**  
因为对基于非 Lambda 的使用器发出的读取请求要收取 DynamoDB Streams 费用，所以，如何在近实时要求和使用者应用程序必须轮询 DynamoDB Stream 的次数之间找到一个平衡点非常重要。

使用基于 DynamoDB Streams Kinesis Adapter 的应用程序轮询 DynamoDB Streams 的频率由配置的 `idleTimeBetweenReadsInMillis` 值决定。该参数决定了使用器在对某个分片的 `GetRecords` 调用未返回任何记录时，需等待多久（以毫秒为单位）才能再次处理该分片。该参数的默认值为 1000ms。如果不需要近实时处理，则可以提高该参数值，以减少使用器应用程序的 `GetRecords` 调用次数，并对 DynamoDB Streams 调用进行优化。

## 优化 Kinesis Data Streams 的成本
<a name="CostOptimization_StreamsUsage_Options_KDS"></a>

将一个 Kinesis Data Stream 设置为目的地来传送 DynamoDB 表的变化数据捕获事件时，该 Kinesis Data Stream 可能需要单独的规模调整管理，而这将影响总体成本。DynamoDB 费用按变化数据捕获单位 (CDU) 来收取，每个单位由 DynamoDB 服务向目的地 Kinesis Data Stream 尝试传送的一个 DynamoDB 项目（大小最多 1KB）构成。

除了 DynamoDB 服务费用，还将产生标准 Kinesis Data Stream 费用。如[定价页面](https://www.amazonaws.cn/kinesis/data-streams/pricing/)所述，服务定价因容量模式（预置和按需模式）而异，这种容量模式与 DynamoDB 表容量模式不同，它们是由用户定义的。概言之，Kinesis Data Streams 费用是一种小时费率，基于容量模式以及 DynamoDB 服务摄取到流中的数据量。根据用户对 Kinesis Data Stream 的配置，可能会存在其他费用，例如数据检索（对于按需模式）、数据留存时间延长（超过默认的 24 小时）和增强的扇出式使用器检索等。

**监控 Kinesis Data Streams 使用情况**  
除了标准的 Kinesis Data Stream CloudWatch 指标外，Kinesis Data Streams for DynamoDB 还发布了来自 DynamoDB 的指标。DynamoDB 服务的 `Put` 尝试受到 Kinesis 服务节流（因 Kinesis Data Streams 容量不足），或者受到依赖组件（例如可能配置为加密静态 Kinesis Data Stream 数据的 Amazon KMS 服务）节流是有可能的。

要了解 DynamoDB 服务针对 Kinesis Data Stream 发布的 CloudWatch 指标的更多信息，请参阅 [使用 Kinesis Data Streams 监控更改数据捕获](kds_using-shards-and-metrics.md#kds_using-shards-and-metrics.monitoring)。为避免因节流而导致额外的服务重试费用，在预置模式下适当调整 Kinesis Data Stream 的大小非常重要。

**为 Kinesis Data Streams 选择适当的容量模式**  
Kinesis Data Streams 有两种受支持的容量模式 - 预置模式和按需模式。
+ 如果涉及 Kinesis Data Stream 的工作负载具有可预测的应用程序流量、稳定或逐渐增加的流量或者可以准确预测的流量，那么选择 Kinesis Data Streams **预置模式**合适，并且具有较好的成本效益
+ 如果工作负载是新的，具有不可预测的应用程序流量，或者您不想管理容量，那么 Kinesis Data Streams 的**按需模式**合适，并且具有较好的成本效益

优化成本的一种最佳实践是，评估与 Kinesis Data Stream 关联的 DynamoDB 表是否具有可预测的流量模式，且该模式可以利用 Kinesis Data Streams 的预置模式。如果工作负载是新的，则您可以在最初的几周使用 Kinesis Data Streams 的按需模式，查看 CloudWatch 指标以了解流量模式，然后根据工作负载的性质将相同的 Stream 切换到预置模式。在预置模式下，遵循 Kinesis Data Streams 的分片管理注意事项来估算分片数量。

**使用 Kinesis Data Streams for DynamoDB 评估使用器应用程序**  
由于 Kinesis Data Streams 不对像 DynamoDB Streams 这样的 `GetRecords` 调用收费，所以使用器应用程序可以进行任意次调用，但前提是频率低于 `GetRecords` 的节流限制。在 Kinesis Data Streams 的按需模式下，按 GB 量收取数据读取费。对于 Kinesis Data Streams 的预置模式，如果数据存储时间少于 7 天，则不收取读取费用。当 Lambda 函数作为 Kinesis Data Streams 使用器时，Lambda 按每秒一次的基本频率轮询 Kinesis Stream 中的每个分片以获取记录。

## 两种 Streams 使用模式的成本优化策略
<a name="CostOptimization_StreamsUsage_GuidanceForBoth"></a>

**用于 Amazon Lambda 使用器的事件筛选**  
Lambda 事件筛选允许您基于筛选条件丢弃 Lambda 函数调用批处理中的事件。这将优化在使用者函数逻辑中处理或丢弃不需要的流记录的 Lambda 成本。要了解有关配置事件筛选和编写筛选条件的更多信息，请参阅 [Lambda 事件筛选](https://docs.amazonaws.cn/lambda/latest/dg/invocation-eventfiltering.html)。

**调整 Amazon Lambda 使用器**  
可以通过调整 Lambda 配置参数来进一步优化成本，例如提高 `BatchSize` 来增加每次调用的处理量，启用 `BisectBatchOnFunctionError` 来防止处理重复项（这会产生额外成本），以及设置 `MaximumRetryAttempts` 以免过多重试。默认情况下，失败的使用器 Lambda 调用会重试无限次，直到记录在流中过期，对于 DynamoDB Streams，记录的过期时间大约为 24 小时，对于 Kinesis Data Streams，过期时间可以配置为从 24 小时到最长 1 年。有关其他可用的 Lambda 配置选项，包括上面提到的 DynamoDB Stream 使用器的配置选项，请参阅 [Amazon Lambda 开发人员指南](https://docs.amazonaws.cn/lambda/latest/dg/with-ddb.html#services-ddb-params)。

# 评估预置容量以在 DynamoDB 表中预置合适的容量大小
<a name="CostOptimization_RightSizedProvisioning"></a>

本节概述如何评估您的 DynamoDB 表预置大小是否合适。随着工作负载的演变，您应该相应地修改操作程序，尤其是当您的 DynamoDB 表以预置模式配置时，因为它们可能存在过度配置或配置不足的风险。

下述程序需要来自于支持您的生产应用程序的 DynamoDB 表的统计信息。要了解您的应用程序行为，应该定义一个足够长的周期，以捕捉应用程序中的数据季节性特点。例如，如果您的应用程序表现出周模式，则不妨使用三周的周期来分析应用程序吞吐量需求。

如果您不知道从哪里开始，可根据至少一个月的使用数据来进行以下计算。

在评估容量时，DynamoDB 表可以独立配置**读取容量单位 (RCU)** 和**写入容量单位 (WCU)**。如果您的表配置了任何全局二级索引 (GSI)，则需要指定它们将占用的吞吐量，这些吞吐量也将独立于基表的 RCU 和 WCU。

**注意**  
本地二级索引 (LSI) 占用基表的容量。

**Topics**
+ [如何检索 DynamoDB 表上的占用指标](#CostOptimization_RightSizedProvisioning_ConsumptionMetrics)
+ [如何识别配置不足的 DynamoDB 表](#CostOptimization_RightSizedProvisioning_UnderProvisionedTables)
+ [如何识别过度配置的 DynamoDB 表](#CostOptimization_RightSizedProvisioning_OverProvisionedTables)

## 如何检索 DynamoDB 表上的占用指标
<a name="CostOptimization_RightSizedProvisioning_ConsumptionMetrics"></a>

要评估表和 GSI 容量，请监控以下 CloudWatch 指标，然后选择适当的维度来检索表或 GSI 信息：


| 读取容量单位 | 写入容量单位 | 
| --- | --- | 
|  `ConsumedReadCapacityUnits`  |  `ConsumedWriteCapacityUnits`  | 
|  `ProvisionedReadCapacityUnits`  |  `ProvisionedWriteCapacityUnits`  | 
|  `ReadThrottleEvents`  |  `WriteThrottleEvents`  | 

您可以通过 Amazon CLI 或 Amazon Web Services 管理控制台 来执行此操作。

------
#### [ Amazon CLI ]

在检索表占用指标之前，我们需要先使用 CloudWatch API 捕获一些历史数据点。

首先创建两个文件：`write-calc.json` 和 `read-calc.json`。这些文件将代表表或 GSI 的计算。您需要更新一些字段，如下表所示，以匹配您的环境。


| 字段名称 | 定义 | 示例 | 
| --- | --- | --- | 
| <table-name> | 您将分析的表的名称 | SampleTable | 
| <period> | 您将用来评估利用率目标的周期，以秒为单位 | 对于 1 小时的周期，应指定：3600 | 
| <start-time> | 评估间隔的开始时间，以 ISO8601 格式指定 | 2022-02-21T23:00:00 | 
| <end-time> | 评估间隔的结束时间，以 ISO8601 格式指定 | 2022-02-22T06:00:00 | 

写入计算文件将检索指定日期范围内的时间段中所预置和消耗的 WCU 数量。它还将生成用于分析的利用率百分比。`write-calc.json` 文件的完整内容应类似如下：

```
{
  "MetricDataQueries": [
    {
      "Id": "provisionedWCU",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/DynamoDB",
          "MetricName": "ProvisionedWriteCapacityUnits",
          "Dimensions": [
            {
              "Name": "TableName",
              "Value": "<table-name>"
            }
          ]
        },
        "Period": <period>,
        "Stat": "Average"
      },
      "Label": "Provisioned",
      "ReturnData": false
    },
    {
      "Id": "consumedWCU",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/DynamoDB",
          "MetricName": "ConsumedWriteCapacityUnits",
          "Dimensions": [
            {
              "Name": "TableName",
              "Value": "<table-name>""
            }
          ]
        },
        "Period": <period>,
        "Stat": "Sum"
      },
      "Label": "",
      "ReturnData": false
    },
    {
      "Id": "m1",
      "Expression": "consumedWCU/PERIOD(consumedWCU)",
      "Label": "Consumed WCUs",
      "ReturnData": false
    },
    {
      "Id": "utilizationPercentage",
      "Expression": "100*(m1/provisionedWCU)",
      "Label": "Utilization Percentage",
      "ReturnData": true
    }
  ],
  "StartTime": "<start-time>",
  "EndTime": "<ent-time>",
  "ScanBy": "TimestampDescending",
  "MaxDatapoints": 24
}
```

读取计算文件使用类似的文件。此文件将检索指定日期范围内的时间段中预置和消耗了多少 RCU。`read-calc.json` 文件的内容应与以下内容类似：

```
{
  "MetricDataQueries": [
    {
      "Id": "provisionedRCU",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/DynamoDB",
          "MetricName": "ProvisionedReadCapacityUnits",
          "Dimensions": [
            {
              "Name": "TableName",
              "Value": "<table-name>"
            }
          ]
        },
        "Period": <period>,
        "Stat": "Average"
      },
      "Label": "Provisioned",
      "ReturnData": false
    },
    {
      "Id": "consumedRCU",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/DynamoDB",
          "MetricName": "ConsumedReadCapacityUnits",
          "Dimensions": [
            {
              "Name": "TableName",
              "Value": "<table-name>"
            }
          ]
        },
        "Period": <period>,
        "Stat": "Sum"
      },
      "Label": "",
      "ReturnData": false
    },
    {
      "Id": "m1",
      "Expression": "consumedRCU/PERIOD(consumedRCU)",
      "Label": "Consumed RCUs",
      "ReturnData": false
    },
    {
      "Id": "utilizationPercentage",
      "Expression": "100*(m1/provisionedRCU)",
      "Label": "Utilization Percentage",
      "ReturnData": true
    }
  ],
  "StartTime": "<start-time>",
  "EndTime": "<end-time>",
  "ScanBy": "TimestampDescending",
  "MaxDatapoints": 24
}
```

创建文件后，即可以开始检索利用率数据。

1. 要检索写入利用率数据，请发出以下命令：

   ```
   aws cloudwatch get-metric-data --cli-input-json file://write-calc.json
   ```

1. 要检索读取利用率数据，请发出以下命令：

   ```
   aws cloudwatch get-metric-data --cli-input-json file://read-calc.json
   ```

这两个查询的结果都将是一系列 JSON 格式的数据点，将用于分析。您的结果将取决于您指定的数据点数量、周期和您自己的特定工作负载数据。它可能如下所示：

```
{
    "MetricDataResults": [
        {
            "Id": "utilizationPercentage",
            "Label": "Utilization Percentage",
            "Timestamps": [
                "2022-02-22T05:00:00+00:00",
                "2022-02-22T04:00:00+00:00",
                "2022-02-22T03:00:00+00:00",
                "2022-02-22T02:00:00+00:00",
                "2022-02-22T01:00:00+00:00",
                "2022-02-22T00:00:00+00:00",
                "2022-02-21T23:00:00+00:00"
            ],
            "Values": [
                91.55364583333333,
                55.066631944444445,
                2.6114930555555556,
                24.9496875,
                40.94725694444445,
                25.61819444444444,
                0.0
            ],
            "StatusCode": "Complete"
        }
    ],
    "Messages": []
}
```

**注意**  
如果您指定了短周期和长时间范围，则可能需要修改 `MaxDatapoints`，该值在脚本中默认设置为 24。这表示每小时一个数据点，每天 24 个数据点。

------
#### [ Amazon Web Services 管理控制台 ]

1. 登录 Amazon Web Services 管理控制台，并导航到 CloudWatch 服务页面。根据需要选择合适的 Amazon Web Services 区域。

1. 在左侧导航栏中，找到**指标**部分，然后选择**全部指标**。

1. 这将打开一个控制面板，其中包含两个面板。顶部面板显示图形，底部面板显示用于绘图的指标。选择 **DynamoDB**。

1. 选择**表指标**。这将显示您当前所在区域中的表。

1. 使用搜索框搜索您的表名并选择写入操作指标：`ConsumedWriteCapacityUnits` 和 `ProvisionedWriteCapacityUnits`
**注意**  
本例中使用了写入操作指标，但您也可以使用这些步骤绘制读取操作指标图。

1. 选择**绘成图表的指标（2）**选项卡来修改公式。默认情况下，CloudWatch 为图表选择统计函数 **Average**。  
![\[选定的图表指标和 Average 作为默认统计函数。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning1.png)

1. 选中两个图表化指标（左侧的复选框）后，选择菜单**添加数学函数**，然后选择**常用**，再选择 **Percentage** 函数。重复该过程两次。

   第一次选择 **Percentage** 函数：  
![\[CloudWatch 控制台。已为绘成图表的指标选择百分比函数。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning2.png)

   第二次选择 **Percentage** 函数：  
![\[CloudWatch 控制台。再次为绘成图表的指标选择百分比函数。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning3.png)

1. 此时，底部菜单中应该有四个指标。我们来进行 `ConsumedWriteCapacityUnits` 计算。为了保持一致，我们需要使这些名称与在 Amazon CLI 节中使用的匹配。单击 **m1 ID** 并将此值更改为 **consumedWCU**。  
![\[CloudWatch 控制台。ID 为 m1 的绘成图表的指标已重命名为 consumedWCU。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning4.png)

   将 **ConsumedWriteCapacityUnit** 标签重命名为 **consumedWCU**。  
![\[带 ConsumedWriteCapacityUnit 标签的绘成图表的指标已重命名为 consumedWCU。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning5.png)

1. 将统计函数从 **Average** 改为 **Sum**。此操作将自动创建另一个名为 **ANOMALY\$1DETECTION\$1BAND** 的指标。对于此过程的范围，我们可通过删除新生成的 **ad1 指标**上的复选框来忽略它。  
![\[CloudWatch 控制台。在绘成图表的指标的下拉列表中选择了统计信息 SUM。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning6.png)  
![\[CloudWatch 控制台。从绘成图表的指标的列表中删除 ANOMALY_DETECTION_BAND 指标。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning7.png)

1. 重复步骤 8，将 **m2 ID** 重命名为 **provisionedWCU**。保留统计函数设置为 **Average**。  
![\[CloudWatch 控制台。ID 为 m2 的绘成图表的指标重命名为 provisionedWCU。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning8.png)

1. 选择 **Expression1** 标签，并将值更新为 **m1**，将标签更新为 **Consumed WCUs**。
**注意**  
确保您只选择了 **m1**（左侧的复选框）和 **provisionedWCU** 以正确可视化数据。更新公式，方法是单击**详细信息**并将公式更改为 **consumedWCU/PERIOD(consumedWCU)**。这一步还可能生成另一个 **ANOMALY\$1DETECTION\$1BAND** 指标，但对于此过程的范围，我们可以忽略它。  

![\[已选择 m1 和 provisionedWCU。m1 的详细信息已更新为 consumedWCU/PERIOD(consumedWCU)。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning10.png)


1. 您现在应该有两个图形：一个指示表上预置的 WCU，另一个指示已使用的 WCU。您的图形形状可能与下面的不同，不过可以将其作为参考：  
![\[图中绘制了表的预置 WCU 和已使用 WCU。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning11.png)

1. 通过选择 Expression2 图形 (**e2**) 来更新百分比公式。将标签和 ID 重命名为 **utilizationPercentage**。重命名公式，以匹配 **100\$1(m1/provisionedWCU)**。  
![\[CloudWatch 控制台。Expression2 的标签和 ID 已重命名为 utilizationPercentage。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning12.png)  
![\[CloudWatch 控制台。Expression2 的百分比公式已更新为 100*(m1/provisionedWCU)。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning13.png)

1. 清除除 **utilizationPercentage** 之外的所有指标的复选框，以可视化您的利用率模式。默认间隔设置为 1 分钟，但您可以根据需要随意修改。  
![\[图中显示了所选时间间隔内的 utilizationPercentage 指标。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning14.png)

以下是一个更长的时间段和一个更大的 1 小时周期的视图。可以看到，一些间隔的利用率超过 100%，不过这种特定的工作负载具有较长的零利用率间隔。

![\[较长时段的利用率模式。它突出显示了利用率超过 100% 和零的时段。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/CostOptimization/RightSizedProvisioning15.png)


此时，您可能获得与本例中的图片不同的结果。这完全取决于您的工作负载中的数据。利用率超过 100% 的间隔容易导致节流事件。DynamoDB 提供[容量暴增](burst-adaptive-capacity.md#burst-capacity)，不过一旦容量暴增，任何超过 100% 的流量都将被节流。

------

## 如何识别配置不足的 DynamoDB 表
<a name="CostOptimization_RightSizedProvisioning_UnderProvisionedTables"></a>

对于大多数工作负载，如果一个表持续消耗其配置容量超过 80%，则该表被视为配置不足。

[容量暴增](burst-adaptive-capacity.md#burst-capacity)是 DynamoDB 的一项功能，它允许客户临时消耗比最初配置更多的 RCU/WCU（超过表中定义的每秒预调配吞吐量）。创建容量暴增的目的是应对因特殊事件或使用量高峰而突然增加的流量。这种容量暴增不能永远持续下去。一旦未用的 RCU 和 WCU 耗尽，您再试图消耗比预置更多的容量时，就会被节流。当您的应用程序流量接近 80% 的利用率时，被节流的风险会大大增加。

80% 的利用率规则因数据的季节性和流量增长而异。考虑以下场景：
+ 如果您的流量在过去 12 个月中一直**稳定**在大约 90% 的利用率，那么您的表容量正合适
+ 如果您的应用程序流量在不到 3 个月内以每月 8% 的速度**增长**，那么您将达到 100% 利用率
+ 如果您的应用程序流量在略长于 4 个月内以每月 5% 的速度**增长**，那么您仍然将达到 100% 利用率

以上查询的结果可以说明您的利用率。将它们作为指导，来进一步评估其他指标，以帮助您在需要时选择增加表容量（例如：每月或每周增长率）。与您的运营团队合作，为您的工作负载和表定义一个合适的百分比。

在有些特殊场景中，当我们每天或每周进行数据分析时，会发现数据是倾斜的。例如，季节性应用程序在工作时间会出现使用量激增情况（但在工作时间之外则降至几乎为零），您可以通过[安排自动扩缩](https://docs.amazonaws.cn/autoscaling/application/userguide/examples-scheduled-actions.html)来轻松指定一天中什么时间（以及一周中的星期几）增加预置容量，以及何时减少预置容量。即使您的季节性不那么明显，也可以利用 [DynamoDB 表自动扩缩](AutoScaling.md)配置，而无需为了涵盖繁忙时间而设定较高的容量。

**注意**  
为基表创建 DynamoDB Auto Scaling 配置时，请记住为任何与该表关联的 GSI 包括另一个配置。

## 如何识别过度配置的 DynamoDB 表
<a name="CostOptimization_RightSizedProvisioning_OverProvisionedTables"></a>

从上述脚本中获得的查询结果提供了执行某些初始分析所需的数据点。如果您的数据集在多个时间间隔内显示为利用率低于 20%，则您的表可能配置过度。要进一步确定是否需要减少 WCU 和 RCU 的数量，应重新查看间隔内的其他读数。

当您的表包含多个低使用量间隔时，您其实可以利用自动扩缩策略，安排自动扩缩，或者为表配置基于利用率的默认自动扩缩策略。

如果您的工作负载具有低利用率和高节流比率（间隔内的 **Max(ThrottleEvents)/Min(ThrottleEvents)**），这说明在某些天（或几小时）内流量大增，因而造成了非常高的工作负载，但总体上流量一直较低。在这些场景中，使用[预定自动扩缩](https://docs.amazonaws.cn/autoscaling/application/userguide/examples-scheduled-actions.html)可能有所裨益。

Amazon [Well-Architected Framework](https://www.amazonaws.cn/architecture/well-architected/) 可帮助云架构师为各种应用程序和工作负载构建安全、高性能、弹性和高效的基础架构。Amazon Well-Architected 围绕六大支柱 - 卓越运营、安全性、可靠性、性能效率、成本优化和可持续性，为客户和合作伙伴提供了评估架构和实施可扩展设计的一致方法。

Amazon [Well-Architected Lenses](https://docs.amazonaws.cn/wellarchitected/latest/userguide/lenses.html) 将 Amazon Well-Architected 提供的指导扩展到特定的行业和技术领域。Amazon DynamoDB Well-Architected Lens 专注于 DynamoDB 工作负载。它提供了最佳实践、设计原则和问题，用于评估和审查 DynamoDB 工作负载。完成 Amazon DynamoDB Well-Architected Lens 审查将为您提供有关推荐设计原则的培训和指导，因为它与 Amazon Well-Architected 的每个支柱都有关。本指南基于我们与不同行业、细分市场、规模和地域的客户合作的经验。

 作为 Well-Architected Lens 审查的直接结果，您将收到一份可行的建议摘要，用以优化和改进 DynamoDB 工作负载。

## 执行 Amazon DynamoDB Well-Architected Lens 审查
<a name="bp-wal-conducting"></a>

DynamoDB Well-Architected Lens 审查通常由 Amazon 解决方案架构师与客户共同执行，但也可以由客户以自助服务形式执行。虽然我们建议将所有六个 Well-Architected 支柱作为 Amazon DynamoDB Well-Architected Lens 的一部分进行审查，但您也可以决定先将重点放在一个或多个支柱上。

有关进行 Amazon DynamoDB Well-Architected Lens 审查的更多信息和说明，请观看[此视频](https://youtu.be/mLAUvJYvBjA)并查看 [DynamoDB Well-Architected Lens GitHub 页面](https://github.com/aws-samples/custom-lens-wa-hub/tree/main/DynamoDB)。

## Amazon DynamoDB Well-Architected Lens 的支柱
<a name="bp-wal-pillars"></a>

Amazon DynamoDB Well-Architected Lens 围绕六个支柱：

**性能效率支柱**

性能效率支柱涉及有效地使用计算资源以满足系统要求的能力以及在需求变化和技术改进时保持此效率的能力。

该支柱的主要 DynamoDB 设计原则围绕[数据建模](https://docs.amazonaws.cn/amazondynamodb/latest/developerguide/bp-relational-modeling.html)、[选择分区键](https://docs.amazonaws.cn/amazondynamodb/latest/developerguide/HowItWorks.Partitions.html#HowItWorks.Partitions.SimpleKey)和[排序键](https://docs.amazonaws.cn/amazondynamodb/latest/developerguide/HowItWorks.Partitions.html#HowItWorks.Partitions.CompositeKey)以及根据应用程序访问模式[定义二级索引](https://docs.amazonaws.cn/amazondynamodb/latest/developerguide/bp-indexes.html)展开。其他注意事项包括为工作负载选择最佳吞吐量模式、Amazon SDK 调整以及在适当时使用最佳缓存策略。要了解有关这些设计原则的更多信息，请观看这个关于 DynamoDB Well-Architected Lens 性能效率支柱的[深入探讨视频](https://youtu.be/PuCIy5Weyi8)。

**成本优化支柱**

成本优化支柱侧重于避免不必要的成本。

关键主题包括了解和控制资金花在哪里，选择最合适和正确数量的资源类型，分析一段时间内的支出，设计数据模型以优化应用程序特定访问模式的成本，以及在不超支的情况下进行扩缩以满足业务需求。

DynamoDB 的关键成本优化设计原则围绕为您的表选择最合适的容量模式和表类别，并通过使用按需容量模式或带自动扩缩功能的预置容量模式来避免过度配置容量。其它注意事项包括高效的数据建模和查询以减少消耗的容量、以折扣价保留部分已消耗容量、最小化项目大小、识别和移除未使用的资源以及使用 [TTL](TTL.md) 自动免费删除过时的数据。要了解有关这些设计原则的更多信息，请观看这个关于 DynamoDB Well-Architected Lens 成本优化支柱的[深入探讨视频](https://youtu.be/iuI0HUuw6Jg)。

有关 DynamoDB 成本优化最佳实践的更多信息，请参阅[成本优化](https://docs.amazonaws.cn/amazondynamodb/latest/developerguide/bp-cost-optimization.html)。

**卓越运营支柱**

卓越运营支柱侧重于运行和监控系统以提供商业价值，并不断改进流程和程序。关键主题包括自动变更、响应事件和定义管理日常运营的标准。

DynamoDB 的主要卓越运营设计原则包括通过 Amazon CloudWatch 和 Amazon Config 监控 DynamoDB 指标，以及在违反预定义阈值或检测到不合规规则时自动发出警报并进行修正。其他注意事项包括通过基础架构将 DynamoDB 资源定义为代码，以及利用标签更好地组织、识别和核算 DynamoDB 资源。要了解有关这些设计原则的更多信息，请观看这个关于 DynamoDB Well-Architected Lens 卓越运营支柱的[深入探讨视频](https://youtu.be/41HUSL9tJa8)。

**可靠性支柱**

可靠性支柱侧重于确保工作负载按预期正确、一致地执行其预期功能。弹性工作负载可快速从故障中恢复，以满足业务和客户需求。关键主题包括分布式系统设计、恢复规划以及如何处理更改。

DynamoDB 的基本可靠性设计原则围绕以下几个方面：根据您的 RPO 和 RTO 要求选择备份策略和保留；对多区域工作负载或 RTO 较低的跨区域灾难恢复场景使用 DynamoDB 全局表；通过在 Amazon SDK 中配置和使用这些功能以在应用程序中使用指数回退实现重试逻辑；通过 Amazon CloudWatch 监控 DynamoDB 指标，以及在违反预定义阈值时自动发出警报并进行修复。要了解有关这些设计原则的更多信息，请观看这个关于 DynamoDB Well-Architected Lens 可靠性支柱的[深入探讨视频](https://youtu.be/8AoPBxVQYM8)。

**安全支柱**

安全支柱侧重于保护信息和系统。关键主题包括数据的机密性和完整性、识别和管理谁能通过权限管理做什么、保护系统以及建立用以检测安全事件的控制措施。

DynamoDB 的主要安全设计原则是使用 HTTPS 加密传输中的数据，选择静态数据加密的密钥类型，以及定义 IAM 角色和策略以对 DynamoDB 资源进行身份验证、授权和提供精细访问。其他注意事项包括通过 Amazon CloudTrail 审计 DynamoDB 控制面板和数据面板。要了解有关这些设计原则的更多信息，请观看这个关于 DynamoDB Well-Architected Lens 安全支柱的[深入探讨视频](https://youtu.be/95prjv2EEXA?si=xvNci2MM856siejv)。

有关 DynamoDB 安全性的更多信息，请参阅[安全性](https://docs.amazonaws.cn/amazondynamodb/latest/developerguide/security.html)。

**可持续发展支柱**

可持续发展支柱侧重于最大限度地降低运行云工作负载对环境的影响。关键主题包括可持续发展责任共担模式、了解影响以及最大限度提高利用率以尽量减少所需资源和减轻下游影响。

DynamoDB 的主要可持续发展设计原则包括以下方面：识别和移除未使用的 DynamoDB 资源；通过使用按需容量模式或具有自动扩缩功能的预置容量模式避免过度配置；高效查询以减少所消耗的容量，以及通过压缩数据和使用 TTL 删除过时的数据来减少存储空间占用。要了解有关这些设计原则的更多信息，请观看这个关于 DynamoDB Well-Architected Lens 可持续发展支柱的[深入探讨视频](https://youtu.be/fAfYms7u3EE)。

# 在 DynamoDB 中设计并有效使用分区键的最佳实践
<a name="bp-partition-key-design"></a>

唯一标识 Amazon DynamoDB 表中每个项目的主键可以是简单结构（仅分区键）或复合结构（分区键与排序键的组合）。

应对应用程序进行设计，以便在表中的所有分区键及其二级索引中开展统一的活动。可以确定应用程序所需的访问模式，以及每个表和二级索引所需的读取与写入单位。

**注意**  
自适应容量适用于按需模式和预调配容量。

DynamoDB 表中的每个分区都设计为提供每秒 3000 个读取单位和每秒 1000 个写入单位的最大容量。一个读取单位表示对大小最多为 4 KB 的项目每秒执行一次强一致性读取操作，或每秒执行两次最终一致性读取操作。一个写入单位表示对大小最多为 1 KB 的项目每秒执行一次写入操作。

在评估表的分区吞吐量限制时，必须考虑项目大小。例如，如果表的项目大小为 20 KB，则单个一致性读取操作将使用 5 个读取单位。这意味着，在达到分区限制之前，可以每秒同时对该单个项目进行 600 次一致性读取操作。表中所有分区的总吞吐量在预置模式下可能受预调配吞吐量所限，或在按需模式下可能受表级吞吐量限制所限。有关更多信息，请参阅[服务限额](https://docs.amazonaws.cn/amazondynamodb/latest/developerguide/ServiceQuotas.html)。

**Topics**
+ [在 DynamoDB 中设计分区键来分配工作负载](bp-partition-key-uniform-load.md)
+ [在 DynamoDB 表中使用写入分片来均匀分配工作负载](bp-partition-key-sharding.md)
+ [在 DynamoDB 中的数据上传期间高效分配写入活动](bp-partition-key-data-upload.md)

# 在 DynamoDB 中设计分区键来分配工作负载
<a name="bp-partition-key-uniform-load"></a>

表主键的分区键部分确定存储表数据的逻辑分区，反过来会影响底层物理分区。不能有效分配 I/O 请求的分区键设计会产生“热”分区，从而导致节流且不能有效地使用预置 I/O 容量。

要最佳使用表的预调配吞吐量，不仅取决于各个项目的工作负载模式，还取决于分区键设计。这并不意味着必须访问所有分区键值以实现高效吞吐量，或者已访问分区键值的百分比必须非常高，而是工作负载访问的分区键值越不同，请求在已分配空间分配越多。通常随着访问分区键值数量与分区键值总数的比值增加，使用预调配吞吐量的效率将提高。

下面比较一些常见分区键架构的预调配吞吐量效率。


****  

| 分区键值 | 均匀性 | 
| --- | --- | 
| 用户 ID，应用程序有多个用户。 | 好 | 
| 状态代码，只有少数几个可能的状态代码。 | 差 | 
| 项目创建日期，四舍五入至最近的时间（例如，天、小时或分钟）。 | 差 | 
| 设备 ID，每个设备以相对类似间隔访问数据。 | 好 | 
| 设备 ID，即使跟踪多个设备，但其中一个设备远比所有其他设备热门。 | 差 | 

如果单个表只有少量的分区键值，请考虑更多不同非分区键值之间分配写入操作。也就是说，设计主键元素，避免出现一个“热门”（请求频率非常高的）分区键值，导致整体性能降低。

例如，考虑设计具有复合主键的表。分区键代表项目创建日期，四舍五入至最近的天。排序键是项目标识符。在指定日期，如 `2014-07-09`，**所有**新项目写入单个分区键值（和对应的物理分区）。

如果表可以完全放入单个分区（考虑数据随时间的增加），并且应用程序的读写吞吐量要求不超过单个分区的读取和写入容量，则应用程序不会因分区遇到意外节流。

要使用 NoSQL Workbench for DynamoDB 来帮助可视化您的分区键设计，请参阅[使用 NoSQL Workbench 构建数据模型](workbench.Modeler.md)。

# 在 DynamoDB 表中使用写入分片来均匀分配工作负载
<a name="bp-partition-key-sharding"></a>

扩大空间是一种在 Amazon DynamoDB 的分区键空间更好地分配写入的方式。可以通过多种不同方式来进行。可以将随机数加入分区键值，在分区之间分配项目。或者，可以使用基于查询内容计算的数字。

## 使用随机后缀分片
<a name="bp-partition-key-sharding-random"></a>

将随机数加入分区键值末尾，是在分区键空间更均匀分配负载的一种策略。然后在更大空间内随机写入。

例如，对于表示当天日期的分区键，可能选择介于 `1` 和 `200` 之间的随机数，并将它作为后缀连接到日期。这将生成分区键值 `2014-07-09.1`、`2014-07-09.2`，以此类推，直到 `2014-07-09.200`。由于随机化分区键，每天表的写入将均匀分布在多个分区。这样可以提高并行度和总体吞吐量。

但是，要读取指定日期的所有项目，必须针对所有后缀查询项目，然后合并结果。例如，先对分区键值 `2014-07-09.1` 发出 `Query` 请求。然后，对 `2014-07-09.2` 发出另一个 `Query`，以此类推，直到 `2014-07-09.200`。最后，应用程序必须合并所有 `Query` 请求的结果。

## 使用计算后缀分片
<a name="bp-partition-key-sharding-calculated"></a>

随机化策略可以显著改善写入吞吐量，但难以读取特定项目，因为不知道写入项目时使用的后缀值。要更容易读取各个项目，可使用其他策略。不使用随机数在分区间分配项目，而是使用可根据查询内容计算的数字。

考虑上一个示例，表在分区键中使用当天日期。现在假设每个项目有一个可访问的 `OrderId` 属性，除了日期外，最经常需要按订单 ID 查找项目。应用程序将项目写入表之前，可以根据订单 ID 计算一个哈希后缀，并将此后缀追加到分区键日期。计算可能产生一个介于 1 和 200 之间非常均匀分布的数字，类似于随机策略所生成的数字。

简单的计算足够了，例如订单 ID 字符的 UTF-8 码位值的乘积，取模 200，加 1。分区键值是连接计算结果的日期。

利用此策略，写入均匀分布在分区键值之间，从而均匀分布在物理分区之间。可以对特殊项目和日期轻松执行 `GetItem` 操作，因为可以为特定 `OrderId` 值计算分区键值。

要读取指定日期的所有项目，仍必须`Query`每个 `2014-07-09.N` 键（其中，`N` 为 1-200），然后应用程序必须合并所有结果。好处是避免出现单个“热门”分区键值，承担所有工作负载。

**注意**  
有关专门设计用于处理大容量时间序列数据的更高效策略，请参见[时间序列数据](bp-time-series.md)。

# 在 DynamoDB 中的数据上传期间高效分配写入活动
<a name="bp-partition-key-data-upload"></a>

通常从其他数据源加载数据时，Amazon DynamoDB 会在多个服务器上分区表数据。如果同时将数据上传到所有分配的服务器，则可以获得更好的性能。

例如，假设要将用户消息上传到的 DynamoDB 表使用复合主键，`UserID` 作为分区键，`MessageID` 作为排序键。

上传数据时，可以为每个用户上传的所有消息项目，一个用户接一个用户：


****  

| UserID | MessageID | 
| --- | --- | 
| U1 | 1 | 
| U1 | 2 | 
| U1 | ... | 
| U1 | ... 最多 100 | 
| U2 | 1 | 
| U2 | 2 | 
| U2 | ... | 
| U2 | ... 最多 200 | 

这种情况下的问题是没有在 DynamoDB 的分区键值中分配写入请求。一次取一个分区键值，上传所有项目，然后下一个分区键值，进行相同操作。

DynamoDB 后台在多个服务器中分区表的数据。要充分利用为表预置的所有吞吐容量，必须在分区键值之间分配工作负载。对全部具有相同分区键值的项目进行不均匀的上传工作，将无法完全利用 DynamoDB 为表预置的所有资源。

可以使用排序键从每个分区键值中加载一个项目，然后从每个分区键值加载另一个项目，以此类推，分配上传工作：


****  

| UserID | MessageID | 
| --- | --- | 
| U1 | 1 | 
| U2 | 1 | 
| U3 | 1 | 
| ... | ... | 
| U1 | 2 | 
| U2 | 2 | 
| U3 | 2 | 
| ... | ... | 

按这个顺序均匀加载，将使用不同的分区键值，使更多 DynamoDB 服务器同时处于繁忙状态，提高吞吐量性能。

# 在 DynamoDB 中使用排序键整理数据的最佳实践
<a name="bp-sort-keys"></a>

在 Amazon DynamoDB 表中，唯一标识表中每个项目的主键由分区键和排序键组成。

精心设计的排序键具有两个重要优势：
+ 将相关信息聚集在一个位置，可以高效查询。利用精心设计的排序键，可以使用带运算符（如 `begins_with`、`between`、`>`、`<` 等）的范围查询检索通常需要的相关项目组。
+ 利用复合排序键，可以在数据中定义层级（一对多）关系，在任何层级查询。

  例如，在列出地理位置的表中，可以如下设计排序键。

  ```
  [country]#[region]#[state]#[county]#[city]#[neighborhood]
  ```

  这样可以为任何一个聚合层面的位置列表（从 `country` 到 `neighborhood` 以及二者之间的所有内容）设计高效的范围查询。

## 使用排序键进行版本控制
<a name="bp-sort-keys-version-control"></a>

许多应用程序需要维护项目级修订历史记录，用于审计或合规性用途，并且需要能够轻松检索最新版本。下面介绍一种使用排序键前缀实现的有效设计模式：
+ 对于每个新项目，创建两个副本：一个副本在排序键开始位置具有版本号前缀零（如 `v0_`），一个副本具有版本号前缀 1 (如 `v1_`)。
+ 每次更新项目时，在更新版本的排序键中使用下一个更高的版本前缀，将更新后的内容复制到具有版本前缀零的项目。这意味着，可使用前缀零轻松找到任何项目的最新版本。

例如，部件制造商可使用如下所示的架构。

![\[版本控制示例，显示一个包含主键和数据项目属性的表。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/VersionControl.png)


`Equipment_1` 项目将经过各个审计员的一系列审计。每次新审计的结果保存在表的一个新项目中，从版本号 1 开始，每次后续修订递增该数字。

添加新版本后，应用程序层将版本零项目的内容（排序键等于 `v0_Audit`）替换为新版本的内容。

如果应用程序需要检索最新审计状态，可以查询 `v0_` 的排序键前缀。

如果应用程序需要检索完整修订历史记录，可以查询项目分区键下的所有项目，筛选 `v0_` 项目。

此设计还适合在排序键前缀后加入排序键的各个部件 ID，对一件设备的多个部件进行审计的情况。

# 在 DynamoDB 中使用二级索引的最佳实践
<a name="bp-indexes"></a>

二级索引通常对于支持应用程序需要的查询模式至关重要。但是，过度使用二级索引或低效率使用也会不必要地增加成本，降低性能。

**Contents**
+ [DynamoDB 中二级索引的一般指导原则](bp-indexes-general.md)
  + [高效使用索引](bp-indexes-general.md#bp-indexes-general-efficiency)
  + [慎重选择投影](bp-indexes-general.md#bp-indexes-general-projections)
  + [优化频繁查询以避免获取](bp-indexes-general.md#bp-indexes-general-fetches)
  + [创建本地二级索引时注意项目集合大小限制](bp-indexes-general.md#bp-indexes-general-expanding-collections)
+ [利用稀疏索引](bp-indexes-general-sparse-indexes.md)
  + [DynamoDB 中稀疏索引的示例](bp-indexes-general-sparse-indexes.md#bp-indexes-sparse-examples)
+ [在 DynamoDB 中使用全局二级索引进行具体化聚合查询](bp-gsi-aggregation.md)
  + [示例场景和访问模式](bp-gsi-aggregation.md#bp-gsi-aggregation-scenario)
  + [采用预先计算聚合的原因](bp-gsi-aggregation.md#bp-gsi-aggregation-why)
  + [表设计](bp-gsi-aggregation.md#bp-gsi-aggregation-table-design)
  + [使用 Streams 和 Amazon Lambda 的聚合管道](bp-gsi-aggregation.md#bp-gsi-aggregation-pipeline)
  + [稀疏 GSI 设计](bp-gsi-aggregation.md#bp-gsi-aggregation-sparse-gsi)
  + [查询 GSI](bp-gsi-aggregation.md#bp-gsi-aggregation-querying)
  + [注意事项](bp-gsi-aggregation.md#bp-gsi-aggregation-considerations)
+ [在 DynamoDB 中重载全局二级索引](bp-gsi-overloading.md)
+ [在 DynamoDB 中对选择性表查询使用全局二级索引写入分片](bp-indexes-gsi-sharding.md)
  + [模式设计](bp-indexes-gsi-sharding.md#bp-indexes-gsi-sharding-pattern-design)
  + [分片策略](bp-indexes-gsi-sharding.md#bp-indexes-gsi-sharding-strategy)
  + [查询分片 GSI](bp-indexes-gsi-sharding.md#bp-indexes-gsi-querying-the-sharded-GSI)
  + [并行查询执行注意事项：](bp-indexes-gsi-sharding.md#bp-indexes-gsi-parallel-query-execution-considerations)
  + [代码示例](bp-indexes-gsi-sharding.md#bp-indexes-gsi-code-example)
+ [在 DynamoDB 中使用全局二级索引创建最终一致副本](bp-indexes-gsi-replica.md)

# DynamoDB 中二级索引的一般指导原则
<a name="bp-indexes-general"></a>

Amazon DynamoDB 支持两种的二级索引：
+ **全局二级索引（GSI）** – 索引的分区键和排序键可与基表不同。全局二级索引之所以称为“全局”，是因为索引上的查询可跨过所有分区，覆盖基表的所有数据。全局二级索引没有大小限制，有自己的预置读取写入吞吐量设置，与表不同。
+ **本地二级索引（LSI）** – 索引的分区键与基表相同，但排序键不同。本地二级索引之所以称为“本地”，是因为索引的每个分区的范围都限定为具有相同分区键值的基表分区。因此，任何一个分区键值的索引项目总大小不得超过 10 GB。此外，本地二级索引与其索引的表共享预置的读写吞吐量设置。

DynamoDB 每个表最多可以具有 20 个全局二级索引（默认配额）和 5 个本地二级索引。

全局二级索引通常比本地二级索引更有用。确定要使用哪种类型的索引还将取决于您的应用程序的要求。有关全局二级索引和本地二级索引的比较以及有关如何在二者之间进行选择的更多信息，请参阅[在 DynamoDB 中使用二级索引改进数据访问](SecondaryIndexes.md)。

在 DynamoDB 中创建索引时要记住的一些一般原则和设计模式如下：

**Topics**
+ [高效使用索引](#bp-indexes-general-efficiency)
+ [慎重选择投影](#bp-indexes-general-projections)
+ [优化频繁查询以避免获取](#bp-indexes-general-fetches)
+ [创建本地二级索引时注意项目集合大小限制](#bp-indexes-general-expanding-collections)

## 高效使用索引
<a name="bp-indexes-general-efficiency"></a>

**尽可能减少索引数量。**不要对不常查询的属性创建二级索引。很少使用的索引会增加存储和 I/O 成本，无法提高应用程序性能。

## 慎重选择投影
<a name="bp-indexes-general-projections"></a>

二级索引占用存储空间和预调配吞吐量，应尽可能减小索引。此外，索引越小，相比查询整个表的性能优势越明显。如果查询通常只返回很少一部分属性，并且这些属性的总和远小于整个项目，则应只投影经常请求的属性。

如果预计表有大量写入操作（相比读取），请遵循以下最佳实践：
+ 考虑减少投影属性的数量，尽可能减少写入索引的项目大小。但是，这只适用于投影属性的大小大于单个写入容量单位 (1 KB) 的情况。例如，如果索引条目的大小仅为 200 字节，则 DynamoDB 将向上取整为 1 KB。也就是说，如果索引项目很小，可以投影更多属性，而不会额外增加成本。
+ 避免投影查询中极少需要的属性。每次更新索引中投影的属性时，将产生更新索引的额外成本。仍然可以以较高的预调配吞吐量成本检索 `Query` 中的未投影属性，但查询成本明显低于频繁更新索引的成本。
+ 仅当需要查询返回按不同排序键排序的整个表项目时，指定 `ALL`。投影所有属性将无需表获取，但在大多数情况下，存储和写入操作成本将加倍。

下一节介绍在保证索引尽可能小，与保证获取操作尽可能少的需求之间取得平衡。

## 优化频繁查询以避免获取
<a name="bp-indexes-general-fetches"></a>

要实现最快查询并且延迟最低，可以投影预计查询将返回的所有属性。具体而言，如果对没有投影的属性查询本地二级索引，DynamoDB 将自动从表获取这些属性，这需要从表读取整个项目，带来本可避免的延迟和额外 I/O 操作。

请记住，这些“偶尔”查询经常会转变成“必要”查询。如果预计仅仅偶尔查询，所以计划不投影某些属性，请考虑是否情况可能改变，后悔没有投影这些属性。

有关表获取的更多信息，请参阅 [全局二级索引的预调配吞吐量注意事项](LSI.md#LSI.ThroughputConsiderations)。

## 创建本地二级索引时注意项目集合大小限制
<a name="bp-indexes-general-expanding-collections"></a>

*项目集合*指表的所有项目及其具有相同分区键的本地二级索引。项目集合不能超过 10 GB，因此特定的分区键值可能会出现空间用尽的情况。

添加或更新表项目时，DynamoDB 会更新受影响的所有本地二级索引。如果表中定义索引属性，本地二级索引也会增长。

创建本地二级索引时，考虑将写入的数据量，以及具有相同分区键值的数据项目数量。如果预计特定分区键值的表和索引项目总和可能超过 10 GB，请考虑是否应避免创建索引。

如果无法避免创建本地二级索引，则必须预计项目集合大小限制，并在超过限制前采取措施。作为最佳实践，在写入项目时应使用 [https://docs.amazonaws.cn/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/model/ReturnItemCollectionMetrics.html](https://docs.amazonaws.cn/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/model/ReturnItemCollectionMetrics.html) 参数来监控接近 10 GB 大小限制的项目集大小并发出警报。超过最大项目集大小将导致写入尝试失败。您可以在项目集大小尚未对应用程序造成影响前，对其进行监控并发出警报，以此来缓解项目集大小问题。

**注意**  
创建后，您无法删除本地二级索引。

有关在限制范围内操作以及采取纠正措施的策略，请参阅 [项目集合大小限制](LSI.md#LSI.ItemCollections.SizeLimit)。

# 利用稀疏索引
<a name="bp-indexes-general-sparse-indexes"></a>

对于表中的任何项目，**仅当项目中存在索引键属性时**，DynamoDB 才会写入相应的索引条目。对于全局二级索引，这意味着必须在项目上定义索引分区键，如果索引还有排序键，则该属性也必须存在。如果某个项目中缺少任何一个关键属性，则该项目不会出现在索引中。仅有基表中一部分项目的索引称为*稀疏*索引。

稀疏索引对于查询表的小子集非常有用。例如，假设有一个表存储所有客户订单，具有以下键属性：
+ 分区键：`CustomerId`
+ 排序键：`OrderId`

要跟踪未结订单，可以在尚未发货的订单项目中插入一个名为 `isOpen` 的属性。订单发货后，可以删除该属性。如果对 `CustomerId`（分区键）和 `isOpen`（排序键）创建索引，则仅显示定义了 `isOpen` 的订单。如果数以千计的订单中只有少量订单处于未结状态，查询未结订单索引比扫描整个表更快，成本更低。

可以使用值在索引中生成有用排序顺序的属性，代替 `isOpen` 属性。例如，可以使用设置为下每个订单的日期的 `OrderOpenDate` 属性，订单完成后删除。这样查询稀疏索引时，返回的项目将按下每个订单的日期排序。

## DynamoDB 中稀疏索引的示例
<a name="bp-indexes-sparse-examples"></a>

全局二级索引默认属于稀疏型。创建全局二级索引时，指定一个分区键，可以选择指定一个排序键。在基表中，只有包含所要求关键属性的项目才会显示在索引中。如果某个项目缺少索引分区键（或已定义的排序键），则将在索引中排除该项目。

将全局二级索引设计为稀疏索引，可以配置低于基表的写入吞吐量，同时仍实现出色的性能。

例如，游戏应用程序可能跟踪每个用户的所有得分，但通常只需查询一些高分。下面的设计高效处理这种情况：

![\[稀疏 GSI 示例。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/SparseIndex_A.png)


Rick 玩三款游戏，在其中一款游戏中达到 `Champ` 状态。Padma 玩四款游戏，在其中两款游戏中达到 `Champ` 状态。请注意，只有用户达到奖励的项目存在 `Award` 属性。关联全局二级索引如下所示：

![\[稀疏 GSI 示例。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/SparseIndex_B.png)


全局二级索引仅包含经常查询的高分，这是基表的一个小子集。

# 在 DynamoDB 中使用全局二级索引进行具体化聚合查询
<a name="bp-gsi-aggregation"></a>

对于希望快速做出决策的企业来说，对快速更改的数据维持近实时聚合和键指标正变得越来越重要。例如，音乐库可能需要近实时地展示下载量最多的歌曲，或者电子商务平台可能需要按类别显示热门产品。

由于 DynamoDB 并不原生支持跨项目的 `SUM` 或 `COUNT` 等聚合操作，因此在读取时计算这些值需要扫描大量项目，这可能导致速度缓慢并且成本高昂。您可以改为随着数据的更改*预先计算*聚合，并将结果作为常规项目存储在表中。这种模式称为*实体化聚合*。

**Topics**
+ [示例场景和访问模式](#bp-gsi-aggregation-scenario)
+ [采用预先计算聚合的原因](#bp-gsi-aggregation-why)
+ [表设计](#bp-gsi-aggregation-table-design)
+ [使用 Streams 和 Amazon Lambda 的聚合管道](#bp-gsi-aggregation-pipeline)
+ [稀疏 GSI 设计](#bp-gsi-aggregation-sparse-gsi)
+ [查询 GSI](#bp-gsi-aggregation-querying)
+ [注意事项](#bp-gsi-aggregation-considerations)

## 示例场景和访问模式
<a name="bp-gsi-aggregation-scenario"></a>

请考虑具有以下要求的音乐库应用程序：
+ 该应用程序记录大量的单首歌曲下载量（每秒数千首）。
+ 用户需要以个位数毫秒级的延迟，查看给定月份下载量最多的歌曲。
+ 该应用程序还需要支持诸如“本月十大热门歌曲”和“给定月份的所有歌曲下载量”之类的查询。

对于这种规模，若在读取时通过扫描所有下载记录来计算下载计数，成本可能会非常高。您可以改为维护一个累计计数，在每次下载时对其进行更新，并以支持高效查询的方式存储该计数。

## 采用预先计算聚合的原因
<a name="bp-gsi-aggregation-why"></a>

有多种方法可以计算聚合。下表比较了常见的备用方案，并解释了为什么通常 DynamoDB 中的实体化聚合最适合此类使用案例。


| 方法 | 权衡 | 何时使用 | 
| --- | --- | --- | 
| 读取时扫描并计数 | 需要在每次查询时读取所有下载记录。延迟会随着数据量的增长而增加，并且会消耗大量读取容量。 | 仅适用于非常小的数据集，此时不会有多少延迟影响。 | 
| 外部聚合存储（例如，Amazon ElastiCache） | 使用单独的服务进行管理，增加了操作复杂性。DynamoDB 与缓存之间需要同步逻辑。 | 需要亚毫秒级读取时，或者使用的复杂聚合逻辑超出了简单计数时。 | 
| 写入时的应用程序级别聚合 | 聚合逻辑与写入路径相耦合。如果在记录下载后但在更新计数之前，应用程序出现故障，则聚合结果将变得不一致。 | 需要同步的强一致性聚合并且可以容忍增加的写入延迟时。 | 
| 使用 Streams 和 Lambda 的实体化聚合 | 聚合与写入路径相分离。聚合具有最终一致性（通常落后几秒钟）。增加 Lambda 调用成本。 | 需要具有低读取延迟且可以容忍最终一致性的近实时聚合时。这是本页上介绍的方法。 | 

实体化聚合方法保持了简单的写入路径（只需记录下载），将聚合过程分载到异步流程，并将结果存储在 DynamoDB 中，这样就可以实现延迟为个位数毫秒级的查询。

## 表设计
<a name="bp-gsi-aggregation-table-design"></a>

此设计使用具有两种项目类型的单个表，这些项目共享相同的分区键 (`songID`)，但使用不同的排序键模式来区分它们：
+ **下载记录**：单独的下载事件。排序键是 `DownloadID`（每次下载的唯一标识符）。
+ **每月聚合项目**：每个月每首歌曲的预先计算下载计数。排序键是月份，采用 `YYYY-MM` 格式（例如，`2018-01`）。这些项目还包含 `DownloadCount` 属性，提供累计总数。

只有每月聚合项目包含 `Month` 属性。这种区别对于后文介绍的稀疏 GSI 设计很重要。

下图显示了具有这两种项目类型的表布局：

![\[音乐库表布局，显示共享相同分区键（songID）的下载记录和每月聚合项目。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/AggregationQueries.png)



| 项目类型 | 分区键（songID） | 排序键 | 其它属性 | 
| --- | --- | --- | --- | 
| 下载记录 | song1 | download-abc123 | UserID, Timestamp | 
| 每月聚合 | song1 | 2018-01 | Month=2018-01, DownloadCount=1,746,992 | 

## 使用 Streams 和 Amazon Lambda 的聚合管道
<a name="bp-gsi-aggregation-pipeline"></a>

聚合管道的工作方式如下：

1. 下载歌曲后，应用程序会向表中写入一个新项目，其 `Partition-Key=songID` 且 `Sort-Key=DownloadID`。

1. DynamoDB Streams 将此写入捕获为流记录。

1. 附加到流的 Lambda 函数处理新记录。该函数识别 `songID` 和当前月份，然后通过递增 `DownloadCount` 属性来更新对应的每月聚合项目。

1. 接下来，更新后的聚合项目可通过稀疏 GSI 进行查询。

Lambda 函数使用带有 `ADD` 表达式的 `UpdateItem` 调用，以原子方式增加下载计数。这样可以避免“读取-修改-写入”争用情况：

```
import boto3

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('MusicLibrary')

def handler(event, context):
    for record in event['Records']:
        if record['eventName'] == 'INSERT':
            new_image = record['dynamodb']['NewImage']
            song_id = new_image['songID']['S']
            # Derive the month from the download timestamp
            timestamp = new_image['Timestamp']['S']
            month = timestamp[:7]  # Extract YYYY-MM

            table.update_item(
                Key={
                    'songID': song_id,
                    'SK': month
                },
                UpdateExpression='ADD DownloadCount :inc SET #m = :month',
                ExpressionAttributeNames={
                    '#m': 'Month'
                },
                ExpressionAttributeValues={
                    ':inc': 1,
                    ':month': month
                }
            )
```

**注意**  
如果在写入更新后的聚合值之后，Lambda 执行失败，则可能会重试流记录。由于 `ADD` 操作在每次运行时都会增加计数，因此对于同一次下载，重试会多次导致增加计数，从而给出一个*近似*值。对于大多数分析和排行榜使用案例来说，这种细小的误差幅度是可以接受的。如果您需要精确的计数，则可以考虑添加幂等性逻辑，例如，使用条件表达式来检查是否已经处理了特定的 `DownloadID`。

## 稀疏 GSI 设计
<a name="bp-gsi-aggregation-sparse-gsi"></a>

要高效地查询聚合结果，请使用以下键架构创建全局二级索引：
+ **GSI 分区键：**`Month`（字符串）
+ **GSI 排序键：**`DownloadCount`（数字）

此 GSI 是*稀疏*的，因为只有每月聚合项目包含 `Month` 属性。单独的下载记录没有此属性，因此会自动从索引中排除。这意味着 GSI 仅包含预先计算的聚合项目，而这只是表中总项目的一小部分。

稀疏 GSI 有两个主要优点：
+ **更低的成本**：由于仅将聚合项目复制到索引，因此与包含表中所有项目的索引相比，您消耗的写入容量和存储空间要少得多。
+ **更快的查询**：索引仅包含您需要查询的数据，因此读取效率很高，并且能够以个位数毫秒级的延迟返回结果。

有关稀疏索引工作原理的更多信息，请参阅[利用稀疏索引](bp-indexes-general-sparse-indexes.md)。

## 查询 GSI
<a name="bp-gsi-aggregation-querying"></a>

在采用了稀疏 GSI 之后，您可以高效地处理多种类型的查询：

**获取给定月份下载次数最多的歌曲：**

```
aws dynamodb query \
    --table-name "MusicLibrary" \
    --index-name "MonthDownloadsIndex" \
    --key-condition-expression "#m = :month" \
    --expression-attribute-names '{"#m": "Month"}' \
    --expression-attribute-values '{":month": {"S": "2018-01"}}' \
    --scan-index-forward false \
    --limit 1
```

将 `ScanIndexForward` 设置为 `false`，可以按 `DownloadCount` 的降序顺序对结果进行排序，使用 `Limit=1` 可以仅返回排名第一的歌曲。

**获取给定月份排名前 10 的歌曲：**

```
aws dynamodb query \
    --table-name "MusicLibrary" \
    --index-name "MonthDownloadsIndex" \
    --key-condition-expression "#m = :month" \
    --expression-attribute-names '{"#m": "Month"}' \
    --expression-attribute-values '{":month": {"S": "2018-01"}}' \
    --scan-index-forward false \
    --limit 10
```

**获取给定月份内的所有歌曲下载量**（按下载次数排序）：

```
aws dynamodb query \
    --table-name "MusicLibrary" \
    --index-name "MonthDownloadsIndex" \
    --key-condition-expression "#m = :month" \
    --expression-attribute-names '{"#m": "Month"}' \
    --expression-attribute-values '{":month": {"S": "2018-01"}}' \
    --scan-index-forward false
```

## 注意事项
<a name="bp-gsi-aggregation-considerations"></a>

实施此模式时，请记住以下几点：
+ **最终一致性**：聚合值通过 DynamoDB Streams 和 Lambda 异步更新。从记录下载到更新聚合，通常会有几秒的延迟。这意味着 GSI 反映的是近实时数据，而不是实时数据。
+ **Lambda 并发性**：如果表具有较高的写入量，则多个 Lambda 调用可能会尝试同时更新同一个聚合项目。原子性 `ADD` 操作可以安全地处理这种情况，但您应该监控 Lambda 的并发度和节流指标，以确保函数能够满足流式处理的速度。
+ **GSI 写入容量**：由于稀疏 GSI 仅包含聚合项目，因此所需的写入容量要比基表少得多。但是，您仍应预调配足够的容量（或使用按需模式）来应对聚合更新的速度。
+ **近似计数**：如前所述，Lambda 重试可能会导致计数略微过多。对于需要精确计数的使用案例，请在 Lambda 函数中实施幂等性检查。

# 在 DynamoDB 中重载全局二级索引
<a name="bp-gsi-overloading"></a>

虽然 Amazon DynamoDB 的默认配额是每个表 20 个全局二级索引，但实际可以索引的数据字段远超过 20 个。关系数据库管理系统 (RDBMS) 的表架构统一，DynamoDB 的表则不同，一次可以保存多种不同类型的数据项目。此外，不同项目的同一属性可以包含完全不同的信息。

考虑下面保存各种不同数据的 DynamoDB 表布局的示例。

![\[GSI 重载的表架构。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/OverloadGSIexample.png)


所有项目公共的 `Data` 属性根据父项目不同，具有不同内容。如果为表创建一个全局二级索引，使用表的排序键作为分区键，`Data` 属性作为排序键，则可以使用该全局二级索引执行各种不同查询。查询包括：
+ 在全局二级索引中按姓名查找员工，使用 `Employee_Name` 作为分区键值，员工姓名（例如 `Murphy, John`）作为排序键值。
+ 使用全局二级索引搜索仓库 ID（如 `Warehouse_01`），查找具体仓库中工作的所有员工。
+ 查询全局二级索引，将 `HR_confidential` 作为分区键值，在排序键值中使用日期范围，获取最近招聘员工列表，

# 在 DynamoDB 中对选择性表查询使用全局二级索引写入分片
<a name="bp-indexes-gsi-sharding"></a>

当您需要在特定时间范围内查询近期数据时，对于大多数读取操作，DynamoDB 要求提供分区键，这可能会带来挑战。为了解决这种情况，您可以将写入分片和全局二级索引（GSI）结合使用，来实施高效的查询模式。

通过这种方法，您能够高效地检索和分析时间敏感型数据，而无需执行全表扫描，全表扫描会占用大量资源且成本高昂。通过战略性的表结构和索引设计，您可以创建灵活的解决方案，支持基于时间的数据检索，同时保持出色的性能。

**Topics**
+ [模式设计](#bp-indexes-gsi-sharding-pattern-design)
+ [分片策略](#bp-indexes-gsi-sharding-strategy)
+ [查询分片 GSI](#bp-indexes-gsi-querying-the-sharded-GSI)
+ [并行查询执行注意事项：](#bp-indexes-gsi-parallel-query-execution-considerations)
+ [代码示例](#bp-indexes-gsi-code-example)

## 模式设计
<a name="bp-indexes-gsi-sharding-pattern-design"></a>

使用 DynamoDB 时，为了克服时间敏感型数据检索带来的挑战，您可以实施一种精巧的模式，将写入分片和全局二级索引结合使用，从而实现对近期数据时段灵活且高效的查询。

**表的结构**
+ 分区键（PK）：“Username”

**GSI 的结构**
+ GSI 分区键（PK\$1GSI）：“ShardNumber\$1”
+ GSI 排序键（SK\$1GSI）：ISO 8601 时间戳（例如，“2030-04-01T12:00:00Z”）

![\[时间序列数据的模式设计。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/BestPractices-44-TimeBoundedTable-2.png)


## 分片策略
<a name="bp-indexes-gsi-sharding-strategy"></a>

假设您决定使用 10 个分片，则分片编号的范围可以是从 0 到 9。在记录活动时，您需要计算分片编号（例如，对用户 ID 上使用哈希函数，然后获取分片编号的模数），后将其添加到 GSI 分区键的前面。此方法将条目分布在不同的分片上，降低了过热分区的风险。

## 查询分片 GSI
<a name="bp-indexes-gsi-querying-the-sharded-GSI"></a>

在 DynamoDB 表中，要对所有分片查询特定时间范围内的项目，而其中的数据使用多个分区键进行分片，那么就需要采用与查询单个分区不同的方法。由于 DynamoDB 查询限制为一次使用一个分区键，因此您无法通过单个查询操作直接对多个分片进行查询。但是，您可以使用应用程序级逻辑，通过执行多个查询，每个查询针对一个特定的分片，然后汇总结果，这样就能得到所需的结果。以下过程说明如何执行此操作。

**查询和聚合分片**

1. 确定分片策略中使用的分片编号范围。例如，如果您有 10 个分片，则分片编号的范围是 0 到 9。

1. 对于每个分片，构造并执行一个查询，用于提取所需时间范围内的项目。查询可以并行执行来提高效率。在这些查询中，使用带有分片编号的分区键，以及带有时间范围的排序键。以下是针对单个分片的示例查询：

   ```
   aws dynamodb query \
       --table-name "YourTableName" \
       --index-name "YourIndexName" \
       --key-condition-expression "PK_GSI = :pk_val AND SK_GSI BETWEEN :start_date AND :end_date" \
       --expression-attribute-values '{
           ":pk_val": {"S": "ShardNumber#0"},
           ":start_date": {"S": "2024-04-01"},
           ":end_date": {"S": "2024-04-30"}
       }'
   ```  
![\[针对单个分片的查询示例。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/BestPractices-44-single-shard-example.png)

   您可以为每个分片复制此查询，并相应调整分区键（例如，“ShardNumber\$11”、“ShardNumber\$12”、…“ShardNumber\$19”）。

1. 所有查询完成后，汇总各个查询的结果。在应用程序代码中执行此聚合，将结果合并到一个数据集中，该数据集就代表了指定时间范围内所有分片中的项目。

## 并行查询执行注意事项：
<a name="bp-indexes-gsi-parallel-query-execution-considerations"></a>

每个查询都会消耗表或索引的读取容量。如果您使用预置吞吐量，请确保表已经预置了足够的容量用于处理突发的并行查询。如果您使用的是按需容量，请注意潜在的成本影响。

## 代码示例
<a name="bp-indexes-gsi-code-example"></a>

在 DynamoDB 中，要使用 Python 跨分片执行并行查询，您可以使用 boto3 库，这是适用于 Python 的 Amazon Web Services SDK。此示例假设您已安装 boto3 并配置了相应的 Amazon 凭证。

以下 Python 代码演示如何针对给定的时间范围，跨多个分片执行并行查询。代码中使用 concurrent.futures 并行执行查询，与顺序执行相比，缩短了总体执行时间。

```
import boto3
from concurrent.futures import ThreadPoolExecutor, as_completed

# Initialize a DynamoDB client
dynamodb = boto3.client('dynamodb')

# Define your table name and the total number of shards
table_name = 'YourTableName'
total_shards = 10  # Example: 10 shards numbered 0 to 9
time_start = "2030-03-15T09:00:00Z"
time_end = "2030-03-15T10:00:00Z"

def query_shard(shard_number):
    """
    Query items in a specific shard for the given time range.
    """
    response = dynamodb.query(
        TableName=table_name,
        IndexName='YourGSIName',  # Replace with your GSI name
        KeyConditionExpression="PK_GSI = :pk_val AND SK_GSI BETWEEN :date_start AND :date_end",
        ExpressionAttributeValues={
            ":pk_val": {"S": f"ShardNumber#{shard_number}"},
            ":date_start": {"S": time_start},
            ":date_end": {"S": time_end},
        }
    )
    return response['Items']

# Use ThreadPoolExecutor to query across shards in parallel
with ThreadPoolExecutor(max_workers=total_shards) as executor:
    # Submit a future for each shard query
    futures = {executor.submit(query_shard, shard_number): shard_number for shard_number in range(total_shards)}
    
    # Collect and aggregate results from all shards
    all_items = []
    for future in as_completed(futures):
        shard_number = futures[future]
        try:
            shard_items = future.result()
            all_items.extend(shard_items)
            print(f"Shard {shard_number} returned {len(shard_items)} items")
        except Exception as exc:
            print(f"Shard {shard_number} generated an exception: {exc}")

# Process the aggregated results (e.g., sorting, filtering) as needed
# For example, simply printing the count of all retrieved items
print(f"Total items retrieved from all shards: {len(all_items)}")
```

在运行此代码之前，请务必使用您的 DynamoDB 设置中的实际表和 GSI 名称替换 `YourTableName` 和 `YourGSIName`。此外，根据您的具体要求调整 `total_shards`、`time_start` 和 `time_end` 变量。

此脚本在每个分片中，查询指定时间范围内的项目并汇总结果。

# 在 DynamoDB 中使用全局二级索引创建最终一致副本
<a name="bp-indexes-gsi-replica"></a>

可以使用全局二级索引创建表的最终一致副本。创建副本可以实现以下用途：
+ **为不同读取器设置不同预调配读取容量。**例如，假设有两个应用程序：一个应用程序处理高优先级查询，需要最高读取性能，另一个应用程序处理低优先级查询，可以容忍读取操作节流。

  如果这两个应用程序从同一个表读取，则低优先级应用程序的大量读取负载可能占用表的所有可用读取容量。这将限制高优先级应用程序的读取操作。

  可以通过全局二级索引创建副本，设置与表不同的读取容量。然后让低优先级应用程序查询副本而不是表。
+ **完全不从表读取。**例如，一个应用程序从网站捕获大量点击流操作，不希望出现读取干扰的风险。可以隔离此表，阻止其他应用程序读取（请参阅 [使用 IAM 策略条件进行精细访问控制](specifying-conditions.md)），同时允许其他应用程序读取用全局二级索引创建的副本。

要创建复本，设置与父表具有相同键架构的全局二级索引，投影部分或全部非键属性。在应用程序中，可以将部分或全部读取操作定向到此全局二级索引，而不是父表。然后可以调整全局二级索引的预置读取容量处理这些读取，无需更改父表的预置读取容量。

写入父表与索引显示写入数据之间始终存在较短的传输延迟。换句话说，应用程序应考虑到全局二级索引副本只与父表具有*最终一致性*。

可以创建多个全局二级索引副本，支持不同读取模式。创建副本时，仅投影每个读取模式实际需要的属性。然后应用程序可以消耗较少的预置读取容量，仅获取所需的数据，而不必从父表读取项目。这种优化可以随着时间的推移大幅节约成本。

# 在 DynamoDB 中存储大型项目和属性的最佳实践
<a name="bp-use-s3-too"></a>

Amazon DynamoDB 将表中存储的每个项目的大小限制为 400 KB（请参阅 [项目大小](Constraints.md#limits-items-size)）。如果应用程序需要存储的项目数据超出 DynamoDB 限制允许，可以尝试压缩一个或多个大型属性，或者将项目拆分为多个项目（按照排序键高效索引）。还可以将项目作为对象存储在 Amazon Simple Storage Service (Amazon S3) 中，然后将 Amazon S3 对象标识符存储在 DynamoDB 项目中。

作为最佳实践，在写入项目时应使用 [https://docs.amazonaws.cn/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/model/ReturnConsumedCapacity.html](https://docs.amazonaws.cn/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/model/ReturnConsumedCapacity.html) 参数来监控接近 400 KB 最大大小的项目并发出警报。超过最大项目大小将导致写入尝试失败。DynamoDB 将返回 [ValidationException 错误](https://docs.amazonaws.cn/amazondynamodb/latest/developerguide/Programming.Errors.html)。监控项目大小并发出警报将使您能够在项目大小问题对应用程序造成影响之前缓解这些问题。

## 压缩大型属性值
<a name="bp-use-s3-too-or-compress"></a>

压缩大型属性值可以让属性值符合 DynamoDB 的项目限制，降低存储成本。压缩算法（如 GZIP 或 LZO）将产生的二进制输出可以存储在项目内的 `Binary` 属性类型中。

例如，考虑存储由论坛用户写入的消息的表。此类消息通常包含长文本字符串，可供压缩。虽然压缩可以减小项目的大小，但缺点是压缩后的属性值对筛选没有用处。

有关演示如何在 DynamoDB 中压缩此类消息的示例代码，请参见以下内容：
+ [示例：使用 适用于 Java 的 Amazon SDK 文档 API 处理二进制类型属性](JavaDocumentAPIBinaryTypeExample.md)
+ [示例：使用 适用于 .NET 的 Amazon SDK 低级 API 处理二进制类型属性](LowLevelDotNetBinaryTypeExample.md)

## 垂直分区
<a name="bp-use-s3-too-vertical-partitioning"></a>

处理大项目的另一种解决方案是将它们分解为较小的数据块，然后按分区键值关联所有相关项目。然后，您可以使用排序键字符串来识别与之一起存储的关联信息。通过执行此操作，按相同的分区键值对多个项目进行分组，您即创建一个[*项目集*](WorkingWithItemCollections.md)。

有关此方法的更多信息，请参阅：
+ [Use vertical partitioning to scale data efficiently in Amazon DynamoDB](https://www.amazonaws.cn/blogs/database/use-vertical-partitioning-to-scale-data-efficiently-in-amazon-dynamodb/) 
+ [Implement vertical partitioning in Amazon DynamoDB using Amazon Glue](https://www.amazonaws.cn/blogs/database/implement-vertical-partitioning-in-amazon-dynamodb-using-aws-glue/) 

## 在 Amazon S3 中存储大型属性值
<a name="bp-use-s3-too-large-values"></a>

如前所述，还可以使用 Amazon S3 存储无法放入 DynamoDB 项目的大型属性值。可以将它们作为对象存储在 Amazon S3 中，然后将对象标识符存储在 DynamoDB 项目中。

还可使用 Amazon S3 的对象元数据支持，提供返回至 DynamoDB 中父项目的链接。将项目的主键值作为对象的 Amazon S3 元数据存储在 Amazon S3 中。这样做通常有助于维护 Amazon S3 对象。

例如，请考虑 `ProductCatalog` 表。此表中的项目存储商品价格、描述、书的作者以及其他产品尺寸的信息。如果要存储的每个产品的图片过大，无法放入项目，可以将图片存储在 Amazon S3 而不是 DynamoDB 中。

实施此策略时，请记住以下几点：
+ DynamoDB 不支持跨 Amazon S3 和 DynamoDB 的事务。因此，应用程序必须处理任何故障，包括清理孤立的 Amazon S3 对象。
+ Amazon S3 限制对象标识符长度。因此组织数据时必须确保，不会生成过长对象标识符或违反其他 Amazon S3 约束。

有关如何使用 Amazon S3 的更多信息，请参阅 [Amazon Simple Storage Service 用户指南](https://docs.amazonaws.cn/AmazonS3/latest/userguide/)。

# 在 DynamoDB 中处理时间序列数据的最佳实践
<a name="bp-time-series"></a>

Amazon DynamoDB 的一般设计准则建议尽可能少使用表格。对于大多数应用程序，只需单个表即可。但是，对于时间序列数据，通常最好每个时间段为每个应用程序使用一个表。

## 时间序列数据的设计模式
<a name="bp-time-series-pattern"></a>

考虑一个需要跟踪大量活动的典型时间序列场景。写入访问模式是记录的所有事件都有当天日期。读取访问模式是当天事件读取频率最高，前一天事件的读取频率小很多，更早事件的读取频率几乎为零。一种处理方式是将当前日期和时间加入主键。

下面的设计模式通常可以高效应对这种场景：
+ 每个时间段创建一个表，预置所需的读取和写入容量以及所需的索引。
+ 每个时间段结束前，为下一个时间段预生成表。当前时间段结束时，将事件流量定向至新表。可以为这些表分配名称，指定这些表记录的时间段。
+ 只要不再写入表，就将预置的写入容量降至较低值（例如 1 WCU），预置适当的读取容量。随着时间推移，减少早期表的预置读取容量。可以选择存档或删除几乎或完全不需要其内容的表。

这种做法的目的是将所需的资源分配给承受最高流量的当前时间段，同时降低使用不活跃的旧表的预置资源，从而节省成本。根据业务需求，可能考虑写入分片，将流量均匀地分布到逻辑分区键。有关更多信息，请参阅 [在 DynamoDB 表中使用写入分片来均匀分配工作负载](bp-partition-key-sharding.md)。

## 时间序列表示例
<a name="bp-time-series-examples"></a>

下面是一个时间序列数据示例，当前表预置较高读取/写入容量，较早的表因为访问不频繁，将降低配置。

![\[大量时间序列数据的表架构。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/TimeSeries.png)


# 在 DynamoDB 表中管理多对多关系的最佳实践
<a name="bp-adjacency-graphs"></a>

相邻列表是一种在 Amazon DynamoDB 中建模多对多关系的设计模式。一般地说，它们提供在 DynamoDB 中表示图表数据（节点和边缘）的方式。

## 相邻列表设计模式
<a name="bp-adjacency-lists"></a>

如果应用程序的不同实体之间具有多对多关系，可以将关系建模为相邻列表。在此模式下，所有顶级实体（与图表模型中的节点同义）都使用分区键表示。将排序键值设置为目标实体 ID（目标节点），可以将与其他实体（图表中的边缘）的任何关系表示为分区内的项目。

此模式的优点包括数据重复率最低，简化查询模式查找与目标实体（边缘作为目标节点）相关的所有实体（节点）。

包含多个账单的开票系统是此模式的一个真实示例。一个账单可以属于多个发票。此示例中的分区键为 `InvoiceID` 或 `BillID`。`BillID` 分区的所有属性特定于账单。`InvoiceID` 分区的一个项目存储发票特定属性，一个项目保存汇总到发票的每个 `BillID`。

架构如下所示。

![\[记账相邻列表示例的表架构。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/AdjacencyLists_01.png)


从上述架构可以看到，可以使用表主键查询发票的所有账单。要查找包含一部分账单的所有发票，请对表的排序键创建全局二级索引。

全局二级索引的投影如下所示。

![\[记账相邻列表示例的 GSI 投影。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/AdjacencyLists_02.png)


## 具体化图表模式
<a name="bp-graph-pattern"></a>

很多应用程序建立的基础是在对跨对等排名、实体间关系、相邻实体状态以及其他类型图表样式工作流的了解。对于这些类型的应用程序，考虑下面架构设计模式。

![\[1 号图表示例。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/1513869910203-418.png)


![\[2 号图表示例。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/1513852802235-256.png)


![\[3 号图表示例。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/1513852905360-671.png)


上述架构显示的图表数据结构由一组数据分区定义，包含定义图表边缘和节点的项目。边缘项目包含 `Target` 和 `Type` 属性。这些属性用作复合键名称“TypeTarget”的一部分，标识主表分区或另一个全局二级索引中的项目。

第一个全局二级索引基于 `Data` 属性生成。此属性使用之前介绍的全局二级索引重载，为多个不同属性类型（即 `Dates`、`Names`、`Places` 和 `Skills`）编制索引。一个全局二级索引可以为四个不同属性有效编制索引。

将项目插入表时，可以使用智能分片策略，在全局二级索引上所需数量的逻辑分区之间，分配包含大型聚合（生日、技能）的项目集，避免热门读取/写入问题。

这种设计模式组合为高效实时图表工作流带来可靠数据存储。这些工作流可以提供高性能相邻实体状态和边缘聚合查询，用于建议引擎、社交网络应用程序、节点排名、子树聚合和其他常见图表使用案例。

如果使用案例对实时数据一致性不敏感，可以使用计划的 Amazon EMR 流程，用工作流相关图表摘要聚合填充边缘。如果将边缘添加到图表后，应用程序不需要立即知道，则可以使用计划流程聚合结果。

要保持一定程度的一致性，此设计可以加入 Amazon DynamoDB Streams 和 Amazon Lambda 以处理边缘更新。还可以定期使用 Amazon EMR 任务验证结果。下图说明此方法。常用于社交网络应用程序，实时查询成本高，对立刻知道各个用户更新的需求低。

![\[图表工作流的示意图。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/1513856345673-336.png)


IT 服务管理 (ITSM) 和安全应用程序通常需要实时响应包含复杂边缘聚合的实体状态更改。此类应用程序需要系统可以支持二级和三级关系的实时多个节点聚合或复杂边缘遍历。如果使用案例需要这类实时图表查询工作流，建议考虑使用 [Amazon Neptune](https://docs.amazonaws.cn/neptune/latest/userguide/) 管理工作流。

**注意**  
如果您需要查询高度连接的数据集，或者要执行需要以毫秒级延迟遍历多个节点的查询（也称为多跃点查询），则应考虑使用 [Amazon Neptune](https://docs.amazonaws.cn/neptune/latest/userguide/)。Amazon Neptune 是一个专门打造的高性能图形数据库引擎，它经过优化，可存储数十亿个关系并能以毫秒级延迟进行图形查询。

# 在 DynamoDB 中查询和扫描数据的最佳实践
<a name="bp-query-scan"></a>

本节介绍在 Amazon DynamoDB 中使用 `Query` 和 `Scan` 操作的一些最佳实践。

## 扫描性能注意事项
<a name="bp-query-scan-performance"></a>

通常 `Scan` 操作效率低于 DynamoDB 中的其他操作。`Scan` 操作始终扫描整个表或二级索引，然后加入从结果集移除数据的步骤，筛选值以提供所需的结果。

如果可行，应避免对大型表或索引使用通过筛选器移除大量结果的 `Scan` 操作。此外，随着表或索引的增长，`Scan` 操作速度减慢。`Scan` 操作检查每个项目的请求值，可以在单个操作中用完大型表或索引的预调配吞吐量。要缩短响应时间，请设计表和索引，使应用程序可以使用 `Query` 而不是 `Scan`。（对于表，还可以考虑使用 `GetItem` 和 `BatchGetItem` API。）

或者，您可以设计应用程序，以尽可能降低对请求速率的影响的方式使用 `Scan` 操作。这可能包括在使用全局二级索引（而不是 `Scan` 操作）可能更高效的情况下进行建模。有关此过程的更多信息，请参阅以下视频。

## 避免读取操作的突然峰值
<a name="bp-query-scan-spikes"></a>

创建表时，设置读取和写入容量单位要求。对于读取，容量单位表示为每秒的强一致性 4KB 数据读取请求数量。对于最终一致性读取，一个读取容量单位为每秒两个 4KB 读取请求。`Scan` 操作默认执行最终一致性读取，最多可以返回 1 MB（一页）数据。因此，单个 `Scan` 请求可以占用（1 MB 页面大小/4KB 项目大小）/2（最终一致性读取）= 128 个读取操作。如果请求强一致性读取，则 `Scan` 操作将占用两倍预调配吞吐量 – 256 个读取操作。

这说明相比为表配置的读取容量，使用量突然猛增。扫描的这种容量单位使用方式，阻止对同一表的其他潜在更重要请求使用可用容量单位。因此此类请求可能将得到 `ProvisionedThroughputExceeded` 异常。

问题不仅在于 `Scan` 使用的容量单位突然增加，还可能占用同一分区的所有容量单位，因为扫描请求读取分区上彼此相邻的项目。这意味着请求命中同一分区，导致其所有容量单位被占用，限制对该分区的其他请求。如果读取数据的请求分布在多个分区，则操作不会限制特定分区。

下图说明 `Query` 和 `Scan` 操作使用的容量单位突然峰值造成的影响，以及对同一表的其他请求的影响。

![\[4 种不同场景，显示表的预调配吞吐量间隔、请求以及好坏结果。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/ThroughputIntervals.png)


如下所示，使用峰值可以通过以下几种方式影响表的预调配吞吐量：

1. 良好：均匀分布请求和大小

1. 不够好：频繁请求突发

1. 坏：少数随机大请求

1. 坏：大型扫描操作

可以用以下技术代替大型 `Scan` 操作，尽可能减小扫描对表预调配吞吐量的影响。
+ **减小页面大小**

  Scan 操作读取整个页面（默认 1 MB），可以设置更小的页面大小，减小扫描操作的影响。`Scan` 操作提供一个*限制*参数，可以用于设置请求的页面大小。每个具有更小页面大小的 `Query` 或 `Scan` 请求使用更少读取操作，在每个请求之间产生“暂停”。例如，假设每个项目为 4 KB，将页面大小设置为 40 个项目。`Query` 请求将只占用 20 个最终一致性读取操作或 40 个强一致性读取操作。更多的较小 `Query` 或 `Scan` 操作将允许其他关键请求成功执行，不受限制。
+ **隔离扫描操作**

  DynamoDB 专为轻松扩展而设计。因此，应用程序可以为不同目的创建表，甚至在多个表中重复内容。您希望在表上执行不占用“关键任务”流量的扫描。一些应用程序在两个表之间每小时轮换流量来处理此负载 – 一个用于关键流量，另一个用于记账。其他应用程序在两个表上执行每次写入：“任务关键型”表和“影子”表。

配置应用程序，如果请求接收的响应代码指示超出预调配吞吐量，则重试。或者，使用 `UpdateTable` 操作增加表的预调配吞吐量。如果工作负载临时峰值导致吞吐量偶尔超过预配量，则用指数回退重试请求。有关实施指数回退的更多信息，请参阅 [错误重试和指数回退](Programming.Errors.md#Programming.Errors.RetryAndBackoff)。

## 利用并行扫描
<a name="bp-query-scan-parallel"></a>

相比顺序扫描，许多应用程序都可以从使用并行 `Scan` 操作获益。例如，处理大型历史数据表的应用程序执行并行扫描的速度比顺序扫描要快得多。后台“sweeper”进程的多个工作线程以低优先级扫描表，不影响生产流量。在这些示例中，使用并行 `Scan`，不占用其他应用程序的预调配吞吐量资源。

虽然并行扫描有自己的优势，但对预调配吞吐量要求很高。使用并行扫描时，应用程序的多个工作线程同时运行 `Scan` 操作，快速消耗表的所有预置读取容量。在此情况下，需要访问表的其他应用程序可能会受到限制。

如果满足以下条件，可以选择并行扫描：
+ 表大小为 20 GB 或更大。
+ 未充分利用表的预置读取吞吐量。
+ 顺序 `Scan` 操作太慢。

### 选择 TotalSegments
<a name="bp-query-scan-parallel-total-segments"></a>

`TotalSegments` 的最佳设置取决于具体数据、表的预调配吞吐量设置以及性能要求。可能需要实验来得出正确设置。我们建议从简单比值开始，如每 2 GB 数据一个段。例如，对于 30 GB 表，可以将 `TotalSegments` 设置为 15（30 GB/2 GB）。应用程序将使用 15 个工作线程，每个工作线程扫描一个不同的段。

还可以根据客户端资源选择 `TotalSegments` 值。可以将 `TotalSegments` 设置为 1 到 1000000 之间的任意数字，DynamoDB 支持扫描这个数量的段。例如，如果客户端限制可同时运行的线程数量，可以逐渐增加 `TotalSegments` 直到应用程序达到最佳 `Scan` 性能。

监测并行扫描以优化预调配吞吐量使用，同时确保其他应用程序不会耗尽资源。如果没有占用所有预置吞吐量，但 `Scan` 请求仍遇到限制，增加 `TotalSegments` 值。如果 `Scan` 请求占用的预调配吞吐量超过要使用的量，减少 `TotalSegments` 值。

# DynamoDB 表设计的最佳实践
<a name="bp-table-design"></a>

Amazon DynamoDB 的一般设计准则建议尽可能少使用表格。在大多数情况下，建议您考虑使用单个表。但是，如果单个表或少量表不可行，则这些准则可能会有用。
+ 每账户限制不能增加到每个账户 10000 个表以上。如果您的应用程序需要更多的表，请计划在多个账户间分配这些表。有关更多信息，请参阅 [Amazon DynamoDB 中的服务、账户和表限额](ServiceQuotas.html#limits-tables)。
+ 考虑可能影响表管理的并发控制面板操作的控制面板限制。
+ 与 Amazon 解决方案架构师合作，验证您的多租户设计的设计模式。

# 使用 DynamoDB 全局表
<a name="bp-global-table-design"></a>

全局表基于遍布全球的 Amazon DynamoDB 而构建，可提供完全托管式、多区域和多活动数据库，为大规模的全球应用程序提供高速的本地读写性能。全局表可在您选择的 Amazon Web Services 区域 自动复制 DynamoDB 表。全局表使用现有的 DynamoDB API，因此无需更改应用程序。使用全局表无需支付任何预付费用，也没有任何承诺，您只需为实际使用的资源付费。

本指南将介绍如何有效使用 DynamoDB 全局表。它提供有关全局表的关键事实，解释该功能的主要使用案例，描述两个一致性模式，介绍您应考虑的三种不同写入模型的分类法，引导您了解可以实施的四种主要请求路由选择，讨论撤离处于活动状态的区域或离线区域的方法，解释如何考虑吞吐能力规划，并提供了部署全局表时需要考虑的事项核对清单。

本指南适用于更广泛的 Amazon 多区域部署背景，如 [Amazon 多区域基础知识](https://docs.amazonaws.cn/prescriptive-guidance/latest/aws-multi-region-fundamentals/introduction.html)白皮书和 [Data resiliency design patterns with Amazon](https://www.youtube.com/watch?v=7IA48SOX20c) 视频中所述。

**Topics**
+ [关于 DynamoDB 全局表设计的关键事实](#bp-global-table-design.prescriptive-guidance.facts)
+ [关于 MREC 的关键事实](#bp-global-table-design-MREC-facts)
+ [关于 MRSC 的关键事实](#bp-global-table-design-MRSC-facts)
+ [MREC DynamoDB 全局表使用案例](#bp-global-table-design.prescriptive-guidance.usecases)
+ [使用 DynamoDB 全局表的写入模式](bp-global-table-design.prescriptive-guidance.writemodes.md)
+ [DynamoDB 中的路由策略](bp-global-table-design.prescriptive-guidance.request-routing.md)
+ [撤离过程](bp-global-table-design.prescriptive-guidance.evacuation.md)
+ [DynamoDB 全局表的吞吐能力规划](bp-global-table-design.prescriptive-guidance.throughput.md)
+ [DynamoDB 全局表的准备核对清单](bp-global-table-design.prescriptive-guidance.checklist-and-faq.md)
+ [结论和资源](#bp-global-table-design.prescriptive-guidance-resources-conclusion)

## 关于 DynamoDB 全局表设计的关键事实
<a name="bp-global-table-design.prescriptive-guidance.facts"></a>
+ 全局表有两个版本：当前版本[全局表版本 2019.11.21（当前版）](GlobalTables.md)（有时称为“V2”）和 [全局表版本 2017.11.29（旧版）](globaltables.V1.md)（有时称为“V1”）。本指南仅关注当前版本。
+ DynamoDB（不含全局表）是一项区域服务，这意味着它具有高可用性，且对基础设施故障具有内在的弹性，包括整个可用区的故障。单区域 DynamoDB 表设计为具有 99.99% 的可用性。有关更多信息，请参阅 [DynamoDB 服务水平协议（SLA）](https://www.amazonaws.cn/dynamodb/sla/)。
+ DynamoDB 全局表在两个或更多区域间复制其数据。多区域 DynamoDB 表设计为具有 99.999% 的可用性。通过适当的规划，全局表可以帮助创建一个可抵御区域故障的架构。
+ DynamoDB 没有全局端点。所有请求都向区域端点发出，然后该端点访问该区域本地的全局表实例。
+ 对 DynamoDB 的调用不应跨区域进行。最佳实践是，位于某个区域的应用程序应仅直接访问其所在区域的本地 DynamoDB 端点。如果在某个区域（在 DynamoDB 层或在周围堆栈中）内检测到问题，则应将最终用户流量路由到托管在不同区域中的不同应用程序端点。全局表可确保驻留在每个区域的应用程序都能访问相同的数据。

### 一致性模式
<a name="bp-global-table-design-prescriptive-guidance-consistency"></a>

创建全局表时，您应配置其一致性模式。全局表支持两种一致性模式：多区域最终一致性（MREC）和多区域强一致性（MRSC），后者于 2025 年 6 月推出。

如果您在创建全局表时未指定一致性模式，则全局表默认为 MREC。全局表不能包含配置了不同一致性模式的副本。创建全局表后，无法更改其一致性模式。

## 关于 MREC 的关键事实
<a name="bp-global-table-design-MREC-facts"></a>
+ 使用 MRSC 的全局表也采用主动-主动复制模型。从 DynamoDB 的角度来看，每个区域中的表在接受读取和写入请求方面具有同等地位。收到写入请求后，本地副本表会在后台将写入操作复制到其他参与远程区域。
+ 项目是单独复制的。在单个事务内更新的项目不能一起复制。
+ 源区域中的每个表分区都会与每个其他分区并行复制其写入操作。远程区域内的写入操作顺序可能与源区域内发生的写入操作顺序不匹配。有关表分区的更多信息，请参阅博客文章 [Scaling DynamoDB: How partitions, hot keys, and split for heat impact performance](https://www.amazonaws.cn/blogs/database/part-3-scaling-dynamodb-how-partitions-hot-keys-and-split-for-heat-impact-performance/)。
+ 新写入的项目通常会在一秒内传播到所有副本表。附近区域的传播速度往往更快。
+ Amazon CloudWatch 为每个区域对提供一个 `ReplicationLatency` 指标。它的计算方法是查看到达的项目，将它们的到达时间与其初始写入时间进行比较并计算平均值。时间存储在源区域的 CloudWatch 中。查看平均时间和最大时间对于确定平均和最坏情况下的复制滞后非常有用。对于这种延迟，没有 SLA。
+ 如果大约在同一时间（在此 `ReplicationLatency` 时段内）在两个不同的区域更新单个项目，并且第二次写入操作发生在复制第一次写入操作之前，则可能会出现写入冲突。使用 MREC 的全局表会基于写入操作时间戳，采用以最后写入者为准的机制来解决此类冲突。第一个操作“输给了”第二个操作。这些冲突不会记录在 CloudWatch 或 Amazon CloudTrail 中。
+ 每个项目都有最后一次写入时间戳，保留为一个私有系统属性。以最后写入者为准方法是通过使用条件写入操作来实现的，条件写入操作要求传入项目的时间戳大于现有项目的时间戳。
+ 全局表会将所有项目复制到所有参与区域。如果您想拥有不同的复制范围，可以创建多个全局表，并为每个表分配不同的参与区域。
+ 即使副本区域处于离线状态或 `ReplicationLatency` 增长，本地区域也会接受写入操作。本地表继续尝试将项目复制到远程表，直到每个项目成功为止。
+ 在极少数情况下，如果某个区域完全离线，则当该区域稍后恢复在线时，将重试所有待处理的出站和入站复制。无需特殊操作即可使表恢复同步。*以最后写入者为准*机制可确保数据最终实现一致性。
+ 您可以随时向 DynamoDB MREC 表添加新区域。DynamoDB 将处理初始同步和持续复制。您也可以删除区域（甚至是原始区域），这将删除该区域中的本地表。

## 关于 MRSC 的关键事实
<a name="bp-global-table-design-MRSC-facts"></a>
+ 使用 MRSC 的全局表也采用主动-主动复制模型。从 DynamoDB 的角度来看，每个区域中的表在接受读取和写入请求方面具有同等地位。在写入操作返回成功响应之前，MRSC 全局表副本中的项目更改会**同步**复制到至少一个其他区域。
+ 对任何 MRSC 副本执行的强一致性读取操作始终返回项目的最新版本。条件写入操作始终根据项目的最新版本来评估条件表达式。更新始终根据项目的最新版本进行操作。
+ MRSC 副本上的最终一致性读取操作可能不包括最近在另一个区域发生的更改，甚至可能不包括最近在同一区域发生的更改。
+ 当写入操作尝试修改已在另一个区域中处于正在修改状态的项目时，此操作将失败并引发 `ReplicatedWriteConflictException` 异常。可以重试失败并引发了 `ReplicatedWriteConflictException` 异常的写入操作，如果该项目不再在另一个区域中处于正在修改状态，则写入操作将成功。
+ 使用 MRSC，写入操作和强一致性读取操作的延迟会更高。这些操作需要跨区域通信。此通信会增加延迟，延迟会根据正在访问的区域与加入全局表的最近区域之间的往返延迟而增加。有关更多信息，请参阅 Amazon re:Invent 2024 演示：[Multi-Region strong consistency with DynamoDB global tables](https://www.youtube.com/watch?v=R-nTs8ZD8mA)。最终一致性读取操作不会出现额外的延迟。您可以使用开源[测试工具](https://github.com/awslabs/amazon-dynamodb-tools/tree/main/tester)，通过实验计算区域的这些延迟。
+ 项目是单独复制的。使用 MRSC 的全局表不支持事务 API。
+ MRSC 全局表必须部署在恰好三个区域中。您可以将 MRSC 全局表配置为具有三个副本或具有两个副本和一个见证者。见证者是 MRSC 全局表的一个组成部分，其中包含写入全局表副本的最新数据。见证者为完整副本提供了一种可选的替代方案，同时支持 MRSC 的可用性架构。您无法对见证者执行读取或写入操作。见证者不会产生存储或写入成本。见证者位于与两个副本不同的区域内。
+ 要创建 MRSC 全局表，您可以向不包含任何数据的现有 DynamoDB 表添加一个副本和一个见证者，或者添加两个副本。您无法向现有 MRSC 全局表添加其它副本。您无法从 MRSC 全局表中删除单个副本或见证者。您可以从 MRSC 全局表中删除两个副本，或者删除一个副本和一个见证者。第二种场景将剩余的副本转换为单区域 DynamoDB 表。
+ 您可以从 DescribeTable API 的输出中，确定 MRSC 全局表是否配置了见证者以及在哪个区域中配置了见证者。见证者由 DynamoDB 拥有和管理，并且在配置了见证者的区域中，见证者不会出现在您的 Amazon Web Services 账户中。
+ MRSC 全局表在以下区域集中可用：
  + 美国区域集：美国东部（弗吉尼亚州北部）、美国东部（俄亥俄州）、美国西部（俄勒冈州）
  + 欧洲区域集：欧洲地区（爱尔兰）、欧洲地区（伦敦）、欧洲地区（巴黎）、欧洲地区（法兰克福）
  + 亚太地区区域集：亚太地区（东京）、亚太地区（首尔）和亚太地区（大阪）
+ MRSC 全局表不能跨越区域集。例如，MRSC 全局表不能同时包含来自美国区域集和欧盟区域集的副本。
+ MRSC 全局表不支持生存时间（TTL）。
+ MRSC 全局表不支持本地二级索引（LSI）。
+ CloudWatch Contributor Insights 信息仅针对发生操作的区域进行报告。
+ 只要托管副本或见证者的第二个区域可用于建立仲裁，本地区域就会接受所有读取和写入操作。如果第二个区域不可用，则本地区域只能为最终一致性读取提供服务。
+ 在极少数情况下，如果某个区域完全离线，那么当它稍后重新上线时，会自动赶上进度。在它赶上进度之前，*只有*对正在赶进度的区域执行的写入操作和强一致性读取操作会返回错误，而对其他区域的请求将继续正常执行。对正在赶进度的区域执行的最终一致性读取操作将返回到目前为止已传播到区域中的数据，并且领导节点和本地副本之间具有通常的本地一致性行为。无需特殊操作即可使表恢复同步。

## MREC DynamoDB 全局表使用案例
<a name="bp-global-table-design.prescriptive-guidance.usecases"></a>

MREC 全局表提供以下好处：
+  **低延迟读取操作。**将数据的副本放在离最终用户更近的位置，以减少读取操作期间的网络延迟。数据与 `ReplicationLatency` 值一样保持最新。
+  **低延迟写入操作。**您可以写入附近的区域以减少网络延迟和完成写入所需的时间。必须谨慎路由写入流量，以确保没有冲突。[DynamoDB 中的路由策略](bp-global-table-design.prescriptive-guidance.request-routing.md)中详述了路由技术。
+ **无缝的区域迁移。**您可以添加新区域并删除旧区域，以便将部署从一个区域迁移到另一个区域，而且数据层不会出现任何停机时间。

MREC 和 MRSC 全局表都提供了以下好处：
+  **改善了弹性和灾难恢复。**如果某个区域性能下降或完全中断，您可以将其撤离。撤离意味着将发送到该区域的部分或全部请求转走。使用全局表可将 [DynamoDB SLA](https://www.amazonaws.cn/dynamodb/sla/) 的月度正常运行时间百分比从 99.99% 提高到 99.999%。使用 MREC 支持以秒为单位的恢复点目标（RPO）和恢复时间目标（RTO）。使用 MRSC 支持零 RPO。

  例如，Fidelity Investments 在 re:Invent 2022 大会上介绍了他们如何为其订单管理系统使用 DynamoDB 全局表。他们的目标是在大规模处理中，实现本地处理所无法实现的可靠低延迟，同时还在可用区和区域出现故障时保持韧性。

如果您的目标是韧性和灾难恢复，则 MRSC 表具有更高的写入延迟和更高的强一致性读取延迟，而且支持零 RPO。MREC 全局表支持与副本间的复制延迟相等的 RPO，通常为几秒，具体取决于副本所在的区域。

# 使用 DynamoDB 全局表的写入模式
<a name="bp-global-table-design.prescriptive-guidance.writemodes"></a>

全局表在表级别始终处于主动-主动状态。但是，特别是对于 MREC 表，您可能希望通过控制您路由写入请求的方式来将它们作为主动-被动进行处理。例如，您可能决定将写入请求路由到单个区域，以避免 MREC 表可能发生的潜在写入冲突。

有三种主要的托管式写入模式，如以下三节所述。您应该考虑哪种写入模式适合您的使用案例。此选择会影响您路由请求、撤离区域和处理灾难恢复的方式。后面章节中的指引取决于应用程序的写入模式。

## 写入任何区域模式（非主模式）
<a name="bp-global-table-design.prescriptive-guidance.writemodes.no-primary"></a>

如下图所示，*写入任何区域*模式处于完全主动-主动状态，不会对可能发生写入的位置施加限制。任何区域都可以随时接受写入。这是最简单的模式，但只能用于某些类型的应用程序。此模式适用于所有 MRSC 表。当所有写入器都是幂等的，因此可以安全地重复，使得跨区域的并发或重复的写入操作不会发生冲突时，此模式也适用于 MREC 表。例如，当用户更新其联系人数据时。此模式也适用于一种特殊的幂等情况，即仅限追加的数据集，其中所有写入操作都是确定性主键下的唯一插入。最后，在可以接受写入冲突风险时，此模式适用于 MREC。

![\[客户端写入任何区域的工作原理图。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/gt-client-read-write-to-any-region2.png)


*写入任何区域*模式是实现起来最简单的架构。路由更容易，因为任何区域都可以随时成为写入目标。失效转移更容易，因为使用 MRSC 表可以始终同步项目，而且使用 MRSC 表，任何最近的写入都可以对任何辅助区域重播任意次。在可能的情况下，您应该针对这种写入模式进行设计。

例如，一些视频流媒体服务使用全局表来跟踪书签、评论、观看状态标志等。这些部署之所以使用 MREC 表，是因为它们需要分散在世界各地的副本，每个副本都提供低延迟的读取和写入操作。这些部署可以使用*写入任何区域*模式，只要确保每个写入操作都是幂等的即可。如果每次更新（例如设置新的最新时间码、分配新的评论或设置新的观看状态）都会直接分配用户的新状态，且项目的下一个正确值不依赖于当前值，就会出现这种情况。如果将用户的写入请求碰巧路由到不同的区域，则最后一次写入操作将持续存在，全局状态将根据最后一次分配而定。在此模式下，读取操作最终会变得一致，但会因最新的 `ReplicationLatency` 值而延迟。

在另一个例子中，一家金融服务公司使用全局表作为系统的一部分，以持续统计每位客户的借记卡购买情况，从而计算该客户的现金返还奖励。他们希望每位客户都能保留一个 `RunningBalance` 项目。此写入模式本身并不是幂等的，因为随着事务的流入，它们会使用 `ADD` 表达式来修改余额，而新的正确值取决于当前值。通过使用 MRSC 表，他们仍然可以*写入任何区域*，因为每次 `ADD` 调用总是针对项目的最新值进行操作。

第三个示例涉及一家提供在线广告投放服务的公司。该公司已经决定，为了简化*写入任何区域*模式的设计，可以接受较低的数据丢失风险。在投放广告时，公司只有几毫秒的时间检索足够的元数据来确定要展示的广告，然后记录广告展示，以免很快重复展示同一广告。他们使用全局表，为全世界的最终用户实现低延迟的读取操作和低延迟的写入操作。他们在单个项目内记录用户的所有广告展示，并以不断增长的列表形式呈现。他们使用一个项目而不是追加到项目集合中，以便可以在每次写入操作中删除较旧的广告展示，而无需支付删除操作的费用。此写入操作不是幂等的；如果同一个最终用户在大概同一时间看到来自多个区域的广告，则广告展示的一次写入操作可能会覆盖另一次写入操作。风险在于，用户可能会偶尔看到重复的广告。他们认为这是可接受的。

## 写入一个区域（单主模式）
<a name="bp-global-table-design.prescriptive-guidance.writemodes.single-primary"></a>

如下图所示，*写入一个区域*模式是主动-被动模式，它将所有表写入操作都路由到单个主动区域。请注意，DynamoDB 没有单一主动区域的概念；DynamoDB 外部的应用程序路由对此进行管理。对于需要避免写入冲突的 MREC 表，*写入一个区域*模式可以很好地确保写入操作一次只流向一个区域。当您想使用条件表达式但由于某种原因无法使用 MRSC 时，或者当您需要执行事务时，此写入模式会有所帮助。除非您知道自己是在针对最新数据执行操作，否则这些表达式是不可能的，因此它们需要将所有写入请求发送到具有最新数据的单个区域。

使用 MRSC 表时，为了方便起见，通常可以选择写入一个区域。例如，这有助于最大限度地减少您在 DynamoDB 之外的基础设施构建。写入模式仍将写入任何区域，因为使用 MRSC，您可以随时安全地写入任何区域，而不必担心会导致 MREC 表选择*写入一个区域*的冲突解决方案。

最终一致性读取可以进入任何副本区域以降低延迟。强一致性读取必须进入单个主区域。

![\[写入一个区域的工作原理示意图。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/gt-client-writes-one-region2.png)


有时需要更改主动区域来应对区域故障。一些用户会定期更改当前主动区域，例如实施“跟随太阳”部署。这会将主动区域放置在活动最多的地理位置附近（通常是白天，因此得名），从而实现最低的读取和写入延迟操作。它还有一个附带好处，那就是每天调用区域不断变化的代码，确保在进行任何灾难恢复之前都经过良好的测试。

被动区域可能会在 DynamoDB 周围保留一组缩小规模的基础设施，而只有当被动区域成为主动区域时，此类基础设施才会建立起来。本指南不涵盖 pilot light 和暖备用设计。有关更多信息，请参阅《[Disaster Recovery (DR) Architecture on Amazon, Part III: Pilot Light and Warm Standby](https://www.amazonaws.cn/blogs/architecture/disaster-recovery-dr-architecture-on-aws-part-iii-pilot-light-and-warm-standby/)》。

在使用全局表进行低延迟的全局分布式读取操作时，使用*写入一个区域*模式效果很好。例如，一家大型社交媒体公司需要在全球每个区域提供相同的参考数据。他们不经常更新数据，但在更新数据时，他们只写入一个区域，以避免任何潜在的写入冲突。任何区域都始终允许进行读取操作。

再举一个例子，考虑一下前面提到的那家实施了每日现金返还计算的金融服务公司。其使用*写入任何区域*模式来计算余额，但使用*写入一个区域*模式来跟踪付款。这项工作需要处理事务，而 MRSC 表不支持这些事务，因此使用单独的 MREC 表和*写入一个区域*模式时效果更好。

## 写入您的区域（混合主模式）
<a name="bp-global-table-design.prescriptive-guidance.writemodes.mixed-primary"></a>

下图所示的*写入您的区域*写入模式适用于 MREC 表。它将不同的数据子集分配给不同的主区域，并且仅允许通过其主区域对项目进行写入操作。此模式为主动-被动模式，但会根据项目分配主动区域。每个区域都是其自己的非重叠数据集的主区域，必须保护写入操作以确保位置正确。

此模式与*写入一个区域*类似，不同之处在于它支持低延迟写入操作，因为与每个最终用户关联的数据可以放在离该用户更近的网络上。此模式还可以在各区域之间更均匀地分布周围的基础设施，并且在失效转移方案中构建基础设施所需的工作量更少，因为所有区域的基础设施都有一部分已经处于主动状态。

![\[有关客户端如何写入到单个区域中的每个项目的工作原理图。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/get-client-writes-each-item-single-region2.png)


您可以通过多种方式确定项目的主区域：
+  **内置函数**：数据的某些方面（例如特殊属性或嵌入在其分区键内的值）可明确其主区域。博客文章 [Use Region pinning to set a home Region for items in an Amazon DynamoDB global table](https://www.amazonaws.cn/blogs/database/use-region-pinning-to-set-a-home-region-for-items-in-an-amazon-dynamodb-global-table/) 中介绍了此技术。
+  **已协商：**通过某种外部方式协商每个数据集的主区域，例如与维护分配的单独全局服务进行协商。分配可能有一个有限的期限，在此之后需要重新协商。
+  **面向表：**与其创建单个复制全局表，不如创建与复制区域数量相同的全局表。每个表的名称都指示其主区域。在标准操作中，所有数据都写入主区域，而其他区域则保留只读副本。在失效转移期间，另一个区域临时承担对该表的写入职责。

例如，假设您在一家游戏公司工作。您需要为全世界的所有游戏玩家提供低延迟的读取和写入操作。您将每位游戏玩家分配到离其最近的区域。该区域会承担他们所有的读取和写入操作，从而确保很强的写入后读取一致性。但是，如果该游戏玩家外出旅行或其主区域发生中断，则其数据的完整副本将在备用区域中可用，游戏玩家可以分配到不同的主区域。

再举一个例子，假设您在一家视频会议公司工作。每次电话会议的元数据都会分配到一个特定的区域。呼叫者可以使用离他们最近的区域以实现最低延迟。如果出现区域中断，使用全局表可以快速恢复，因为系统可以将呼叫处理转移到已经有数据复制副本的其他区域。

**总结**
+ “写入任何区域”模式适用于 MRSC 表以及对 MREC 表的幂等调用。
+ “写入一个区域”模式适用于对 MREC 表的非幂等调用。
+ “写入您的区域”模式适用于在让客户端写入离其较近的区域非常重要时，对 MREC 表的非幂等调用。

# DynamoDB 中的路由策略
<a name="bp-global-table-design.prescriptive-guidance.request-routing"></a>

或许全局表部署中最复杂的部分是管理请求路由。请求必须首先从终端用户发送到以某种方式选择和路由的区域。该请求会遇到该区域中的一些服务堆栈，包括一个计算层 [可能由 Amazon Lambda 函数支持的负载均衡器、容器或 Amazon Elastic Compute Cloud（Amazon EC2）节点组成]，以及可能包括其他数据库在内的其他服务。该计算层与 DynamoDB 通信。它应该通过使用该区域的本地端点来实现。全局表中的数据会复制到所有其他参与区域，并且每个区域在其 DynamoDB 表周围都有类似的服务堆栈。

 全局表为不同区域中的每个堆栈提供具有相同数据的本地副本。如果本地 DynamoDB 表出现问题，您可以考虑在单个区域中设计单个堆栈，并预计会远程调用辅助区域的 DynamoDB 端点。这不是最佳实践。如果一个区域存在由 DynamoDB 引起的问题（或者更有可能是由堆栈中的其他内容或其他依赖于 DynamoDB 的服务造成的），则最好将最终用户路由到另一个区域进行处理，然后使用另一个区域的计算层，该计算层将与其本地 DynamoDB 端点通信。此方法完全绕过了有问题的区域。为了确保韧性，您需要跨多个区域进行复制：计算层以及数据层的复制。

 有许多替代技术可以将终端用户请求路由到区域进行处理。最佳选择取决于您的写入模式和失效转移注意事项。本节讨论四个选项：客户端驱动、计算层、Route 53 和 Global Accelerator。

## 客户端驱动的请求路由
<a name="bp-global-table-design.prescriptive-guidance.request-routing.client-driven"></a>

如下图所示，使用客户端驱动的请求路由，最终用户客户端（应用程序、使用 JavaScript 的网页或其他客户端）可以跟踪有效的应用程序端点（例如，Amazon API Gateway 端点，而不是字面上的 DynamoDB 端点），并使用自己的嵌入式逻辑来选择要与之通信的区域。该客户端可以根据随机选择、观测到的最低延迟、观测到的最大带宽测量值或本地执行的运行状况检查来进行选择。

![\[向客户选择的目标进行写入的工作原理图。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/gt-routing-is-clients-choice2_v2.png)


客户端驱动的请求路由的优势在于，它可以适应现实世界中的公共互联网流量状况等情况，以便在发现任何性能下降时切换区域。客户端必须知道所有潜在的端点，但启动新的区域端点并不常见。

通过*写入任何区域*模式，客户端可以单方面选择其首选端点。如果客户端对一个区域的访问受到妨碍，则客户端可以路由到另一个端点。

在*写入一个区域*模式下，客户端将需要一种机制来将其写入操作路由到当前主动区域。这可能像凭经验测试哪个区域当前正在接受写入一样基本（注意任何写入拒绝并回退到备用区域），也可能像调用全局协调器来查询当前应用程序状态一样复杂 [可能建立在 Amazon 应用程序恢复控制器（ARC）路由控制之上，该路由控制提供 5 区域仲裁驱动系统来维护全局状态以满足此类需求]。客户端可以决定读取是可以转到任何区域以实现最终一致性，还是必须路由到主动区域以实现强一致性。有关更多信息，请参阅 [Route 53 的工作原理](https://docs.amazonaws.cn/r53recovery/latest/dg/introduction-how-it-works.html)。

 在*写入您的区域*模式下，客户端需要确定它正在处理的数据集的主区域。例如，如果客户端对应于一个用户账户，并且每个用户账户都有一个区域作为主区域，则客户端可以从全局登录系统请求相应的端点。

 例如，一家通过网络帮助用户管理业务财务的金融服务公司可以使用全局表以及*写入您的区域*模式。每个用户都必须登录中央服务。该服务返回凭证以及将使用这些凭证的区域的端点。凭证在短时间内有效。之后，网页会自动协商新的登录信息，这提供了可能将用户的活动重定向到新区域的机会。

## 计算层请求路由
<a name="bp-global-table-design.prescriptive-guidance.request-routing.compute"></a>

如下图所示，通过计算层请求路由，在计算层中运行的代码决定是要在本地处理请求，还是将请求传递给在另一个区域运行的其自身的副本。当您使用*写入一个区域*模式时，计算层可能会检测到该区域不是主动区域，并允许本地读取操作，而将所有写入操作转发到另一个区域。此计算层代码必须了解数据拓扑和路由规则，并根据最新设置（用于指定哪些区域对于哪些数据为主动状态）可靠地执行这些规则。区域内的外部软件堆栈不必知道微服务是如何路由读取和写入请求的。在稳健的设计中，接收区域会验证它是否为写入操作的当前主区域。如果不是，则会生成一个错误，表明需要更正全局状态。如果主区域处于更改过程中，则接收区域也可能将写入操作缓冲一段时间。在所有情况下，区域中的计算堆栈仅写入其本地 DynamoDB 端点，但计算堆栈可能会相互通信。

![\[计算层请求路由图。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/gt-compute-layer-routing2.png)


正如 [re:Invent 2022](https://www.youtube.com/watch?v=ilgpzlE7Hds&t=1882s) 大会上所介绍的，Vanguard Group 采用了一个名为 Global Orchestration and Status Tool（GOaST）的系统和一个名为 Global Multi-Region library（GMRlib）的库，来实现此路由过程。它们使用的是“跟随太阳”式单一主区域模式。GOaST 负责维护全局状态，其作用与上一节中讨论的 ARC 路由控制类似。它使用全局表来跟踪哪个区域是主区域，以及何时安排下一次主区域切换。所有读取和写入操作都通过 GMRlib 进行，而 GMRlib 会与 GOaST 进行协调。GMRlib 允许在本地以低延迟执行读取操作。对于写入操作，GMRlib 会检查本地区域是否为当前主区域。如果是，则写入操作将直接完成。否则，GMRlib 会将写入任务转发到主区域中的 GMRlib。该接收库确认它也将自己视为主区域，如果不是，则会引发错误，这表明全局状态存在传播延迟。这种方法不直接写入远程 DynamoDB 端点，从而提供了验证方面的好处。

## Route 53 请求路由
<a name="bp-global-table-design.prescriptive-guidance.request-routing.r53"></a>

Amazon 应用程序恢复控制器（ARC）是一种域名服务（DNS）技术。使用 Route 53 时，客户端通过查找众所周知的 DNS 域名来请求其端点，Route 53 返回与其认为最合适的区域端点相对应的 IP 地址。此过程如下图所示。Route 53 具有一个很长路由策略列表，用于确定适当的区域。还可以用于失效转移路由，以将流量路由出运行状况检查失败的区域。

![\[计算层请求路由图。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/gt-rt-53-anycast2_v2.png)


通过*写入任何区域*模式，或者与后端的计算层请求路由结合使用，可以向 Route 53 授予完全访问权限，以根据任何复杂的内部规则（例如，最接近网络中的区域、最接近的地理位置中的区域或任何其他选择）返回区域。

通过*写入一个区域*模式，您可以将 Route 53 配置为返回当前主动区域（使用 Route 53 ARC）。如果客户端想要连接到被动区域（例如，用于读取操作），则可以查找不同的 DNS 名称。

**注意**  
客户端缓存来自 Route 53 的响应中的 IP 地址，缓存时间由域名上的生存时间（TTL）设置指示。较长的 TTL 可延长所有客户端识别新端点的恢复时间目标（RTO）。60 秒的值通常用于失效转移。并非所有软件都能完全遵守 DNS TTL 到期，且可能存在多个级别的 DNS 缓存，例如在操作系统、虚拟机和应用程序中。

在*写入您的区域*模式下，除非您还使用计算层请求路由，否则最好避开 Route 53。

## Global Accelerator 请求路由
<a name="bp-global-table-design.prescriptive-guidance.request-routing.gax"></a>

如下图所示，借助 [Amazon Global Accelerator](https://www.amazonaws.cn/global-accelerator/)，客户端可以在 Route 53 中查找众所周知的域名。然而，客户端接收的是路由到最近 Amazon 边缘站点的任播静态 IP 地址，而不是获取与区域端点对应的 IP 地址。从该边缘站点开始，所有流量都会路由到私有 Amazon 网络上的某个端点（例如负载均衡器或 API Gateway），而该端点所在区域由在 Global Accelerator 中维护的路由规则选择。与基于 Route 53 规则的路由相比，Global Accelerator 请求路由的延迟较低，因为它减少了公共互联网上的流量。此外，由于 Global Accelerator 不依赖于 DNS TTL 到期来更改路由规则，因此它可以更快地调整路由。

![\[使用 Global Accelerator 进行客户端写入的工作原理图。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/gt-routing-gax-excerpt2_v2.png)


 通过*写入任何区域*模式，或者在后端与计算层请求路由结合使用，Global Accelerator 可以无缝运行。客户端连接到最近的边缘站点，无需关心哪个区域会收到请求。

 使用*写入一个区域*时，Global Accelerator 路由规则必须将请求发送到当前主动区域。您可以使用运行状况检查，人为地报告任何未被全局系统视为主动区域的区域上的故障。与 DNS 一样，如果请求可以来自任何区域，则可以使用备用 DNS 域名来路由读取请求。

 在*写入您的区域*模式下，除非您还使用计算层请求路由，否则最好避开 Global Accelerator。

# 撤离过程
<a name="bp-global-table-design.prescriptive-guidance.evacuation"></a>

撤离区域是指将活动（通常是读取和写入活动，也可能是读取活动）从该区域迁移出去的过程。

## 撤离实时区域
<a name="bp-global-table-design.prescriptive-guidance.evacuation.live"></a>

您可能会出于多种原因决定撤离活动区域：作为日常业务活动的一部分（例如，如果您使用“跟随太阳”、“写入一个区域”模式），由于业务决策需要更改当前主动的区域，为了应对 DynamoDB 外部软件堆栈中的故障，或者因为您遇到了一般问题，例如区域内的延迟高于正常水平。

在*写入任何区域*模式下，撤出实时区域很简单。您可以使用任何路由系统将流量路由到备用区域，并让已撤离区域中的写入操作照常复制。

“写入一个区域”模式和“写入您的区域”模式通常与 MREC 表一起使用。因此，在新的主动区域中开始写入操作之前，您必须确保对主动区域的所有写入操作都已完全记录、流处理并全局传播，以确保将来的写入操作是针对最新版本的数据进行处理的。

假设区域 A 处于主动状态，区域 B 处于被动状态（无论是对于整个表，还是对于以区域 A 为主区域的项目）。执行撤离的典型机制是暂停对 A 的写入操作，等待足够长的时间让这些操作完全传播到 B，更新架构堆栈以识别 B 处于主动状态，然后恢复对 B 的写入操作。没有任何指标可以绝对肯定地表明区域 A 已将其数据完全复制到区域 B。如果区域 A 运行状况正常，暂停对区域 A 的写入操作并等待 `ReplicationLatency` 指标的最近最大值的 10 倍，通常足以确定复制完成。如果区域 A 运行状况不佳且显示延迟增加的其他区域，则应为等待时间选择更大的倍数。

## 撤离离线区域
<a name="bp-global-table-design.prescriptive-guidance.evacuation.offline"></a>

有一个特殊情况需要考虑：如果区域 A 在没有通知的情况下完全离线，会怎样？ 这种情况发生的可能性极低，但仍应考虑在内。

撤离离线 MRSC 表  
如果 MRSC 表发生这种情况，您无需执行任何特殊操作。MRSC 表支持恢复点目标（RPO）为零。对离线区域中的 MRSC 表进行的所有成功写入操作都将在所有其他区域表中可用，因此即使区域在未发出通知的情况下完全离线，也不会出现数据缺失的情况。业务可以继续使用位于其他区域的副本。

撤离离线 MREC 表  
如果 MREC 表发生这种情况，则区域 A 中尚未传播的任何写入操作都将保留，并在区域 A 恢复在线后进行传播。写入操作不会丢失，但它们的传播会被无限期延迟。  
在这种情况下，如何继续操作将由应用程序决定。为了实现业务连续性，写入操作可能需要继续指向新的主区域 B。但是，如果区域 B 中的某个项目收到更新，而针对该项目的写入操作有来自区域 A 的挂起传播，则在*以最后写入者为准*模型下，该传播将被抑制。区域 B 中的任何更新都可能抑制传入的写入请求。  
在*写入任何区域*模式下，可以在区域 B 中继续读取和写入操作，同时相信区域 A 中的项目最终会传播到区域 B，并认识到在区域 A 恢复在线之前可能会丢失项目。如果可能，例如对于幂等的写入操作，您应考虑重播新近的写入流量（例如，使用上游事件源），以填补任何可能缺失的写入操作的空白，并让以最后写入者为准冲突解决方案来抑制传入写入操作的最终传播。  
对于其他写入模式，您必须考虑在多大程度上可以继续工作，而对工作环境的看法稍微过时。在区域 A 恢复在线之前，将丢失一些持续时间很短的写入操作（由 `ReplicationLatency` 跟踪）。业务能否向前推进？ 在某些使用案例中可以向前推进，但在另一些使用案例中，如果没有额外的缓解机制，则可能不行。  
例如，假设即使某个区域完全中断服务，您也必须保持可用的抵扣金余额，不得中断。您可以将余额拆分为两个不同的项目，一个位于主区域 A 中，另一个位于区域 B 中，每个项目从可用余额的一半开始。这将使用*写入您的区域*模式。在每个区域中处理的事务性更新将写入余额的本地副本。如果区域 A 变为完全离线，则仍然可以在区域 B 中继续进行事务处理，写入操作将仅限于区域 B 中持有的余额部分。当余额变低或必须重新计算信贷余额时，像这样拆分余额会带来复杂性，但它确实提供了一个安全业务恢复的示例，即使存在不确定的待处理写入操作。  
再举一个例子，假设您正在捕获 Web 表单数据。您可以使用[乐观并发控制（OCC）](DynamoDBMapper.OptimisticLocking.md)为数据项目分配版本，并将最新版本作为隐藏字段嵌入到 Web 表单中。在每次提交时，只有当数据库中的版本仍然与构建表单所依据的版本相匹配时，写入操作才会成功。如果版本不匹配，则可以根据数据库中的当前版本刷新（或谨慎合并）Web 表单，然后用户可以再次继续。OCC 模型通常可以防止其他客户端覆盖并生成新版本的数据，但它也可以在失效转移期间提供帮助，此时客户端可能会遇到更旧版本的数据。假设您使用时间戳作为版本。表单最初是在 12:00 针对区域 A 构建的，但是（失效转移后）尝试写入区域 B 并注意到数据库中的最新版本是 11:59。在这种情况下，客户端可以等待 12:00 版本传播到区域 B，然后在该版本之上进行写入；也可以在 11:59 上构建并创建新的 12:01 版本（写入后，该版本将在区域 A 恢复后抑制传入版本）。  
第三个例子是，一家金融服务公司在 DynamoDB 数据库中保存有关客户账户及其金融交易的数据。如果区域 A 完全中断，他们希望确保与其账户相关的任何写入活动在区域 B 中完全可用，或者希望将他们的账户作为已知的部分账户进行隔离，直到区域 A 恢复在线。他们没有暂停所有业务，而是决定只对他们确定有未传播交易的一小部分账户暂停业务。为了实现这一点，他们使用了第三个区域，我们称为区域 C。在他们处理区域 A 中的任何写入操作之前，他们在区域 C 中简要地汇总了那些待处理的操作（例如，一个账户的新交易数量）。这个摘要足以让区域 B 确定其视图是否完全是最新的。从在区域 C 中写入操作，到区域 A 接受写入操作并且区域 B 收到写入操作之前，此操作实际上锁定了该账户。除非作为失效转移过程的一部分，否则不会使用区域 C 中的数据，之后，区域 B 可以将其数据与区域 C 进行交叉核对，以检查其账户是否已过期。在区域 A 恢复将部分数据传播到区域 B 之前，这些账户将标记为已隔离。如果区域 C 出现故障，则可以启动一个新的区域 D 以供使用。区域 C 中的数据非常短暂，几分钟后，区域 D 将具有足够新的动态写入操作记录，这将非常有用。如果区域 B 出现故障，区域 A 可以继续接受与区域 C 合作的写入请求。这家公司愿意接受延迟更高的写入（写入到两个区域：C，然后是 A），并且很幸运有了一个可以简洁汇总账户状态的数据模型。

# DynamoDB 全局表的吞吐能力规划
<a name="bp-global-table-design.prescriptive-guidance.throughput"></a>

将流量从一个区域迁移到另一个区域时，需要仔细考虑容量方面的 DynamoDB 表设置。

以下是管理写入容量的一些注意事项：
+ 全局表必须处于按需模式，或在启用自动扩缩的情况下进行预调配。
+ 如果使用自动扩缩进行预调配，则会跨区域复制写入设置（最小、最大和目标利用率）。尽管自动扩缩设置已同步，但实际的预调配写入容量可能会在区域间独立浮动。
+ 您可能会看到不同预调配写入容量的原因之一是 TTL 功能。当您在 DynamoDB 中启用 TTL 时，您可以指定一个属性名称，其值表示项目的过期时间，采用 Unix 纪元时间格式，单位为秒。在该时间之后，DynamoDB 可以删除该项目而不会产生写入成本。使用全局表，您可以在任何区域中配置 TTL，并且该设置会自动复制到与全局表关联的其他区域。当某个项目符合通过 TTL 规则删除的条件时，该工作可以在任何区域中完成。执行删除操作时不消耗源表上的写入单位，但是副本表将获得该删除操作的复制写入，并将产生复制写入单位成本。MRSC 表不支持 TTL。
+ 如果您使用自动扩缩，请确保最大预调配写入容量设置足够高，足以处理所有写入操作以及所有潜在的 TTL 删除操作。自动扩缩根据每个区域的写入消耗量调整每个区域。按需表没有最大预调配写入容量设置，但*表级别的最大写入吞吐量限制*指定了按需表将允许的最大持续写入容量。原定设置限制为 40000，但可以调整。我们建议您将其设置得足够高，以处理按需表可能需要的所有写入操作（包括 TTL 写入操作）。设置全局表时，此值在所有参与区域间必须相同。

以下是管理读取容量的一些注意事项：
+ 允许不同区域之间的读取容量管理设置有所不同，因为假设不同的区域可能有独立的读取模式。当您首先向表添加全局副本时，将传播源区域的容量。创建后，您可以调整读取容量设置，这些新设置不会传输到另一端。
+ 使用 DynamoDB Auto Scaling 时，请确保最大预调配读取容量设置足够高，足以处理所有区域的所有读取操作。在标准操作期间，读取容量可能会分布在各个区域之间，但在失效转移期间，表应该能够自动适应增加的读取工作负载。按需表没有最大预调配读取容量设置，但*表级别的最大读取吞吐量限制*指定了按需表将允许的最大持续读取容量。原定设置限制为 40000，但可以调整。我们建议您将其设置得足够高，以处理该表可能需要的所有读取操作（当所有读取操作都路由到此单个区域时）。
+ 如果一个区域中的表通常不会接收读取流量，但在失效转移后可能必须吸收大量读取流量，则可以预调配该表的容量以接受更高水平的读取流量。

无论您是否使用 Route 53 来路由请求，ARC 都有[就绪检查](https://docs.amazonaws.cn/r53recovery/latest/dg/recovery-readiness.rules-resources.html)功能，这对于确认 DynamoDB 区域是否具有相似的表设置和账户限额非常有用。这些就绪检查功能还有助于调整账户级别的限额以确保它们匹配。

# DynamoDB 全局表的准备核对清单
<a name="bp-global-table-design.prescriptive-guidance.checklist-and-faq"></a>

在部署全局表时，请使用下面的决策和任务核对清单。
+ 确定您的使用案例是否从 MRSC 或 MREC 一致性模式中受益更多。即使延迟较高以及存在其他方面的不足，您是否也需要强一致性？
+ 确定全局表应涉及多少个区域以及哪些区域。如果您计划使用 MRSC，请决定希望第三个区域成为副本区域还是见证区域。
+ 确定应用程序的写入模式。这与一致性模式不同。有关更多信息，请参阅 [使用 DynamoDB 全局表的写入模式](bp-global-table-design.prescriptive-guidance.writemodes.md)。
+ 根据写入模式规划您的[DynamoDB 中的路由策略](bp-global-table-design.prescriptive-guidance.request-routing.md)策略。
+ 根据您的一致性模式、写入模式和路由策略，定义[ 撤离过程  使用全局表撤离区域   撤离区域是指将活动（通常是读取和写入活动，也可能是读取活动）从该区域迁移出去的过程。  撤离实时区域实时区域  撤离实时区域   您可能会出于多种原因决定撤离活动区域：作为日常业务活动的一部分（例如，如果您使用“跟随太阳”、“写入一个区域”模式），由于业务决策需要更改当前主动的区域，为了应对 DynamoDB 外部软件堆栈中的故障，或者因为您遇到了一般问题，例如区域内的延迟高于正常水平。 在*写入任何区域*模式下，撤出实时区域很简单。您可以使用任何路由系统将流量路由到备用区域，并让已撤离区域中的写入操作照常复制。 “写入一个区域”模式和“写入您的区域”模式通常与 MREC 表一起使用。因此，在新的主动区域中开始写入操作之前，您必须确保对主动区域的所有写入操作都已完全记录、流处理并全局传播，以确保将来的写入操作是针对最新版本的数据进行处理的。 假设区域 A 处于主动状态，区域 B 处于被动状态（无论是对于整个表，还是对于以区域 A 为主区域的项目）。执行撤离的典型机制是暂停对 A 的写入操作，等待足够长的时间让这些操作完全传播到 B，更新架构堆栈以识别 B 处于主动状态，然后恢复对 B 的写入操作。没有任何指标可以绝对肯定地表明区域 A 已将其数据完全复制到区域 B。如果区域 A 运行状况正常，暂停对区域 A 的写入操作并等待 `ReplicationLatency` 指标的最近最大值的 10 倍，通常足以确定复制完成。如果区域 A 运行状况不佳且显示延迟增加的其他区域，则应为等待时间选择更大的倍数。   撤离离线区域离线区域  撤离离线区域   有一个特殊情况需要考虑：如果区域 A 在没有通知的情况下完全离线，会怎样？ 这种情况发生的可能性极低，但仍应考虑在内。  

撤离离线 MRSC 表  
如果 MRSC 表发生这种情况，您无需执行任何特殊操作。MRSC 表支持恢复点目标（RPO）为零。对离线区域中的 MRSC 表进行的所有成功写入操作都将在所有其他区域表中可用，因此即使区域在未发出通知的情况下完全离线，也不会出现数据缺失的情况。业务可以继续使用位于其他区域的副本。 

撤离离线 MREC 表  
如果 MREC 表发生这种情况，则区域 A 中尚未传播的任何写入操作都将保留，并在区域 A 恢复在线后进行传播。写入操作不会丢失，但它们的传播会被无限期延迟。  
在这种情况下，如何继续操作将由应用程序决定。为了实现业务连续性，写入操作可能需要继续指向新的主区域 B。但是，如果区域 B 中的某个项目收到更新，而针对该项目的写入操作有来自区域 A 的挂起传播，则在*以最后写入者为准*模型下，该传播将被抑制。区域 B 中的任何更新都可能抑制传入的写入请求。  
在*写入任何区域*模式下，可以在区域 B 中继续读取和写入操作，同时相信区域 A 中的项目最终会传播到区域 B，并认识到在区域 A 恢复在线之前可能会丢失项目。如果可能，例如对于幂等的写入操作，您应考虑重播新近的写入流量（例如，使用上游事件源），以填补任何可能缺失的写入操作的空白，并让以最后写入者为准冲突解决方案来抑制传入写入操作的最终传播。  
对于其他写入模式，您必须考虑在多大程度上可以继续工作，而对工作环境的看法稍微过时。在区域 A 恢复在线之前，将丢失一些持续时间很短的写入操作（由 `ReplicationLatency` 跟踪）。业务能否向前推进？ 在某些使用案例中可以向前推进，但在另一些使用案例中，如果没有额外的缓解机制，则可能不行。  
例如，假设即使某个区域完全中断服务，您也必须保持可用的抵扣金余额，不得中断。您可以将余额拆分为两个不同的项目，一个位于主区域 A 中，另一个位于区域 B 中，每个项目从可用余额的一半开始。这将使用*写入您的区域*模式。在每个区域中处理的事务性更新将写入余额的本地副本。如果区域 A 变为完全离线，则仍然可以在区域 B 中继续进行事务处理，写入操作将仅限于区域 B 中持有的余额部分。当余额变低或必须重新计算信贷余额时，像这样拆分余额会带来复杂性，但它确实提供了一个安全业务恢复的示例，即使存在不确定的待处理写入操作。  
再举一个例子，假设您正在捕获 Web 表单数据。您可以使用[乐观并发控制（OCC）](DynamoDBMapper.OptimisticLocking.md)为数据项目分配版本，并将最新版本作为隐藏字段嵌入到 Web 表单中。在每次提交时，只有当数据库中的版本仍然与构建表单所依据的版本相匹配时，写入操作才会成功。如果版本不匹配，则可以根据数据库中的当前版本刷新（或谨慎合并）Web 表单，然后用户可以再次继续。OCC 模型通常可以防止其他客户端覆盖并生成新版本的数据，但它也可以在失效转移期间提供帮助，此时客户端可能会遇到更旧版本的数据。假设您使用时间戳作为版本。表单最初是在 12:00 针对区域 A 构建的，但是（失效转移后）尝试写入区域 B 并注意到数据库中的最新版本是 11:59。在这种情况下，客户端可以等待 12:00 版本传播到区域 B，然后在该版本之上进行写入；也可以在 11:59 上构建并创建新的 12:01 版本（写入后，该版本将在区域 A 恢复后抑制传入版本）。  
第三个例子是，一家金融服务公司在 DynamoDB 数据库中保存有关客户账户及其金融交易的数据。如果区域 A 完全中断，他们希望确保与其账户相关的任何写入活动在区域 B 中完全可用，或者希望将他们的账户作为已知的部分账户进行隔离，直到区域 A 恢复在线。他们没有暂停所有业务，而是决定只对他们确定有未传播交易的一小部分账户暂停业务。为了实现这一点，他们使用了第三个区域，我们称为区域 C。在他们处理区域 A 中的任何写入操作之前，他们在区域 C 中简要地汇总了那些待处理的操作（例如，一个账户的新交易数量）。这个摘要足以让区域 B 确定其视图是否完全是最新的。从在区域 C 中写入操作，到区域 A 接受写入操作并且区域 B 收到写入操作之前，此操作实际上锁定了该账户。除非作为失效转移过程的一部分，否则不会使用区域 C 中的数据，之后，区域 B 可以将其数据与区域 C 进行交叉核对，以检查其账户是否已过期。在区域 A 恢复将部分数据传播到区域 B 之前，这些账户将标记为已隔离。如果区域 C 出现故障，则可以启动一个新的区域 D 以供使用。区域 C 中的数据非常短暂，几分钟后，区域 D 将具有足够新的动态写入操作记录，这将非常有用。如果区域 B 出现故障，区域 A 可以继续接受与区域 C 合作的写入请求。这家公司愿意接受延迟更高的写入（写入到两个区域：C，然后是 A），并且很幸运有了一个可以简洁汇总账户状态的数据模型。   ](bp-global-table-design.prescriptive-guidance.evacuation.md#bp-global-table-design.prescriptive-guidance.evacuation.title)[撤离过程](bp-global-table-design.prescriptive-guidance.evacuation.md)。
+ 捕获有关每个区域的运行状况、延迟和错误的指标。有关 DynamoDB 指标的列表，请参阅 Amazon 博客文章[监控 Amazon DynamoDB 的操作感知](https://www.amazonaws.cn/blogs/database/monitoring-amazon-dynamodb-for-operational-awareness/)，以获取需要观察的指标列表。您还应该使用 [Synthetics Canary](https://docs.amazonaws.cn/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries.html)（合成金丝雀，旨在检测故障的人工请求，以煤矿中的金丝雀命名），以及实时观察客户流量。并非所有问题都会出现在 DynamoDB 指标中。
+ 如果您使用的是 MREC，请在 `ReplicationLatency` 中为任何持续增长设置警报。增加可能表示意外配置错误，即全局表在不同区域中具有不同的写入设置，这会导致复制请求失败和延迟增加。这也可能表明存在区域中断。一个[很好的例子](https://www.amazonaws.cn/blogs/database/monitoring-amazon-dynamodb-for-operational-awareness/)是，如果最近的平均值超过 180000 毫秒，则生成警报。您可能还会观察到 `ReplicationLatency` 降至 0，这表示复制已停止。
+ 为每个全局表分配足够的最大读取和写入设置。
+ 提前确定撤离某个区域的原因。如果决定涉及人为判断，请记录所有考虑因素。这项工作应该事先仔细完成，而不是在压力下匆匆了事。
+ 为撤离某个区域时必须采取的每项措施制定一份运行手册。通常，全局表所涉及的工作非常少，但是移动堆栈的其余部分可能很复杂。
**注意**  
使用失效转移过程，最佳做法是仅依赖数据面板操作，而不依赖控制面板操作，因为在区域故障期间，某些控制面板操作可能会降级。

   有关更多信息，请参阅 Amazon 博客文章[使用 Amazon DynamoDB 全局表构建弹性应用程序：第 4 部分](https://www.amazonaws.cn/blogs/database/part-4-build-resilient-applications-with-amazon-dynamodb-global-tables/)。
+ 定期测试运行手册的各个方面，包括区域撤离。未经测试的运行手册是不可靠的。
+ 考虑使用 [Amazon Resilience Hub](https://docs.amazonaws.cn/resilience-hub/latest/userguide/what-is.html)来评估整个应用程序（包括全局表）的韧性。通过韧性监测中心的控制面板，您可以全面查看应用程序产品组合的整体弹性状态。
+ 考虑使用 ARC 就绪检查功能来评估应用程序的当前配置，并跟踪与最佳实践的任何偏差。
+ 在编写用于 Route 53 或 Global Accelerator 的运行状况检查时，进行一组覆盖整个数据库流的调用。如果您将检查限制为仅确认 DynamoDB 端点已启动，则无法涵盖许多故障模式，例如 Amazon Identity and Access Management（IAM）配置错误、代码部署问题、DynamoDB 外部的堆栈故障、高于平均读取或写入延迟等。

## 有关部署全局表的常见问题解答（FAQ）
<a name="bp-global-table-design.prescriptive-guidance.faq"></a>

**全局表的定价是多少？**
+ 对传统 DynamoDB 表的写入操作按写入容量单位（WCU，用于预调配表）或写入请求单位（WRU，用于按需表）定价。如果您写入一个 5KB 项目，则会产生 5 个单位的费用。对全局表的写入按复制写入容量单位（rWCU，用于预调配表）或复制写入请求单位（rWRU，用于按需表）定价。rWCU 和 rWRU 的价格与 WGU 和 WRU 的价格相同。
+ 在其中直接写入或通过复制写入项目的每个区域都会产生 rWCU 和 rWRU 费用。跨区域数据传输费用适用。
+ 写入全局二级索引（GSI）被视为本地写入操作，使用常规写入单位。
+ 目前 rWCU 或 rWRU 没有可用的预留容量。对于 GSI 消耗写入单位的表，为 WCU 购买预留容量可能仍有好处。
+ 当您将新区域添加到全局表时，DynamoDB 会自动引导新区域，并根据表的大小（GB）向您收取费用，就像表还原一样。还会收取跨区域数据传输费用。

**全局表支持哪些区域？**

[全局表版本 2019.11.21（当前）](GlobalTables.md)对于 MREC 表支持所有 Amazon Web Services 区域，对于 MRSC 表支持以下区域集：
+ 美国区域集：美国东部（弗吉尼亚州北部）、美国东部（俄亥俄州）、美国西部（俄勒冈州）
+ 欧洲区域集：欧洲地区（爱尔兰）、欧洲地区（伦敦）、欧洲地区（巴黎）、欧洲地区（法兰克福）
+ 亚太地区区域集：亚太地区（东京）、亚太地区（首尔）和亚太地区（大阪）

**如何使用全局表处理 GSI？**

在[全局表版本 2019.11.21（当前版）](GlobalTables.md)中，当您在一个区域中创建 GSI 时，它会在其它参与区域中自动创建并自动回填。

**如何停止复制全局表？** 
+ 您可以像删除任何其他表一样删除副本表。删除全局表将停止复制到该区域，并删除保留在该区域中的表副本。但是，不能在将表的副本保留为独立实体时停止复制，也不能暂停复制。
+ MRSC 表必须部署在恰好三个区域中。要删除副本，您必须删除所有副本和见证者，以使 MRSC 表成为本地表。

**DynamoDB Streams 如何与全局表交互？**
+ 每个全局表都基于其所有写入操作生成一个独立的流，而无论这些写入是从何处开始的。您可以选择在一个区域或在所有区域中（独立）使用 DynamoDB 流。如果您想要处理本地而不是复制的写入操作，则可以向每个项目添加您自己的区域属性，以确定写入区域。然后，您可以使用 Lambda 事件筛选条件，以便只调用 Lambda 函数来处理本地区域中的写入操作。这有助于执行插入和更新操作，但不能执行删除操作。
+ 配置为多区域最终一致性的全局表（MREC 表）可通过从副本表上的 DynamoDB 流读取这些更改，并将该更改应用于所有其他副本表，从而复制更改。因此，默认情况下，对 MREC 全局表中的所有副本都启用了 DynamoDB，并且无法在这些副本上禁用。MREC 复制过程可能会在短时间内将多项更改合并到单个复制写入操作中。因此，每个副本的流均可能包含略有不同的记录。MREC 副本上的 DynamoDB Streams 记录始终按每个项目排序，但项目间的排序可能在副本之间有所不同。
+ 配置为多区域强一致性的全局表（MRSC 表）不使用 DynamoDB Streams 进行复制，因此默认情况下不在 MRSC 副本上启用此功能。您可以在 MRSC 副本上启用 DynamoDB Streams。MRSC 副本上的 DynamoDB Streams 记录对于每个副本都是相同的，并且始终按项目排序，但项目之间的排序在副本之间可能有所不同。

**全局表如何处理事务？** 
+ 对 MRSC 表的事务操作会生成错误。
+ 对 MREC 表的事务操作，仅在最初发生写入操作的区域内提供原子性、一致性、隔离性和持久性（ACID）保证。全局表中不支持跨区域的事务。例如，如果您有一个 MREC 全局表，该表在美国东部（俄亥俄州）和美国西部（俄勒冈州）区域中具有副本，并且在美国东部（俄亥俄州）区域中执行 `TransactWriteItems` 操作，则在复制更改时，可能会在美国西部（俄勒冈州）区域观察到部分完成的事务。更改仅在源区域中提交后才复制到其他区域。

**全局表如何与 DynamoDB Accelerator 缓存（DAX）交互？**

全局表通过直接更新 DynamoDB 绕过 DAX，因此 DAX 并不知道它保存的是陈旧数据。DAX 缓存只有在缓存的 TTL 过期时才会刷新。

**表上的标签会传播吗？**

不，标签不会自动传播。

**我应该备份所有区域中的表，还是只备份一个区域中的表？**

答案取决于备份的目的。
+ 如果您想确保数据的耐久性，DynamoDB 已经提供了这种保护措施。该服务可确保耐久性。
+ 如果您想保留历史记录的快照（例如，为了符合法规要求），备份一个区域中的表就应该足够了。您可以使用 将备份复制到其他区域Amazon Backup
+ 如果您想恢复错误删除或修改的数据，请在一个区域中使用 [DynamoDB 时间点故障恢复（PITR）](PointInTimeRecovery_Howitworks.md)。

**如何使用 Amazon CloudFormation 部署全局表？**
+ CloudFormation 将 DynamoDB 表和全局表表示为两个独立的资源：`AWS::DynamoDB::Table` 和 `AWS::DynamoDB::GlobalTable`。一种方法是使用 `GlobalTable` 构造来创建所有可能成为全局表的表，最初将其保留为独立的表，然后在以后需要时再添加区域。
+ 在 CloudFormation 中，每个全局表都由单个区域中的单个堆栈控制，而与副本的数量无关。部署模板时，CloudFormation 将创建和更新所有副本（作为单个堆栈操作的一部分）。您不应在多个区域中部署相同的 [AWS::DynamoDB::GlobalTable](https://docs.amazonaws.cn/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-globaltable.html) 资源。这样做会导致错误，不受支持。如果在多个区域中部署应用程序模板，则可以使用条件在单个区域中创建 `AWS::DynamoDB::GlobalTable` 资源。或者，您可以选择在独立于应用程序堆栈的堆栈中定义 `AWS::DynamoDB::GlobalTable` 资源，并确保仅将该资源部署到单个区域。
+ 如果您有一个常规表，并且想要将其转换为全局表，同时保持它由 CloudFormation 管理，则将删除策略设置为 `Retain`，从堆栈中删除该表，在控制台中将该表转换为全局表，然后将该全局表作为新资源导入到堆栈中。有关更多信息，请参阅 [Amazon GitHub 存储库](https://github.com/aws-samples/amazon-dynamodb-table-to-global-table-cdk)。
+ 目前不支持跨账户复制。

## 结论和资源
<a name="bp-global-table-design.prescriptive-guidance-resources-conclusion"></a>

DynamoDB 全局表的控件很少，但仍然需要仔细考虑。您必须确定您的写入模式、路由模型和撤离过程。您必须针对每个区域对应用程序进行检测，并准备好调整路由或执行撤离，以维护全局运行状况。回报是拥有一个全球分布的数据集，该数据集可以实现低延迟的读取和写入操作，可用性设计为 99.999%。

有关 DynamoDB 全局表的更多信息，请参阅以下资源：
+ [DynamoDB 文档](https://docs.amazonaws.cn/dynamodb/)
+ [Amazon Application Recovery Controller](https://www.amazonaws.cn/application-recovery-controller/)
+ [ARC 中的就绪检查](https://docs.amazonaws.cn/r53recovery/latest/dg/recovery-readiness.html)（Amazon 文档）
+ [Route 53 路由策略](https://docs.amazonaws.cn/Route53/latest/DeveloperGuide/routing-policy.html)
+ [Amazon Global Accelerator](https://www.amazonaws.cn/global-accelerator/)
+ [DynamoDB 服务水平协议](https://www.amazonaws.cn/dynamodb/sla/)
+ [Amazon 多区域基础知识](https://docs.amazonaws.cn/prescriptive-guidance/latest/aws-multi-region-fundamentals/introduction.html)（Amazon 白皮书）
+ [Data resiliency design patterns with Amazon](https://www.youtube.com/watch?v=7IA48SOX20c)（Amazon re:Invent 2022 演示）
+ [How Fidelity Investments and Reltio modernized with Amazon DynamoDB](https://www.youtube.com/watch?v=QUpV5MDu4Ys&t=706s)（Amazon re:Invent 2022 演示）
+ [Multi-Region design patterns and best practices](https://www.youtube.com/watch?v=ilgpzlE7Hds&t=1882s)（Amazon re:Invent 2022 演示）
+ [Disaster Recovery (DR) Architecture on Amazon, Part III: Pilot Light and Warm Standby](https://www.amazonaws.cn/blogs/architecture/disaster-recovery-dr-architecture-on-aws-part-iii-pilot-light-and-warm-standby/)（Amazon 博客文章）
+ [Use Region pinning to set a home Region for items in an Amazon DynamoDB global table](https://www.amazonaws.cn/blogs/database/use-region-pinning-to-set-a-home-region-for-items-in-an-amazon-dynamodb-global-table/)（Amazon 博客文章）
+ [Monitoring Amazon DynamoDB for operational awareness](https://www.amazonaws.cn/blogs/database/monitoring-amazon-dynamodb-for-operational-awareness/)（Amazon 博客文章）
+ [Scaling DynamoDB: How partitions, hot keys, and split for heat impact performance](https://www.amazonaws.cn/blogs/database/part-3-scaling-dynamodb-how-partitions-hot-keys-and-split-for-heat-impact-performance/)（Amazon 博客文章）
+ [Multi-Region strong consistency with DynamoDB global tables](https://www.youtube.com/watch?v=R-nTs8ZD8mA)（Amazon re:Invent 2024 演示）

# 在 DynamoDB 中管理控制面板的最佳实践
<a name="bp-control-plane"></a>

**注意**  
DynamoDB 引入的控制面板节流限制为每秒 2500 个请求，并提供重试选项。有关其他详细信息，请参阅下文。

DynamoDB 控制面板操作可让您管理 DynamoDB 表以及依赖于表（例如索引）的对象。有关这些操作的更多信息，请参阅 [控制面板](HowItWorks.API.md#HowItWorks.API.ControlPlane)。

在某些情况下，您可能需要采取行动，并将由控制面板调用返回的数据用作业务逻辑的一部分。例如，您可能需要知道 `DescribeTable` 返回的 `ProvisionedThroughput` 的值。在这些情况下，请遵循以下最佳做法：
+ 不要过度查询 DynamoDB 控制面板。
+ 不要在同一个代码中混用控制面板调用和数据面板调用。
+ 处理对控制面板请求的限制，然后使用退避功能重试。
+ 从单个客户端调用和跟踪对特定资源的更改。
+ 与其以短的时间间隔多次检索同一个表的数据，不如缓存数据以进行处理。

# 在 DynamoDB 中使用批量数据操作的最佳实践
<a name="BestPractices_BulkDataOperations"></a>

DynamoDB 支持批量操作，例如 `BatchWriteItem`，使用该参数，您可以同时执行多达 25 个 `PutItem` 和 `DeleteItem` 请求。但是，`BatchWriteItem` 不支持 `UpdateItem` 操作。对于批量更新，区别在于更新的要求和性质。您可以使用其他 DynamoDB API（例如 `TransactWriteItems`）来处理不超过 100 的批次大小。当涉及到更多项目时，您可以使用 Amazon Glue、Amazon EMR、Amazon Step Functions 等服务，也可以使用 DynamoDB-Shell 等自定义脚本和工具进行批量更新。

**Topics**
+ [有条件批量更新](BestPractices_ConditionalBatchUpdate.md)
+ [高效执行批量操作](BestPractices_EfficientBulkOperations.md)

# 有条件批量更新
<a name="BestPractices_ConditionalBatchUpdate"></a>

DynamoDB 支持批量操作，例如 `BatchWriteItem`，使用该参数，您可以在单个批次中执行多达 25 个 `PutItem` 和 `DeleteItem` 请求。但是，`BatchWriteItem` 不支持 `UpdateItem` 操作，也不支持条件表达式。作为解决方法，您可以使用其他 DynamoDB API（例如 `TransactWriteItems`）来处理不超过 100 的批次大小。

在涉及到更多项目并且需要更改主要的数据块时，您可以使用 Amazon Glue、Amazon EMR、Amazon Step Functions 等服务，也可以使用 DynamoDB-Shell 等自定义脚本和工具高效地进行批量更新。

**使用此模式的时机**
+ 生产环境应用场景不支持 DynamoDB-shell。
+ `TransactWriteItems` – 上限为 100 个单独的更新，可以有条件或无条件，执行方式为全有或全无 ACID 捆绑。如果应用程序需要幂等性，则也可以提供带有 `ClientRequestToken` 的 `TransactWriteItems` 调用，这意味着多个相同的调用与单个调用具有相同的效果。这种方法可以确保您不会多次执行同一个事务，最终导致数据状态不正确。

  权衡 – 会使用额外的吞吐量。每 1KB 写入 2 个 WCU，而不是标准的每 1 KB 写入 1 个 WGU。
+ PartiQL `BatchExecuteStatement` – 最多 25 个更新，可以有条件或无条件。`BatchExecuteStatement` 始终返回对整个请求的成功响应，还会返回保留了顺序的单独操作响应的列表。

  权衡 – 对于较大的批次，需要额外的客户端逻辑，以便按照 25 的批次大小分发请求。在确定重试策略时，需要考虑到单独错误的响应。

## 代码示例
<a name="bp-conditional-code-examples"></a>

这些代码示例使用 boto3 库，即适用于 Python 的 Amazon SDK。示例假设您已安装 boto3 并配置了相应的 Amazon 凭证。

假设一家电气供应商在欧洲多个城市建有多个仓库，供应商有一个库存数据库。由于夏天快要结束了，供应商想要甩卖台式风扇，以便为其他库存腾出空间。供应商希望对所有从意大利仓库供货的台式风扇的价格打折，但前提是要有 20 个台式风扇的储备库存。DynamoDB 表名为 **inventory**，在其键架构中，分区键为 **sku**，这是每个产品的唯一标识符，排序键为 **warehouse**，这是数据仓库的标识符。

以下 Python 代码演示如何使用 `BatchExecuteStatement` API 调用，执行此有条件的批量更新。

```
import boto3

client=boto3.client("dynamodb")

before_image=client.query(TableName='inventory', KeyConditionExpression='sku=:pk_val AND begins_with(warehouse, :sk_val)', ExpressionAttributeValues={':pk_val':{'S':'F123'},':sk_val':{'S':'WIT'}}, ProjectionExpression='sku,warehouse,quantity,price')
print("Before update: ", before_image['Items'])

response=client.batch_execute_statement(
        Statements=[
            {'Statement': 'UPDATE inventory SET price=price-5 WHERE sku=? AND warehouse=? AND quantity > 20', 'Parameters': [{'S':'F123'}, {'S':'WITTUR1'}], 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'},
            {'Statement': 'UPDATE inventory SET price=price-5 WHERE sku=? AND warehouse=? AND quantity > 20', 'Parameters': [{'S':'F123'}, {'S':'WITROM1'}], 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'},
            {'Statement': 'UPDATE inventory SET price=price-5 WHERE sku=? AND warehouse=? AND quantity > 20', 'Parameters': [{'S':'F123'}, {'S':'WITROM2'}], 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'},
            {'Statement': 'UPDATE inventory SET price=price-5 WHERE sku=? AND warehouse=? AND quantity > 20', 'Parameters': [{'S':'F123'}, {'S':'WITROM5'}], 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'},
            {'Statement': 'UPDATE inventory SET price=price-5 WHERE sku=? AND warehouse=? AND quantity > 20', 'Parameters': [{'S':'F123'}, {'S':'WITVEN1'}], 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'},
            {'Statement': 'UPDATE inventory SET price=price-5 WHERE sku=? AND warehouse=? AND quantity > 20', 'Parameters': [{'S':'F123'}, {'S':'WITVEN2'}], 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'},
            {'Statement': 'UPDATE inventory SET price=price-5 WHERE sku=? AND warehouse=? AND quantity > 20', 'Parameters': [{'S':'F123'}, {'S':'WITVEN3'}], 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'},
        ],
        ReturnConsumedCapacity='TOTAL'
    )

after_image=client.query(TableName='inventory', KeyConditionExpression='sku=:pk_val AND begins_with(warehouse, :sk_val)', ExpressionAttributeValues={':pk_val':{'S':'F123'},':sk_val':{'S':'WIT'}}, ProjectionExpression='sku,warehouse,quantity,price')
print("After update: ", after_image['Items'])
```

在示例数据上，执行会生成以下输出：

```
Before update:  [{'quantity': {'N': '20'}, 'warehouse': {'S': 'WITROM1'}, 'price': {'N': '40'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '25'}, 'warehouse': {'S': 'WITROM2'}, 'price': {'N': '40'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '28'}, 'warehouse': {'S': 'WITROM5'}, 'price': {'N': '38'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '26'}, 'warehouse': {'S': 'WITTUR1'}, 'price': {'N': '40'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '10'}, 'warehouse': {'S': 'WITVEN1'}, 'price': {'N': '38'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '20'}, 'warehouse': {'S': 'WITVEN2'}, 'price': {'N': '38'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '50'}, 'warehouse': {'S': 'WITVEN3'}, 'price': {'N': '35'}, 'sku': {'S': 'F123'}}]
After update:  [{'quantity': {'N': '20'}, 'warehouse': {'S': 'WITROM1'}, 'price': {'N': '40'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '25'}, 'warehouse': {'S': 'WITROM2'}, 'price': {'N': '35'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '28'}, 'warehouse': {'S': 'WITROM5'}, 'price': {'N': '33'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '26'}, 'warehouse': {'S': 'WITTUR1'}, 'price': {'N': '35'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '10'}, 'warehouse': {'S': 'WITVEN1'}, 'price': {'N': '38'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '20'}, 'warehouse': {'S': 'WITVEN2'}, 'price': {'N': '38'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '50'}, 'warehouse': {'S': 'WITVEN3'}, 'price': {'N': '30'}, 'sku': {'S': 'F123'}}]
```

由于这是内部系统的限定操作，因此没有考虑幂等性要求。这里可以设置额外的护栏机制，例如只有在价格大于 35 且小于 40 时才应更新价格，以可靠地进行更新靠。

或者，如果存在更严格的幂等性和 ACID 要求，我们可以使用 `TransactWriteItems` 执行相同的批量更新操作。但是，请务必记住，事务捆绑中的所有操作需要都要完成，否则整个捆绑失败。

我们假设意大利出现热浪，对台扇的需求急剧增长。供应商希望将意大利所有仓库的台式风扇价格提高 20 欧元，但监管机构要求，只有当所有库存的当前价格低于 70 欧元时才能够提价。这里的关键之处在于，只有当每个仓库中的价格都低于 70 欧元时，才能一次性更新对所有库存的价格，而且只能更新一次。

以下 Python 代码演示如何使用 `TransactWriteItems` API 调用，执行此批量更新。

```
import boto3

client=boto3.client("dynamodb")

before_image=client.query(TableName='inventory', KeyConditionExpression='sku=:pk_val AND begins_with(warehouse, :sk_val)', ExpressionAttributeValues={':pk_val':{'S':'F123'},':sk_val':{'S':'WIT'}}, ProjectionExpression='sku,warehouse,quantity,price')
print("Before update: ", before_image['Items'])

response=client.transact_write_items(
        ClientRequestToken='UUIDAWS124',
        TransactItems=[
            {'Update': { 'Key': {'sku': {'S':'F123'}, 'warehouse': {'S':'WITTUR1'}}, 'UpdateExpression': 'SET price = price + :inc', 'ConditionExpression': 'price < :cap', 'ExpressionAttributeValues': { ':inc': {'N': '20'}, ':cap': {'N': '70'}}, 'TableName': 'inventory', 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'}},
            {'Update': { 'Key': {'sku': {'S':'F123'}, 'warehouse': {'S':'WITROM1'}}, 'UpdateExpression': 'SET price = price + :inc', 'ConditionExpression': 'price < :cap', 'ExpressionAttributeValues': { ':inc': {'N': '20'}, ':cap': {'N': '70'}}, 'TableName': 'inventory', 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'}},
            {'Update': { 'Key': {'sku': {'S':'F123'}, 'warehouse': {'S':'WITROM2'}}, 'UpdateExpression': 'SET price = price + :inc', 'ConditionExpression': 'price < :cap', 'ExpressionAttributeValues': { ':inc': {'N': '20'}, ':cap': {'N': '70'}}, 'TableName': 'inventory', 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'}},
            {'Update': { 'Key': {'sku': {'S':'F123'}, 'warehouse': {'S':'WITROM5'}}, 'UpdateExpression': 'SET price = price + :inc', 'ConditionExpression': 'price < :cap', 'ExpressionAttributeValues': { ':inc': {'N': '20'}, ':cap': {'N': '70'}}, 'TableName': 'inventory', 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'}},
            {'Update': { 'Key': {'sku': {'S':'F123'}, 'warehouse': {'S':'WITVEN1'}}, 'UpdateExpression': 'SET price = price + :inc', 'ConditionExpression': 'price < :cap', 'ExpressionAttributeValues': { ':inc': {'N': '20'}, ':cap': {'N': '70'}}, 'TableName': 'inventory', 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'}},
            {'Update': { 'Key': {'sku': {'S':'F123'}, 'warehouse': {'S':'WITVEN2'}}, 'UpdateExpression': 'SET price = price + :inc', 'ConditionExpression': 'price < :cap', 'ExpressionAttributeValues': { ':inc': {'N': '20'}, ':cap': {'N': '70'}}, 'TableName': 'inventory', 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'}},
            {'Update': { 'Key': {'sku': {'S':'F123'}, 'warehouse': {'S':'WITVEN3'}}, 'UpdateExpression': 'SET price = price + :inc', 'ConditionExpression': 'price < :cap', 'ExpressionAttributeValues': { ':inc': {'N': '20'}, ':cap': {'N': '70'}}, 'TableName': 'inventory', 'ReturnValuesOnConditionCheckFailure': 'ALL_OLD'}},
        ],
        ReturnConsumedCapacity='TOTAL'
    )

after_image=client.query(TableName='inventory', KeyConditionExpression='sku=:pk_val AND begins_with(warehouse, :sk_val)', ExpressionAttributeValues={':pk_val':{'S':'F123'},':sk_val':{'S':'WIT'}}, ProjectionExpression='sku,warehouse,quantity,price')
print("After update: ", after_image['Items'])
```

在示例数据上，执行会生成以下输出：

```
Before update:  [{'quantity': {'N': '20'}, 'warehouse': {'S': 'WITROM1'}, 'price': {'N': '60'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '25'}, 'warehouse': {'S': 'WITROM2'}, 'price': {'N': '55'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '28'}, 'warehouse': {'S': 'WITROM5'}, 'price': {'N': '53'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '26'}, 'warehouse': {'S': 'WITTUR1'}, 'price': {'N': '55'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '10'}, 'warehouse': {'S': 'WITVEN1'}, 'price': {'N': '58'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '20'}, 'warehouse': {'S': 'WITVEN2'}, 'price': {'N': '58'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '50'}, 'warehouse': {'S': 'WITVEN3'}, 'price': {'N': '50'}, 'sku': {'S': 'F123'}}]
After update:  [{'quantity': {'N': '20'}, 'warehouse': {'S': 'WITROM1'}, 'price': {'N': '80'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '25'}, 'warehouse': {'S': 'WITROM2'}, 'price': {'N': '75'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '28'}, 'warehouse': {'S': 'WITROM5'}, 'price': {'N': '73'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '26'}, 'warehouse': {'S': 'WITTUR1'}, 'price': {'N': '75'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '10'}, 'warehouse': {'S': 'WITVEN1'}, 'price': {'N': '78'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '20'}, 'warehouse': {'S': 'WITVEN2'}, 'price': {'N': '78'}, 'sku': {'S': 'F123'}}, {'quantity': {'N': '50'}, 'warehouse': {'S': 'WITVEN3'}, 'price': {'N': '70'}, 'sku': {'S': 'F123'}}]
```

在 DynamoDB 中有多种执行批量更新的方法。哪种方法合适取决于多种因素，例如 ACID 和/或幂等性等要求、要更新的项目数量以及对 API 的熟悉程度等。

# 高效执行批量操作
<a name="BestPractices_EfficientBulkOperations"></a>

**使用此模式的时机**

这些模式对于在 DynamoDB 项目上高效地执行批量更新非常有用。
+ 生产环境应用场景不支持 DynamoDB-shell。
+ `TransactWriteItems` – 最高 100 个单独的更新，可以有条件或无条件，以全有或全无的 ACID 捆绑的形式执行 

  权衡 – 会消耗额外的吞吐量，每 1 KB 写入 2 个 WCU。
+ PartiQL `BatchExecuteStatement` – 最高 25 个更新，可以有条件或无条件

  权衡 – 需要额外的逻辑，以便按照 25 的批次大小分发请求。
+ Amazon Step Functions – 为熟悉 Amazon Lambda 的开发人员提供速率受限的批量操作。

  权衡 – 执行时间与速率限制成反比。受最大 Lambda 函数超时时间的限制。该功能会使得在读取和写入之间发生的数据更改可能会被覆盖。有关更多信息，请参阅 [Backfilling an Amazon DynamoDB Time to Live attribute using Amazon EMR: Part 2](https://www.amazonaws.cn/blogs/database/part-2-backfilling-an-amazon-dynamodb-time-to-live-attribute-using-amazon-emr/)。
+ Amazon Glue 和 Amazon EMR – 速率受限的批量操作，采用托管式并行度。对于不注重时效性的应用程序或更新，这些选项可以在后台运行，只消耗一小部分吞吐量。这两项服务都使用 emr-dynamodb-connector 来执行 DynamoDB 操作。这些服务会执行大量读取，然后大量写入更新的项目，并有速率限制选项。

  权衡 – 执行时间与速率限制成反比。使用该功能时，所包含的在读取和写入之间发生的数据更改可能会被覆盖。您无法从全局二级索引（GSI）中读取。请参阅 [Backfilling an Amazon DynamoDB Time to Live attribute using Amazon EMR: Part 2](https://www.amazonaws.cn/blogs/database/part-2-backfilling-an-amazon-dynamodb-time-to-live-attribute-using-amazon-emr/)。
+ DynamoDB Shell – 使用类似 SQL 的查询执行速率受限的批量操作。您可以从 GSI 中读取以提高效率。

  权衡 – 执行时间与速率限制成反比。请参阅 [Rate limited bulk operations in DynamoDB Shell](https://www.amazonaws.cn/blogs/database/rate-limited-bulk-operations-in-dynamodb-shell/)。

## 使用模式
<a name="BestPractices_EfficientBulkOperations_UsingThePattern"></a>

批量更新会对成本造成巨大的影响，尤其是当您使用按需吞吐量模式时。如果您使用预置吞吐量模式，则需要在速度和成本之间进行权衡。设置极为严格的速率限制参数可能会导致极长的处理时间。您可以使用平均项目大小和速率限制来大致确定更新速度。

或者，您可以根据更新过程的预期持续时间以及平均项目大小，来确定该过程所需的吞吐量。随各模式提供的博客引用详细介绍了使用该模式的策略、实施和限制。有关更多信息，请参阅 [Cost-effective bulk processing with Amazon DynamoDB](https://www.amazonaws.cn/blogs/database/cost-effective-bulk-processing-with-amazon-dynamodb/)。

在对活动 DynamoDB 表执行批量更新时，可以采取多种方法。哪种方法合适取决于多种因素，例如 ACID 和/或幂等性等要求、要更新的项目数量以及对 API 的熟悉程度等。此处非常重要的一点是要权衡成本与时间，上文讨论的大多数方法都提供了选项，可对更新作业使用的吞吐量限制速率。

# 在 DynamoDB 中处理并发更新的最佳实践
<a name="BestPractices_ImplementingVersionControl"></a>

在分布式系统中，多个进程或用户可能会尝试同时修改相同的数据。如果没有并发控制，这些并发写入可能会导致更新丢失、数据不一致或争用情况。DynamoDB 提供了多种机制来帮助您管理并发访问和维护数据完整性。

**注意**  
`UpdateItem` 等单独的写入操作是原子操作，无论并发度如何，都始终对项目的最新版本进行操作。当您的应用程序必须读取某个项目，然后根据读取的值将其写回时（读取-修改-写入周期），就需要使用锁定策略，因为其他进程可能会在读取和写入之间修改该项目。

处理并发更新的主要策略有两种：
+ **乐观锁**：假设冲突情况很少。此策略允许并发访问，并在写入时使用条件写入检测冲突。如果检测到冲突，则写入失败，应用程序可以重试。
+ **悲观锁**：假设很可能出现冲突。此策略在修改资源之前获取对资源的独占访问权限，以这种方式来防止并发访问。其他进程必须等待直至解锁。

下表汇总了 DynamoDB 中提供的方法：


| 方法 | 机制 | 适用于 | 
| --- | --- | --- | 
| 乐观锁 | 版本属性 \$1 条件写入 | 争用率低，重试成本低 | 
| 悲观锁（事务） | TransactWriteItems | 多项目原子性，争用率中等 | 
| 悲观锁（锁客户端） | 带租约和检测信号的专用锁定表 | 长时间运行的工作流，分布式协调 | 

# 乐观锁（使用版本号）
<a name="BestPractices_OptimisticLocking"></a>

乐观锁是一种在写入时检测冲突而不是防止冲突的策略。每个项目都包含一个版本属性，其值会随着每次更新而增加。更新项目时，您需要包含一个[条件表达式](Expressions.ConditionExpressions.md)，用于检查版本号是否与应用程序上次读取的值匹配。如果在这个期间其他进程修改了该项目，则条件失败并且 DynamoDB 返回 `ConditionalCheckFailedException`。

## 使用乐观锁的情况
<a name="BestPractices_OptimisticLocking_WhenToUse"></a>

乐观锁非常适合用于以下情况：
+ 多个用户或进程可能会更新同一个项目，但冲突并不频繁。
+ 对于应用程序来说，重试失败写入的成本较低。
+ 您需要避免管理分布式锁的开销和复杂性。

常见的示例包括电子商务库存更新、协作编辑平台和金融交易记录。

## 权衡
<a name="BestPractices_OptimisticLocking_Tradeoffs"></a>

**高争用率下的重试开销**  
在高并发环境中，发生冲突的可能性会增加，进而可能导致更高的重试次数和写入成本。

**实施复杂性**  
向项目添加版本控制和处理有条件检查，会增加应用程序逻辑的复杂性。适用于 Java 的 Amazon SDK v2 增强版客户端通过 [https://docs.amazonaws.cn/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE](https://docs.amazonaws.cn/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE) 注解提供内置支持，这可以自动为您管理版本号。

## 模式设计
<a name="BestPractices_OptimisticLocking_PatternDesign"></a>

每个项目中包含一个版本属性。以下提供了一个简单的架构设计：
+ 分区键：每个项目的唯一标识符（例如 `ItemId`）。
+ 属性：
  + `ItemId` – 项目的唯一标识符。
  + `Version` – 表示项目版本号的整数。
  + `QuantityLeft` – 项目的剩余库存。

首次创建项目时，`Version` 属性设置为 1。每次进行更新时，版本号增加 1。


| ItemID（分区键） | 版本 | QuantityLeft | 
| --- | --- | --- | 
| Bananas | 1 | 10 | 
| Apples | 1 | 5 | 
| Oranges | 1 | 7 | 

## 实现
<a name="BestPractices_OptimisticLocking_Implementation"></a>

要实施乐观锁，请按照以下步骤操作：

1. 读取项目的当前版本。

   ```
   def get_item(item_id):
       response = table.get_item(Key={'ItemID': item_id})
       return response['Item']
   
   item = get_item('Bananas')
   current_version = item['Version']
   ```

1. 使用检查版本号的条件表达式更新项目。

   ```
   def update_item(item_id, qty_bought, current_version):
       try:
           response = table.update_item(
               Key={'ItemID': item_id},
               UpdateExpression="SET QuantityLeft = QuantityLeft - :qty, Version = :new_v",
               ConditionExpression="Version = :expected_v",
               ExpressionAttributeValues={
                   ':qty': qty_bought,
                   ':new_v': current_version + 1,
                   ':expected_v': current_version
               },
               ReturnValues="UPDATED_NEW"
           )
           return response
       except ClientError as e:
           if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
               print("Version conflict: another process updated this item.")
           raise
   ```

1. 使用新的读取进行重试来处理冲突。

   每次重试都需要进行额外的读取操作，因此请限制重试的总次数。

   ```
   def update_with_retry(item_id, qty_bought, max_retries=3):
       for attempt in range(max_retries):
           item = get_item(item_id)
           try:
               return update_item(item_id, qty_bought, item['Version'])
           except ClientError as e:
               if e.response['Error']['Code'] != 'ConditionalCheckFailedException':
                   raise
               print(f"Retry {attempt + 1}/{max_retries}")
       raise Exception("Update failed after maximum retries.")
   ```

对于 Java 应用程序，适用于 Java 的 Amazon SDK v2 增强版客户端通过 [https://docs.amazonaws.cn/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE](https://docs.amazonaws.cn/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE) 注解提供内置乐观锁支持，这可以自动为您管理版本号。

有关条件表达式的更多信息，请参阅 [DynamoDB 条件表达式 CLI 示例](Expressions.ConditionExpressions.md)。

# DynamoDB 事务的悲观锁
<a name="BestPractices_PessimisticLocking"></a>

DynamoDB [事务](transactions.md)为分组操作提供了一种全有或全无的方法。使用 `TransactWriteItems` 时，DynamoDB 监控事务中的所有项目。在事务处理期间，如果任何项目被其他操作修改，则整个事务将被取消，DynamoDB 将返回 `TransactionCanceledException`。此行为提供了一种悲观并发控制，因为这样可以防止出现冲突的并发修改，而不是在事后检测。

## 使用事务进行锁定的情况
<a name="BestPractices_PessimisticLocking_WhenToUse"></a>

事务非常适合下列情况：
+ 您需要以原子方式更新多个项目，无论是在同一个表还是多个表中。
+ 您的业务逻辑需要全有或全无语义，即要么所有更改都成功，要么不应用任何更改。

常见的例子包括账户之间的资金转移、同时更新库存和订单表的下订单行为，以及在游戏中玩家之间交换物品。

## 权衡
<a name="BestPractices_PessimisticLocking_Tradeoffs"></a>

**写入成本更高**  
对于不超过 1 KB 的项目，每个项目的事务会消耗 2 WCU（一个用于准备，一个用于提交），而标准写入消耗 1 WCU。

**项目限制**  
一个事务可以包含对一个或多个表的最多 100 个操作。

**冲突敏感性**  
如果事务中的任何项目被其他操作修改，则整个事务失败。在高争用率的场景中，这可能会导致频繁取消。

## 实现
<a name="BestPractices_PessimisticLocking_Implementation"></a>

以下示例使用 `TransactWriteItems` 以原子方式在两个项目之间转移库存。如果其他进程在事务处理期间修改了任一项目，则整个操作都将回退。

```
import boto3

client = boto3.client('dynamodb')

def transfer_inventory(source_id, target_id, quantity):
    try:
        client.transact_write_items(
            TransactItems=[
                {
                    'Update': {
                        'TableName': 'Inventory',
                        'Key': {'ItemID': {'S': source_id}},
                        'UpdateExpression': 'SET QuantityLeft = QuantityLeft - :qty',
                        'ConditionExpression': 'QuantityLeft >= :qty',
                        'ExpressionAttributeValues': {
                            ':qty': {'N': str(quantity)}
                        }
                    }
                },
                {
                    'Update': {
                        'TableName': 'Inventory',
                        'Key': {'ItemID': {'S': target_id}},
                        'UpdateExpression': 'SET QuantityLeft = QuantityLeft + :qty',
                        'ExpressionAttributeValues': {
                            ':qty': {'N': str(quantity)}
                        }
                    }
                }
            ]
        )
        return True
    except client.exceptions.TransactionCanceledException as e:
        print(f"Transaction canceled: {e}")
        return False
```

在此示例中，条件表达式检查是否存在足够的库存，但不需要版本属性。在准备阶段和提交阶段之间，如果事务中的任何项目被其他操作修改，DynamoDB 会自动取消事务。这种机制提供了悲观并发控制，事务本身可以防止相互冲突的并发修改。

**注意**  
您可以通过添加版本检查作为附加的条件表达式，将事务与乐观锁结合起来。这可以提供额外的保护措施，但并不是事务检测冲突所必需的。

有关更多信息，请参阅 [使用 DynamoDB 事务管理复杂工作流](transactions.md)。

# 使用 DynamoDB 锁客户端进行分布式锁定
<a name="BestPractices_DistributedLocking"></a>

DynamoDB 锁客户端是一个开源库，对于需要传统“锁定-获取-释放”语义的应用程序，该客户端使用 DynamoDB 表作为锁定存储来实施分布式锁定。当您需要对多个应用程序实例协调对外部资源（例如 S3 对象或共享配置）的访问时，此方法非常有用。

锁客户端以开源 [Java 库](https://github.com/awslabs/amazon-dynamodb-lock-client)的形式提供。

## 工作原理
<a name="BestPractices_DistributedLocking_HowItWorks"></a>

锁客户端使用专用 DynamoDB 表来跟踪锁定。每个锁定都表示为具有以下关键属性的项目：
+ 分区键，标识锁定的资源。
+ 租赁持续时间，指定锁的有效期限。如果锁持有程序崩溃或无响应，则该锁将在租赁持续时间过后自动过期。
+ 检测信号，由锁持有程序定期发送，用于延长租赁。这样可以防止持有程序仍在活跃地进行处理时，锁定变得过期。

锁客户端使用条件写入来确保一次只有一个进程可以获取某个锁。如果锁已经被持有，则调用方可以选择等待并重试或者立即失败。

## 使用锁客户端的情况
<a name="BestPractices_DistributedLocking_WhenToUse"></a>

锁客户端非常适合以下情况：
+ 需要跨多个应用程序实例或微服务协调对共享资源的访问。
+ 关键环节的运行时间很长（几秒到几分钟），出现冲突时重试整个操作成本很高。
+ 您需要自动锁定到期来妥善处理进程故障。

常见的示例包括编排分布式工作流、跨多个实例协调 cron 作业以及管理对共享外部资源的访问。

## 权衡
<a name="BestPractices_DistributedLocking_Tradeoffs"></a>

**其他基础设施**  
需要专用 DynamoDB 表用于锁定管理，并且需要额外的读取和写入容量用于锁定操作和检测信号。

**时钟依赖性**  
锁定到期取决于时间戳。客户端之间的严重时钟偏差可能会导致意外行为，尤其是对于较短的租赁持续时间。

**死锁风险**  
如果应用程序在多个资源上获取锁定，则必须按一致的顺序进行获取以避免死锁。租赁持续时间可以自动从未响应的持有程序上释放锁定，提供了一层安全保护机制。

## 实现
<a name="BestPractices_DistributedLocking_Implementation"></a>

以下示例说明如何使用 DynamoDB 锁客户端获取和释放锁定：

```
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

final DynamoDbClient dynamoDB = DynamoDbClient.builder()
    .region(Region.US_WEST_2)
    .build();

final AmazonDynamoDBLockClient lockClient = new AmazonDynamoDBLockClient(
    AmazonDynamoDBLockClientOptions.builder(dynamoDB, "Locks")
        .withTimeUnit(TimeUnit.SECONDS)
        .withLeaseDuration(10L)
        .withHeartbeatPeriod(3L)
        .withCreateHeartbeatBackgroundThread(true)
        .build());

// Try to acquire a lock on a resource
final Optional<LockItem> lock =
    lockClient.tryAcquireLock(AcquireLockOptions.builder("my-shared-resource").build());

if (lock.isPresent()) {
    try {
        // Perform operations that require exclusive access
        processSharedResource();
    } finally {
        // Always release the lock when done
        lockClient.releaseLock(lock.get());
    }
} else {
    System.out.println("Failed to acquire lock.");
}

lockClient.close();
```

**重要**  
始终释放 `finally` 块中的锁，以确保即使处理逻辑引发异常也会释放锁定。未释放的锁定会阻塞其他进程，直到租赁到期。

您还可以直接使用条件写入，来实施无需锁客户端库的简单锁定机制。以下示例使用 `UpdateItem` 与条件表达式来获取锁定，使用 `DeleteItem` 将其释放：

```
from datetime import datetime, timedelta
from boto3.dynamodb.conditions import Attr

def acquire_lock(table, resource_name, owner_id, ttl_seconds):
    """Attempt to acquire a lock. Returns True if successful."""
    expiry = (datetime.now() + timedelta(seconds=ttl_seconds)).isoformat()
    now = datetime.now().isoformat()
    try:
        table.update_item(
            Key={'LockID': resource_name},
            UpdateExpression='SET #owner = :owner, #expiry = :expiry',
            ConditionExpression=Attr('LockID').not_exists() | Attr('ExpiresAt').lt(now),
            ExpressionAttributeNames={'#owner': 'OwnerID', '#expiry': 'ExpiresAt'},
            ExpressionAttributeValues={':owner': owner_id, ':expiry': expiry}
        )
        return True
    except table.meta.client.exceptions.ConditionalCheckFailedException:
        return False

def release_lock(table, resource_name, owner_id):
    """Release a lock. Only succeeds if the caller is the lock owner."""
    try:
        table.delete_item(
            Key={'LockID': resource_name},
            ConditionExpression=Attr('OwnerID').eq(owner_id)
        )
        return True
    except table.meta.client.exceptions.ConditionalCheckFailedException:
        return False
```

此方法使用条件表达式来确保只能获取尚不存在的锁或者已过期的锁，并且只能由获取该锁的进程释放锁定。请考虑在锁定表上启用[生存时间（TTL）](TTL.md)来自动清理过期的锁定项目。

## 选择并发控制策略
<a name="BestPractices_ChoosingLockingStrategy"></a>

按照以下准则来为工作负载选择正确的方法：

在下列情况下，**使用乐观锁**：  
+ 很少出现冲突。
+ 重试失败写入的成本较低。
+ 一次更新一个项目。

在下列情况下，**使用事务**：  
+ 需要以原子方式更新多个项目。
+ 需要跨项目或表的全有或全无语义。
+ 需要在单个操作中将条件检查和写入操作结合起来。

在下列情况下，**使用锁客户端**：  
+ 需要跨分布式进程协调对外部资源的访问。
+ 关键环节运行时间长，出现冲突时重试的成本很高。
+ 需要自动锁定到期来处理进程故障。

**注意**  
如果您使用 [DynamoDB 全局表](GlobalTables.md)，请注意对于并发更新，全局表使用“以最后一次写入者为准”的协调策略。在需要跨区域时，使用版本号实施乐观锁将无法正常工作，因为在一个区域进行写入操作可能会覆盖另一个区域的并发写入而没有版本检查。使用全局表时，请将应用程序设计为在应用程序级处理冲突。

# 了解 DynamoDB 中的 Amazon 账单和使用情况报告的最佳实践
<a name="bp-understanding-billing"></a>

 本文档解释了与 DynamoDB 相关的费用的 `UsageType` 账单代码。

Amazon 提供了包含所用服务的数据的成本和使用情况报告（CUR）。您可以使用 Amazon 成本和使用情况报告 以 CSV 格式将账单报告发布到 Amazon S3。设置 CUR 时，您可以选择按小时、天或月细分时间段，也可以选择是否要按资源 ID 对使用情况进行细分。有关生成 CUR 的更多详细信息，请参阅[创建成本和使用情况报告](https://docs.amazonaws.cn/cur/latest/userguide/creating-cur.html)

在 CSV 导出中，您将找到每行列出的相关属性。以下是可能包含的属性的示例：
+ **lineitem/UsageStartDate：**用 UTC 表示的行项目的开始日期和时间（含该日期和时间）。
+ **lineitem/UsageEndDate：**用 UTC 表示的对应行项目的结束日期和时间（不含该日期和时间）。
+ **lineitem/ProductCode：**对于 DynamoDB 来说，这将是“AmazonDynamoDB”
+ **lineitem/UsageType：**使用类型的特定描述代码，如本文档所列举
+ **lineItem/Operation：**为费用提供上下文的名称，例如产生费用的操作名称（可选）。
+ **lineitem/ResourceId：**产生使用量的资源的标识符。如果 CUR 包含按资源 ID 划分的明细，则可用。
+ **lineitem/UsageAmount：**在指定时间段内产生的用量。
+ **lineitem/UnblendedCost：**此用量的成本。
+ **lineitem/LineItemDescription：**行项目的文字描述。

有关 CUR 数据字典的更多信息，请参阅[成本和使用情况报告（CUR）2.0](https://docs.amazonaws.cn/cur/latest/userguide/table-dictionary-cur2.html)。请注意，确切的名称因上下文而异。

`UsageType` 是一个字符串，包含值 `ReadCapacityUnit-Hrs`、`USW2-ReadRequestUnits`、`EU-WriteCapacityUnit-Hrs`、或 `USE1-TimedPITRStorage-ByteHrs` 等。每种使用类型都以可选的区域前缀开头。如果没有，则表示 us-east-1 区域。如果有，则下表将简短的计费区域代码与传统的区域代码和名称对应起来。

例如，名为 `USW2-ReadRequestUnits` 的使用表示在 us-west-2 中消耗的读取请求单位。


****  

| 账单区域代码 | 区域代码 | 区域名称 | 
| --- | --- | --- | 
| AFS1 | af-south-1 | 非洲（开普敦） | 
| APE1 | ap-east-1 | 亚太地区（香港） | 
| APN1 | ap-northeast-1 | 亚太地区（东京） | 
| APN2 | ap-northeast-2 | 亚太地区（首尔） | 
| APN3 | ap-northeast-3 | 亚太地区（大阪） | 
| APS1 | ap-southeast-1 | 亚太地区（新加坡） | 
| APS2 | ap-southeast-2 | 亚太地区（悉尼） | 
| APS3 | ap-south-1 | 亚太地区（孟买） | 
| APS4 | ap-southeast-3 | 亚太地区（雅加达） | 
| APS5 | ap-south-2 | 亚太地区（海得拉巴） | 
| APS6 | ap-southeast-4 | 亚太地区（墨尔本） | 
| CAN1 | ca-central-1 | 加拿大（中部） | 
| EU | eu-west-1 | 欧洲地区（爱尔兰） | 
| EUC1 | eu-central-1 | 欧洲地区（法兰克福） | 
| EUC2 | eu-central-2 | 欧洲（苏黎世） | 
| EUN1 | eu-north-1 | 欧洲地区（斯德哥尔摩） | 
| EUS1 | eu-south-1 | 欧洲地区（米兰） | 
| EUS2 | eu-south-2 | 欧洲（西班牙） | 
| EUW1 | eu-west-1 | 欧洲地区（爱尔兰） | 
| EUW2 | eu-west-2 | 欧洲地区（伦敦） | 
| EUW3 | eu-west-3 | 欧洲地区（巴黎） | 
| ILC1 | Il-central-1 | 以色列（特拉维夫） | 
| MEC1 | me-central-1 | 中东（阿联酋） | 
| MES1 | me-south-1 | 中东（巴林） | 
| SAE1 | sa-east-1 | 南美洲（圣保罗） | 
| USE1（默认） | us-east-1 | 美国东部（弗吉尼亚州北部） | 
| USE2 | us-east-2 | 美国东部（俄亥俄州） | 
| UGE1 | us-gov-east-1 | US Government East | 
| UGW1 | us-gov-west-1 | US Government West | 
| USW1 | us-west-1 | 美国西部（北加利福尼亚） | 
| USW2 | us-west-2 | 美国西部（俄勒冈州） | 

在以下各节中，我们在计算 DynamoDB 的费用时使用 `REG-UsageType` 模式，其中 REG 指定使用量发生的区域，usageType 是费用类型的代码。例如，如果您在 CSV 文件中看到 `USW1- ReadCapacityUnit-Hrs` 的行项目，则表示在 US-West-1 中使用了预置读取容量。在这种情况下，清单会显示为 `REG-ReadCapacityUnit-Hrs`。

**Topics**
+ [吞吐能力](#bp-understanding-billing.throughput)
+ [流](#bp-understanding-billing.streams)
+ [仓储服务](#bp-understanding-billing.storage)
+ [备份与还原](#bp-understanding-billing.backup)
+ [数据传输](#bp-understanding-billing.datatransfer)
+ [CloudWatch Contributor Insights](#bp-understanding-billing.cw)
+ [DynamoDB Accelerator (DAX)](#bp-understanding-billing.dax)

## 吞吐能力
<a name="bp-understanding-billing.throughput"></a>

**预置容量读取和写入**

在预置容量模式下创建 DynamoDB 表时，您可以指定应用程序所需的读取和写入容量。使用类型取决于您的表类（标准或标准-不频繁访问）。您可以根据每秒的消耗率预置读取和写入，但费用是根据预置容量按小时计费的。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-ReadCapacityUnit-Hrs | RCU 小时数 | 小时 | 使用“标准”表类在预置容量模式下进行读取的费用。 | 
| REG-IA-ReadCapacityUnit-Hrs  | RCU 小时数 | 小时 | 使用“标准 - IA”表类在预置容量模式下进行读取的费用。 | 
| REG-WriteCapacityUnit-Hrs | WCU 小时数 | 小时 | 使用“标准”表类在预置容量模式下进行写入的费用。 | 
| REG-IA-WriteCapacityUnit-Hrs  | WCU 小时数 | 小时 | 使用“标准 - IA”表类在预置容量模式下进行写入的费用。 | 

**预留容量读取和写入**

通过预留容量，您可以支付一次性预付费用并承诺在一段时间内支付最低预置用量级别的费用。预留容量按折扣小时费率计费。对于您预置的超出预留容量的任何容量，将按照标准的预置容量费率收费。预留容量可用于使用标准表类的 DynamoDB 表上的单区域、预置读取和写入容量单位（RCU 和 WCU）。1 年期和 3 年期预留容量均使用相同的 SKU 进行计费。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-HeavyUsage:dynamodb.read | RCU 小时数 | 预付费，然后按月付费 | 预留容量读取费用：一次性预付费和每月初月度费用，涵盖当月所有折扣承诺 RCU 小时数。将有匹配的零成本 REG-ReadCapacityUnit-Hrs 行项目。 | 
| REG-HeavyUsage:dynamodb.write | WCU 小时数 | 预付费，然后按月付费 | 预留容量写入费用：一次性预付费和每月初月度费用，涵盖当月所有折扣承诺 WCU 小时数。将有匹配的零成本 REG-WriteCapacityUnit-Hrs 行项目。 | 

**按需容量读取和写入**

在按需容量模式下创建 DynamoDB 表时，您只需为应用程序执行的读取和写入付费。读取和写入请求的价格取决于您的表类。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-ReadRequestUnits | RRUs | 单位 | 在按需容量模式下使用“标准”表类进行读取的费用。 | 
| REG-IA-ReadRequestUnits | RRUs | 单位 | 在按需容量模式下使用“标准 - IA”表类进行读取的费用。 | 
| REG-WriteRequestUnits | WRU | 单位 | 在按需容量模式下使用“标准”表类进行写入的费用。 | 
| REG-IA-WriteRequestUnits | WRU | 单位 | 在按需容量模式下使用“标准 - IA”表类进行写入的费用。 | 

**全局表读取和写入**

DynamoDB 根据每个副本表上使用的资源对全局表的使用收费。对于预置全局表，全局表的写入请求以复制的 WCU (rwCu) 而不是标准 WCU 来衡量，对全局表中全局二级索引的写入以 WCU 来衡量。对于按需全局表，写入请求以复制的 WRU (rwRU) 而不是标准 WRU 来衡量。复制而消耗的 rWCU 或 rWRU 数量取决于您使用的全局表的版本。定价取决于您的表类。

对全局二级索引（GSI）的写入按标准写入单位（WCU 和 WRU）计费。读取请求和数据存储的计费方式与单区域表相同。

 如果您添加表副本以在新区域中创建或扩展全局表，则 DynamoDB 会按恢复的数据（GB）对已添加区域中的表恢复进行收费。已恢复的数据按照 REG-RestoreDataSize-Bytes 收费。详情请参阅[DynamoDB 的备份和还原](Backup-and-Restore.md)。跨区域复制和向包含数据的表中添加副本也会产生数据传输费用。

 当您为 DynamoDB 全局表选择按需容量模式时，您只需为应用程序在每个副本表上使用的资源付费。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-ReplWriteCapacityUnit-Hrs | rWCU 小时数 | 小时 | 全局表、预置、“标准”表类。 | 
| REG-IA-ReplWriteCapacityUnit-Hrs | rWCU 小时数 | 小时 | 全局表、预置、“标准 - IA”表类。 | 
| REG-ReplWriteRequestUnits  | rWRU | 单位 | 全局表、按需、“标准”表类。 | 
| REG-IA-ReplWriteRequestUnits | rWRU | 单位 | 全局表、按需、“标准 - IA”表类。 | 

## 流
<a name="bp-understanding-billing.streams"></a>

DynamoDB 有两种流技术，DynamoDB Streams 和 Kinesis。每个都有单独的定价。

DynamoDB Streams 按读取请求单位对读取数据收取费用。每个 `GetRecords` API 调用都按流读取请求计费。对于由 Amazon Lambda 因 DynamoDB 触发器而调用或者 DynamoDB 全局表因复制而调用的 `GetRecords` API 调用，您无需付费。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-Streams-RequestsCount | 计数 | 单位 | DynamoDB Streams 的读取请求单位。 | 

Amazon Kinesis Data Streams 按更改数据捕获单元收取费用。DynamoDB 针对每个写入收取一个更改数据捕获单元的费用（最多 1 KB）。大于 1 KB 的项目需要额外的更改数据捕获单元。您只需为应用程序执行的写入付费，而不必管理表上的吞吐能力。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-ChangeDataCaptureUnits-Kinesis | CDC 单位 | 单位 | Kinesis Data Streams 的更改数据捕获单元。 | 

## 仓储服务
<a name="bp-understanding-billing.storage"></a>

DynamoDB 通过将数据的原始字节大小加上数据的原始字节大小加上按项目的存储开销（这取决于您启用的功能）来衡量计费数据的大小。

**注意**  
使用 `DescribeTable` 时，CUR 中的存储使用量值将高于存储值，因为 `DescribeTable` 不包括每个项目的存储开销。

存储按小时计算，但按月定价，按小时费用的平均值计算。

尽管存储 `UsageType` 将 `ByteHrs` 作为后缀，但 CUR 中的存储使用量以 GB 为单位，按月 GB 定价。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-TimedStorage-ByteHrs | GB | Month | 对于“标准”表类的表，您的 DynamoDB 表和索引使用的存储量。 | 
| REG-IA-TimedStorage- ByteHrs | GB | Month | 对于“标准 - IA”表类的表，您的 DynamoDB 表和索引使用的存储量。 | 

## 备份与还原
<a name="bp-understanding-billing.backup"></a>

DynamoDB 提供两种类型的备份：时间点故障恢复（PITR）备份和按需备份。用户还可以从这些备份恢复到 DynamoDB 表中。以下费用包括备份和恢复。

备份存储费用是在当月的第一天产生的，整个月内会随着备份的添加或删除进行相应调整。有关更多信息，请参阅 [Understanding Amazon DynamoDB On-demand Backups and Billing](https://repost.aws/articles/AR74LYumctRa-t7Z87uwKrlw) 博客


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-TimedBackupStorage-ByteHrs | GB | Month | 按需备份 DynamoDB 表和本地二级索引所使用的存储。 | 
| TimedPITRStorage-ByteHrs | GB | Month | 时间点故障恢复（PITR）备份使用的存储。只要启用了 PITR，DynamoDB 会在整个月中持续监控您的启用 PITR 的表的大小，确定您存储的备份费用和账单。 | 
| REG-RestoreDataSize-Bytes | GB | 大小 | 从 DynamoDB 备份中恢复的数据总大小（包括表数据、本地二级索引和全局二级索引），以 GB 为单位。 | 

### Amazon Backup
<a name="bp-understanding-billing.aws-backup"></a>

Amazon Backup 是一项完全托管的备份服务，有助于轻松地在云中以及本地集中管理和自动执行跨 Amazon 服务的数据备份。Amazon Backup 按存储（热存储或冷存储）、恢复活动和跨区域数据传输收费。以下 `UsageType` 费用显示在“AmazonBackup”产品代码下，而不是“AmazonDynamoDB”下。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-WarmStorage- ByteHrs-DynamoDB | GB | Month | 整个月中 DynamoDB 备份使用的存储由 Amazon Backup 管理，以 GB/月为单位。 | 
| REG-CrossRegion-WarmBytes-DynamoDB | GB | 大小 | 传输到不同 Amazon 区域中的同一个账户或不同 Amazon 账户的数据。在将备份从一个区域复制到另一区域时，会产生跨区域传输费用。费用始终计入从中传输数据的账户。 | 
| REG-Restore-WarmBytes-DynamoDB | GB | 大小 | 从热存储中恢复的数据总大小，以 GB 为单位。 | 
| REG-ColdStorage-ByteHrs-DynamoDB | GB | Month | 整个月中 DynamoDB 备份使用的冷存储由 Amazon Backup 管理，以 GB/月为单位。 | 
| REG-Restore-ColdBytes-DynamoDB | GB | Month | 从冷存储中恢复的数据总大小，以 GB 为单位。 | 

### 导出和导入
<a name="bp-understanding-billing.export-import"></a>

 您可以将数据从 DynamoDB 导出到 Amazon S3 或将数据从 Amazon S3 导入新的 DynamoDB 表。

尽管 `UsageType` 使用 `Bytes` 作为后缀，但 CUR 中的导出和导入使用量是以 GB 计量和定价的。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-ExportDataSize-Bytes | GB | 大小 | 将数据导出到 S3 的费用。DynamoDB 按指定导出创建时间点的 DynamoDB 基表大小（表数据和本地二级索引）对您导出的数据收费。 | 
| REG-ImportDataSize-Bytes | GB | 大小 | 从 S3 导入数据的费用。大小是根据 Amazon S3 中数据的未压缩对象大小计算得出的。使用 GSI 导入到表不会产生额外费用。 | 
| REG-IncrementalExportDataSize-Bytes | GB | 大小 | 为产生增量导出而从连续备份中处理的数据大小的费用。 | 

## 数据传输
<a name="bp-understanding-billing.datatransfer"></a>

数据传输活动可能显示为与 DynamoDB 服务相关联。DynamoDB 不对入站数据传输收费，也不对同一 Amazon 区域内 DynamoDB 与其它 Amazon 服务之间的数据传输收费（换言之，即每 GB 0 美元）。跨 Amazon 区域（例如在美国东部 [弗吉尼亚州北部] 区域的 DynamoDB 和欧洲地区 [爱尔兰] 区域的 Amazon EC2 之间）传输的数据将对传输双方收费。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-DataTransfer-In-Bytes | GB | 单位 | 从互联网传输到 DynamoDB 的数据 | 
| REG-DataTransfer-Out-Bytes | GB | 单位 | 从 DynamoDB 传输到互联网的数据。 | 

## CloudWatch Contributor Insights
<a name="bp-understanding-billing.cw"></a>

CloudWatch Contributor Insights for DynamoDB 是一款诊断工具，可用于识别 DynamoDB 表中最常访问和最常受限制的键。以下 `UsageType` 费用显示在“AmazonCloudWatch”产品代码下，而不是“AmazonDynamoDB”下。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-CW:ContributorEventsManaged | 处理的事件 | 单位 | 处理的 DynamoDB 事件数量。例如，对于启用了 CloudWatch Contributor Insights 的表，每当读取或写入项目时，它都算作一个事件。如果该表具有排序键，则会产生两个事件的费用。 | 
| REG-CW:ContributorRulesManaged | 规则计数 | Month | 当您启用 CloudWatch Contributor Insights 时，DynamoDB 会创建规则来识别最常访问和最常受限制的键。这笔费用产生自针对为记录 CloudWatch Contributor Insights 而配置的每个实体（表和 GSI）所添加的规则。 | 

## DynamoDB Accelerator (DAX)
<a name="bp-understanding-billing.dax"></a>

DynamoDB Accelerator（DAX）根据为服务选择的实例类型按小时计费。以下费用是指预置的 DynamoDB Accelerator 实例。以下 `UsageType` 费用显示在“AmazonDAXh”产品代码下，而不是“AmazonDynamoDB”下。


****  

| UsageType | 单位 | 粒度 | 说明 | 
| --- | --- | --- | --- | 
| REG-NodeUsage:dax-<INSTANCETYPE> | 节点小时数 | 小时 | 特定实例类型的每小时使用量。从节点启动到终止，按消耗的节点小时数计费。消耗的节点小时不足一小时，将按一小时计费。DAX 对 DAX 集群中的每个节点收费。如果您的集群包含多个节点，则会在账单报告中看到多个行项目。 | 

实例类型将是以下列表中的一个值。有关节点类型的详细信息，请参阅 [Nodes](DAX.concepts.cluster.md#DAX.concepts.nodes)。
+ r3.2xlarge、r4.8xlarge 或 r5.8xlarge
+ r3.4xlarge、r4.large 或 r5.large
+ r3.8xlarge、r4.xlarge 或 r5.xlarge
+ r3.2xlarge、r5.12xlarge 或 t2.medium
+ r3.4xlarge、r4.large 或 r5.large
+ r3.xlarge、r5.16xlarge 或 t2.small
+ r4.16xlarge、r5.24xlarge 或 t3.medium
+ r4.2xlarge、r5.2xlarge 或 t3.small
+ r4.4xlarge 或 r5.4xlarge

# 将 DynamoDB 表从一个账户迁移到另一个账户
<a name="bp-migrating-table-between-accounts"></a>

您可以将 Amazon DynamoDB 表从一个账户迁移到另一个账户，以实施多账户策略或备份策略。您也可以出于测试、调试或合规性原因执行此操作。一个常见的用例是在生产、生产前调试、测试和开发环境中复制 DynamoDB 表，其中每个环境都使用不同的 Amazon 账户。

DynamoDB 提供两种将表从一个 Amazon 账户迁移到另一个账户的选项：
+ **Amazon Backup 跨账户备份和还原**：Amazon Backup 是一项完全托管的备份服务，可帮助您集中管理多个 Amazon 服务的备份。借助其跨账户备份和还原功能，您可以在一个账户中备份 DynamoDB 表，然后将该备份还原到同一 Amazon 组织中的另一个账户。
+ **DynamoDB 导出和导入至 Amazon S3**：使用 DynamoDB 导出和导入至 Amazon S3 功能，您可以将数据完全导出到 Amazon S3 存储桶，然后将这些数据导入另一个 Amazon 账户的新表中。当您需要在不属于同一 Amazon 组织的账户之间迁移或者不想使用 Amazon Backup 时，这种方法非常有用。

**注意**  
从 Amazon S3 导入不支持带有本地二级索引（LSI）的表，但支持全局二级索引（GSI）。有关 LSI 和 GSI 的更多信息，请参阅[在 DynamoDB 中使用二级索引改进数据访问](SecondaryIndexes.md)。

**Topics**
+ [使用用于跨账户备份和还原的 Amazon Backup 迁移表](bp-migrating-table-between-accounts-backup.md)
+ [使用导出到 S3 和从 S3 导入功能来迁移表](bp-migrating-table-between-accounts-s3.md)

# 使用用于跨账户备份和还原的 Amazon Backup 迁移表
<a name="bp-migrating-table-between-accounts-backup"></a>

**先决条件**
+ 源和目标 Amazon 账户在 Amazon Organizations 服务中必须属于同一组织
+ 验证 Amazon Identity and Access Management（IAM）权限，以创建和使用 Amazon Backup 保管库

有关设置跨账户备份的更多信息，请参阅[跨 Amazon 账户创建备份副本](https://docs.amazonaws.cn/aws-backup/latest/devguide/create-cross-account-backup.html)。

**定价信息**

Amazon 对备份（基于表大小）、Amazon 区域之间的任何数据复制（基于数据量）、还原（基于数据量）以及任何正在进行的存储活动收费。为避免持续收费，如果在还原后不需要备份，可以删除该备份。

有关定价的更多信息，请参阅 [Amazon Backup 定价](https://www.amazonaws.cn/backup/pricing/)。

## 步骤 1：启用 DynamoDB 和跨账户备份高级功能。
<a name="bp-migrating-table-between-accounts-backup-enable-advanced-features"></a>

1. 在源和目标 Amazon 账户中，访问 Amazon 管理控制台并打开 Amazon Backup 控制台。

1. 选择**设置**选项。

1. 在 **Amazon DynamoDB 备份高级功能**下，确认已启用**高级功能**。如果没有，请选择**启用**。

1. 在**跨账户管理**的**跨账户备份**下，选择**开启**。

## 步骤 2：在源账户和目标账户中创建备份保管库
<a name="bp-migrating-table-between-accounts-backup-create-backup-vault"></a>

1. 在源 Amazon 账户中，打开 Amazon Backup 控制台。

1. 选择**备份保管库**。

1. 选择**创建备份保管库**。

1. 复制并保存已创建备份保管库和目标 Amazon 账户的 **Amazon 资源名称（ARN）**。

1. 在账户之间复制 DynamoDB 表备份时，您将需要源和目标备份保管库的 ARN。

## 步骤 3：在源账户中，创建 DynamoDB 表备份。
<a name="bp-migrating-table-between-accounts-backup-create-table-backup"></a>

1. 在 **Amazon Backup 控制面板**页面上，选择**创建按需备份**。

1. 在**设置**部分，选择 **DynamoDB** 作为**资源类型**，然后选择表名。

1. 在**备份保管库**下拉列表，选择您在源账户中创建的备份保管库。

1. 选择所需的**保留期**。

1. 选择**创建按需备份**。

1. 在 **Amazon Backup 作业**页面的**备份作业**选项卡上，监控备份作业的状态。

## 步骤 4：将 DynamoDB 表备份从源账户复制到目标账户
<a name="bp-migrating-table-between-accounts-backup-copy-table-backup"></a>

1. 完成备份作业后，在源账户中打开 Amazon Backup 控制台，然后选择**备份保管库**。

1. 在**备份**下，选择 DynamoDB 表备份。选择**操作**，然后选择**复制**。

1. 输入目标账户所在的 Amazon 区域。

1. 对于**外部保管库 ARN**，请输入您在目标账户中创建的备份保管库的 ARN。

1.  在目标账户的备份保管库中，启用允许从源账户访问的权限以便复制备份。

## 步骤 5：还原目标账户中的 DynamoDB 表备份
<a name="bp-migrating-table-between-accounts-restore-table-backup"></a>

1. 在目标 Amazon 账户中，打开 Amazon Backup 控制台并选择**备份保管库**。

1. 在**备份**下，选择从源账户复制的备份。选择**操作**，然后选择**还原**。

1. 输入新 DynamoDB 表的名称、此新表将采用的加密方式、加密还原时要使用的密钥以及任何其他选项。

1. 还原完成后，表的状态将显示为**活动**。

# 使用导出到 S3 和从 S3 导入功能来迁移表
<a name="bp-migrating-table-between-accounts-s3"></a>

**先决条件**
+ 必须为表启用时间点故障恢复（PITR）才能执行导出到 S3 的操作。有关更多信息，请参阅[在 DynamoDB 中启用时间点恢复](PointInTimeRecovery_Howitworks.md)。
+ 具有执行导出的有效 IAM 权限。有关更多信息，请参阅 [在 DynamoDB 中请求表导出](S3DataExport_Requesting.md)。
+ 具有足以执行导入的有效 IAM 权限。有关更多信息，请参阅 [在 DynamoDB 中请求表导入](S3DataImport.Requesting.md)。

**定价信息**

Amazon 对 PITR（基于表大小和启用 PITR 的时长）收费。如果您除了导出之外不需要 PITR 功能，则可以在导出结束后将其关闭。Amazon 还会对向 S3 发出请求、将导出的数据存储在 S3 中以及导入（基于导入数据的未压缩大小）收费。

有关 DynamoDB 定价的更多信息，请参阅 [DynamoDB 定价](https://www.amazonaws.cn/dynamodb/pricing/)。

**注意**  
 从 S3 导入 DynamoDB 时，对对象的大小和数量有一些限制。有关更多信息，请参阅 [导入配额](S3DataImport.Validation.md#S3DataImport.Validation.limits)。

## 请求将表导出到 Amazon S3
<a name="bp-migrating-table-between-accounts-s3-table-export"></a>

1. 登录 Amazon 管理控制台，打开 DynamoDB 控制台。

1. 在控制台左侧的导航窗格中，选择**导出到 S3**。

1. 选择源表和目标 S3 存储桶。使用 `s3://bucketname/prefix` 格式输入目标账户存储桶的 URL。`/prefix` 是一个可选文件夹，有助于您的目标存储桶保持井然有序。

1. 选择**完整导出**。完整导出会按照您指定的时间点输出表的完整表快照。

   1. 选择**当前时间**以导出最新的完整表快照。

   1. 对于**导出的文件格式**，请在 DynamoDB JSON 和 Amazon Ion 之间进行选择。默认选项是 DynamoDB JSON。

1. 单击**导出**按钮开始导出。

1. 小型表导出应在几分钟内完成，但 TB 级别的表可能需要一个多小时。

## 请求从 Amazon S3 导入表
<a name="bp-migrating-table-between-accounts-s3-table-import"></a>

1. 登录 Amazon 管理控制台，打开 DynamoDB 控制台。

1. 在控制台左侧的导航窗格中，选择 **Import from S3**（从 S3 导入）。

1. 在显示的页面上选择 **Import from S3**（从 S3 导入）。

1. 输入 Amazon S3 源 URL。也可以使用**浏览 S3** 按钮查找该 URL。预期的路径采用格式 `s3://bucket/prefix/AWSDynamoDB/<XXXXXXXX-XXXXXX>/data/`。

1. 指定您是否为 S3 存储桶拥有者。

1. 在**导入文件压缩**下，选择 **GZIP** 以匹配导出。

1. 在**导入文件格式**下，选择 **DynamoDB JSON** 以匹配导出。

1. 选择**下一步**。对于**指定表详细信息**，为将创建用于存储数据的新表选择相应选项。

1. 选择**下一步**。对于**配置表设置**，请自定义任何其它表设置（如果适用）。

1. 选择**下一步**再次查看导入选项，然后单击**导入**开始导入任务。您会在**从 S3 导入**下看到列出了新表，其状态为**正在导入**。此时无法访问您的表。小规模导入应在几分钟内完成，但 TB 级别的表可能需要一个多小时。

1. 导入完成后，状态显示为**活动**，您可以开始使用该表。

## 在迁移期间保持表同步
<a name="bp-migrating-table-between-accounts-s3-table-sync"></a>

如果可以在迁移期间暂停对源表的写入操作，那么在迁移后源表和输出应该完全匹配。如果无法暂停写入操作，则迁移后目标表通常会稍微落后于源表。要追踪源表，可以使用流媒体（DynamoDB Streams 或 Kinesis Data Streams for DynamoDB）来重播自备份或导出以来源表中发生的写入操作。

在将源表导出到 S3 时，您应该在时间戳之前开始读取流记录。例如，如果向 S3 的导出活动发生在下午 2:00，向目标表的导入活动在晚上 11:00 结束，则应在下午 1:58 启动 DynamoDB 流读取活动。用于更改数据捕获表的流式处理选项总结了每种流式处理模式的功能。

将 DynamoDB Streams 与 Lambda 结合使用提供了一种在源表和目标 DynamoDB 表之间同步数据的简化方法。可以使用 Lambda 函数重播目标表中的每一次写入操作。

**注意**  
项目会在 DynamoDB Streams 中保存 24 小时，因此您应该计划在该时段内完成备份和还原或导出和导入。

# 将 DAX 与 DynamoDB 应用程序集成的规范性指南
<a name="dax-prescriptive-guidance"></a>

[DynamoDB Accelerator](DAX.md)（DAX）是一项与 DynamoDB 兼容的缓存服务，可为要求严苛的应用程序（如读取密集型应用程序）提供快速的内存中性能。使用 DAX，您可以加快访问经常请求的数据所需的响应时间（以微秒为单位）。这份 DynamoDB Accelerator 规范性指南提供了将 DAX 与 DynamoDB 应用程序集成的全面见解和最佳实践。

本指南为那些刚接触 DAX 或者想要优化其现有配置的用户提供了基础知识。本指南涵盖各种主题，例如，何时使用 DAX 和创建 [DAX 集群](DAX.concepts.cluster.md#DAX.concepts.clusters)。它还包括实际示例和详细说明，能够帮助您在项目中有效地实施 DAX。最后，本指南提供了您需要实施的高级策略，可帮助您尽可能提高 DAX 缓存能力，从而加快应用程序运行速度并提高其扩展能力。

**Topics**
+ [评估 DAX 是否适合您的用例](evaluate-dax-suitability.md)
+ [配置 DAX 客户端](dax-config-dax-client.md)
+ [配置 DAX 集群](dax-config-considerations.md)
+ [设置 DAX 集群的容量](dax-cluster-sizing.md)
+ [部署集群](dax-deploy-cluster.md)
+ [管理集群操作](dax-cluster-operations.md)
+ [监控 DAX](pres-guide-monitor-dax.md)

# 评估 DAX 是否适合您的用例
<a name="evaluate-dax-suitability"></a>

本节说明何时以及为何使用 DAX。使用本指南可以帮助您确定将 DAX 与 DynamoDB 集成是否符合应用程序的工作负载模式、性能要求和数据一致性需求。本指南还介绍了可能不适合使用 DAX 的场景，例如写入密集型工作负载和不经常访问的数据。

**Topics**
+ [何时以及为何选择 DAX](#choose-dax)
+ [何时不使用 DAX](#dax-unsuitable-scenarios)

## 何时以及为何选择 DAX
<a name="choose-dax"></a>

在几种情况下，您可以考虑将 DAX 添加到您的应用程序堆栈中。例如，可以使用 DAX 来减少针对 DynamoDB 的读取请求的总体延迟，或者最大限度减少对表中相同数据的重复读取。下表列出了您可以充分利用 DAX 与 DynamoDB 集成的场景示例：
+ **高性能要求**
  + **低延迟读取** – 如果应用程序要求快速响应（以微秒为单位）以实现最终一致性读取，则应考虑使用 DAX。DAX 还可以显著缩短访问频繁读取数据的响应时间。
+ **读取密集型工作负载**
  + **读取密集型应用程序** - 对于读写比率（例如 10:1 或更高）较高的应用程序，DAX 会提高缓存命中率并减少陈旧数据。这将减少对表的读取量。为了避免在应用程序写入量大的情况下从缓存读取陈旧的数据，请确保为缓存设置较低的[在 DynamoDB 中使用生存时间（TTL）](TTL.md)。
  + **缓存常见查询** – 如果您的应用程序经常读取相同的数据（例如电子商务平台上的热门产品），DAX 可以直接从其缓存中处理这些请求。
+ **突发流量模式**
  + **更顺畅的表扩展** – DAX 有助于缓解流量突然激增带来的影响。DAX 提供缓冲区来轻松纵向扩展 DynamoDB 表的容量，从而降低读取节流风险。
  + **提高了对每个项目的读取吞吐量** – DynamoDB 为每个项目分配单独的分区。但是，当项目达到 3,000 个读取[容量单位](provisioned-capacity-mode.md#read-write-capacity-units)（RCU）时，分区会开始限制对该项目的读取。DAX 允许您将单个项目的读取量扩展到 3,000 RCU 以上。
+ **成本优化**
  + **降低 DynamoDB 成本** – 从 DAX 读取数据可以减少发送到 DynamoDB 表的读取量，从而直接影响成本。缓存命中率较高时，降低的表读取成本可能会超过 DAX 集群成本，从而降低净成本。
+ **数据一致性要求**
  + **最终一致性** – DAX 支持最终一致性读取。这使得 DAX 适用于即时一致性并不重要的用例。
  + **直写缓存** – 您对 DAX 所进行的写入采用[直写](DAX.consistency.md)方式。一旦 DAX 确认已将项目写入 DynamoDB，它就会将该项目版本保留在项目缓存中。这种直写机制有助于在缓存和数据库之间保持更紧密的数据一致性，但会使用额外的 DAX 集群资源。

## 何时不使用 DAX
<a name="dax-unsuitable-scenarios"></a>

虽然 DAX 功能强大，但它并不适用于所有场景。下表举例说明了不适合将 DAX 与 DynamoDB 集成的场景：
+ **写入密集型工作负载** – DAX 的主要优势是加快读取速度，但写入操作使用的 DAX 资源多于读取操作。如果您的应用程序主要是写入密集型应用程序，那么 DAX 的优势可能会受到限制。
+ **不经常读取数据** – 如果您的应用程序不经常访问数据或访问大量很少重复使用的数据（冷数据），则可能会遇到[cache hit ratio](pres-guide-monitor-dax.md#cachehitratio)低的情况。在这种情况下，维护缓存的开销可能无法抵消性能收益。
+ **批量读取或写入** - 如果您的应用程序执行的批量写入操作多于单次写入操作，则应围绕 DAX 执行写入操作。此外，对于批量读取，您应该直接对 DynamoDB 表运行全表扫描。
+ **严格的一致性或事务要求** – DAX 将强一致性读取和 [TransactGetItem](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_TransactGetItems.html) 调用传递给 DynamoDB 表。您应该在 DAX 集群中进行这些读取，以避免使用集群资源。以这种方式读取的项目不会被缓存；因此，通过 DAX 传送此类项目没有任何意义。
+ **性能要求适中的简单应用程序** – 对于性能要求适中且可容忍直接 DynamoDB 延迟的应用程序，没必要增加 DAX 带来的复杂性和成本。DynamoDB 本身可以处理高吞吐量并提供个位数的毫秒性能。
+ **除了键值访问之外，还需要复杂查询** – DAX 针对键值访问模式进行了优化。如果您的应用程序需要复杂的查询和筛选功能（例如[查询](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_Query.html)和[扫描](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_Scan.html)操作），那么 DAX 缓存的优势可能会受到限制。

  在这种情况下，可使用 [Amazon ElastiCache (Redis OSS)](https://docs.amazonaws.cn/AmazonElastiCache/latest/red-ug/WhatIs.html) 作为替代方案。ElastiCache (Redis OSS) 支持高级数据结构，例如列表、集合和哈希。它还提供发布/订阅、地理空间索引和脚本编写等功能。
+ **合规要求** – DAX 目前不提供与 DynamoDB 相同的合规认证。例如，DAX 尚未获得 SOC 认证。

# 配置 DAX 客户端
<a name="dax-config-dax-client"></a>

DAX 集群是一个基于实例的集群，可以使用各种 DAX SDK 进行访问。每个 SDK 都为开发人员提供了可配置的选项，例如 requestTimeout 和连接，来满足特定的应用程序要求。

配置 DAX 客户端时，一个关键的考虑因素是客户端应用程序的规模，具体而言，是客户端实例与 DAX 服务器实例的比例（最大为 11）。大型客户端实例集可以生成与 DAX 服务器实例的大量连接，这可能会使它们不堪重负。本指南概述了 DAX 客户端配置的最佳实践。

## 最佳实践
<a name="dax-guidance-configuring-dax-client-best-practices"></a>

1. **客户端实例**：实现单例客户端实例，以确保跨请求重用实例。有关实施详细信息，请参阅[第 4 步：运行一个示例应用程序](DAX.client.run-application.md)。

1. **请求超时**：虽然应用程序通常需要较低的请求超时，才能确保上游系统的延迟降至最低，但将超时设置得过低可能会导致问题。当 DAX 服务器出现临时延迟峰值时，低超时可能会触发到服务器实例的频繁重新连接。在出现超时的情况下，DAX 客户端会终止现有的服务器节点连接，并建立新的服务器节点连接。由于建立连接是资源密集型的，因此大量连续连接会使 DAX 服务器过载。我们建议执行下列操作：
   + 保持默认的请求超时设置。
   + 如果较低超时是必需的，则使用较低的超时值实现单独的应用程序线程，并包括带有指数回退的重试机制。

1. **连接超时**：对于大多数应用程序，我们建议保持默认的连接超时设置。

1. **并发连接**：某些 SDK（例如 JavaV2）支持调整与 DAX 服务器的并发连接。重要注意事项：
   + DAX 服务器实例可以处理多达 40000 个并发连接。
   + 默认设置适用于大多数用例。
   + 大型客户端实例加上高并发连接可能会使服务器过载。
   + 较低的并发连接值可降低服务器过载的风险。
   + 性能计算示例：
     + 假设请求延迟为 1 毫秒，理论上每个连接每秒可以处理 1000 个请求。
     + 对于 3 节点集群，连接到所有节点的单个客户端实例每秒可以处理 3000 个请求。
     + 通过 10 个连接，客户端每秒可以处理大约 30000 个请求。

       建议：从较低的并发连接设置开始，然后根据预期的生产工作负载模式通过性能测试进行验证。

# 配置 DAX 集群
<a name="dax-config-considerations"></a>

DAX 集群是一个托管集群，但您可以调整其配置以满足您的应用程序要求。由于它与 DynamoDB API 操作紧密集成，因此在将您的应用程序与 DAX 集成时，应考虑以下几个方面。

**Topics**
+ [DAX 定价](#dax-pricing)
+ [项目缓存和查询缓存](#item-vs-query-cache)
+ [为缓存选择 TTL 设置](#select-ttl-duration-caches)
+ [使用 DAX 集群缓存多个表](#cache-multi-tables-dax-cluster)
+ [DAX 和 DynamoDB 全局表中的数据复制](#data-replication-dax-ddb-gt)
+ [DAX 区域可用性](#dax-region-availability)
+ [DAX 缓存行为](#dax-caching-behavior)

## DAX 定价
<a name="dax-pricing"></a>

集群的成本取决于其预调配的[节点](DAX.concepts.cluster.md#DAX.concepts.nodes)的数量和大小。每个节点按其在集群中运行的小时数计费。有关更多信息，请参阅 [Amazon DynamoDB 定价](https://www.amazonaws.cn/dynamodb/pricing/)。

缓存命中不会产生 DynamoDB 成本，但会影响 DAX 集群资源。缓存未命中会产生 DynamoDB 读取成本，并需要 DAX 资源。写入会产生 DynamoDB 写入成本，并影响用于代理写入的 DAX 集群资源。

## 项目缓存和查询缓存
<a name="item-vs-query-cache"></a>

DAX 能够维护[项目缓存](DAX.concepts.md#DAX.concepts.item-cache)和[查询缓存](DAX.concepts.md#DAX.concepts.query-cache)。了解这些缓存之间的差异可以帮助您确定它们为应用程序提供的性能和一致性特征。


| 缓存特性 | 项目缓存 | 查询缓存 | 
| --- | --- | --- | 
|  用途  |  存储 [GetItem](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_GetItem.html) 和 [BatchGetItem](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_BatchGetItem.html) API 操作的结果。  |  存储[查询](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_Query.html)和[扫描](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_Scan.html) API 操作的结果。这些操作可以根据查询条件而不是特定项目键返回多个项目。  | 
|  访问类型  |  使用基于键的访问权限。 当应用程序使用 `GetItem` 或 `BatchGetItem` 请求数据时，DAX 会首先使用所请求项目的主键检查项目缓存。如果项目已缓存且未过期，DAX 会立即返回它，而无需访问 DynamoDB 表。 |  使用基于参数的访问权限。 DAX 会缓存 `Query` 和 `Scan` API 操作的结果集。DAX 使用相同的参数处理后续请求，这些参数包括来自缓存的相同查询条件（表、索引）。这大大缩短了响应时间，减少了 DynamoDB 读取吞吐量消耗。 | 
|  缓存失效  |  在以下情况下，DAX 会自动将更新的项目复制到 DAX 集群中节点的项目缓存中： [\[See the AWS documentation website for more details\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/dax-config-considerations.html)  |  查询缓存比项目缓存更难失效。项目更新可能不会直接映射到缓存的查询或扫描。您必须仔细调整查询缓存 TTL 以保持数据一致性。在 TTL 使以前缓存的响应失效，以及 DAX 对 DynamoDB 执行新的查询之前，通过 DAX 或基表执行的写入不会反映在查询缓存中。  | 
|  全局二级索引  | 由于本地二级索引或全局二级索引不支持 GetItem API 操作，因此项目缓存仅缓存从基表读取的内容。 | 查询缓存会缓存针对表和索引的查询。 | 

## 为缓存选择 TTL 设置
<a name="select-ttl-duration-caches"></a>

TTL 决定了数据在过时之前存储在缓存中的时间段。在此时间段之后，数据将在下次请求时自动刷新。为 DAX 缓存选择正确的 TTL 设置时，需要在应用程序性能优化和数据一致性之间取得平衡。由于不存在适用于所有应用程序的通用 TTL 设置，因此最佳 TTL 设置会因应用程序的具体特征和要求而有所不同。建议您首先使用此规范性指南，设置保守的 TTTTL 设置。然后，根据应用程序的性能数据和见解以迭代方式调整 TTL 设置。

DAX 将保留项目缓存的最近最少使用的（LRU）列表。该 LRU 列表会跟踪项目首次写入缓存或最后一次从缓存中读取项目的时间。如果 DAX 节点内存已满，DAX 将逐出较旧的项目（即使其尚未过期），为新项目腾出空间。将始终启用 LRU 算法，用户无法配置。

若要设置适用于应用程序的 TTL 持续时间，请考虑以下几点：

### 了解数据访问模式
<a name="ttl-data-access-patterns"></a>
+ **读取密集型工作负载** – 对于具有读取密集型工作负载且数据更新不频繁的应用程序，请设置更长的 TTL 持续时间以减少缓存未命中几率。更长的 TTL 持续时间还可以减少访问底层 DynamoDB 表的需求。
+ **写入密集型工作负载** – 对于更新频繁且不是通过 DAX 写入的应用程序，请设置较短的 TTL 持续时间，以确保缓存与数据库保持一致。更短的 TTL 持续时间还可以降低提供陈旧数据的风险。

### 评估应用程序的性能要求
<a name="ttl-evaluate-app-performance-reqs"></a>
+ **延迟敏感度** - 如果您的应用程序需要低延迟而不是数据新鲜度，请使用更长的 TTL 持续时间。较长的 TTL 持续时间可最大限度提高缓存命中率，从而降低平均读取延迟。
+ **吞吐量和可扩展性** – 更长的 TTL 持续时间可减少 DynamoDB 表的负载，并提高吞吐量和可扩展性。不过，您应在这一方面与对最新数据的需求之间取得平衡。

### 分析缓存逐出和内存使用情况
<a name="ttl-analyze-cache-evict-mem-use"></a>
+ **缓存内存限制** - 监控 DAX 集群的内存使用情况。较长的 TTL 持续时间可以在缓存中存储更多数据，这可能会达到内存限制并导致基于 LRU 的驱逐。

### 使用指标和监控功能来调整 TTL
<a name="ttl-adjust-use-metrics"></a>

定期查看[指标](dax-metrics-dimensions-dax.md#dax-metrics-dimensions)，例如缓存命中率和未命中率以及 CPU 和内存利用率。根据这些指标调整 TTL 设置，以在性能和数据新鲜度之间取得最佳平衡。如果缓存未命中率高且内存利用率低，请延长 TTL 持续时间以提高缓存命中率。

### 考虑业务需求与合规性
<a name="ttl-business-reqs"></a>

数据保留策略可能会规定您可以为缓存敏感或个人信息设置的最长 TTL 持续时间。

### 将 TTL 设置为零时的缓存行为
<a name="ttl-cache-behavior-zero-value"></a>

如果将 TTL 设置为 0，项目缓存和查询缓存将表现出以下行为：
+ **项目缓存** – 仅在 LRU 驱逐或直写操作发生时，才会刷新缓存中的项目。
+ **查询缓存** - 不缓存查询响应。

## 使用 DAX 集群缓存多个表
<a name="cache-multi-tables-dax-cluster"></a>

对于具有多个不需要单独缓存的小型 DynamoDB 表的工作负载，单个 DAX 集群会缓存对这些表的请求。这使得 DAX 的使用更加灵活和高效，特别是对于访问多个表并需要高性能读取的应用程序。

与 DynamoDB [数据面板](HowItWorks.API.md#HowItWorks.API.DataPlane) API 类似，DAX 请求需要表名。如果您在同一 DAX 集群中使用多个表，则不需要任何特定配置。但是，必须确保集群的安全权限允许访问所有缓存表。

### 将 DAX 用于多个表的注意事项
<a name="multi-table-dax-considerations"></a>

在将 DAX 与多个 DynamoDB 表结合使用时，应考虑以下几点：
+ **内存管理** - 将 DAX 用于多个表时，应考虑工作数据集的总大小。您的数据集中的所有表将共享与您选择的节点类型相同的内存空间。
+ **资源分配** - DAX 集群的资源在所有缓存的表之间共享。但是，高流量表可能会导致从相邻的小表中驱逐数据。
+ **规模经济** – 将较小的资源分组到更大的 DAX 集群中，可以对流量进行平均化，使其处于更稳定的模式。就 DAX 集群所需的读取资源总数而言，拥有三个或更多节点也是经济实惠的。这还提高了集群中所有缓存表的可用性。

## DAX 和 DynamoDB 全局表中的数据复制
<a name="data-replication-dax-ddb-gt"></a>

DAX 是一项基于区域的服务，因此集群只知道其 Amazon Web Services 区域中的流量。当全局表从另一个区域复制数据时，它们会绕过缓存，直接写入底层数据源。

较长的 TTL 持续时间可能会导致陈旧数据在辅助区域中停留的时间比在主区域中更长。这可能会导致辅助区域的本地缓存中的缓存不命中。

下图显示了源区域 A 中在全局表级别上进行的数据复制。区域 B 中的 DAX 集群并未立即意识到来自源区域 A 的新复制数据。

![\[全局表将项目 v2 从区域 A 复制到区域 B。区域 B DAX 集群 B 不知道项目 v2。\]](http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/images/dax-ddb-gt-data-replication.png)


## DAX 区域可用性
<a name="dax-region-availability"></a>

并非所有支持 DynamoDB 表的区域都支持部署 DAX 集群。如果您的应用程序要求通过 DAX 实现低读取延迟，请先查看[支持 DAX 的区域](https://docs.amazonaws.cn/general/latest/gr/ddb.html#ddb_region)列表。然后，为 DynamoDB 表选择区域。

## DAX 缓存行为
<a name="dax-caching-behavior"></a>

DAX 执行元数据和逆向缓存。了解这些缓存行为将有助于您有效地管理缓存项目和逆向缓存条目的属性元数据。
+ **元数据缓存** - DAX 集群无限期维护有关缓存项目的属性名称的元数据。即使在项目过期或已从缓存中逐出之后，此元数据仍会保留。

  随着时间的推移，使用不限制数量的属性名称的应用程序会耗尽 DAX 集群中的内存。此限制仅适用于顶级属性名称，不适用于嵌套属性名称。不受限制的属性名称的示例包含时间戳、UUID 和会话 ID。尽管您可以使用时间戳和会话 ID 作为属性值，但建议使用更短、更加可预测的属性名称。
+ **逆向缓存** – 如果出现缓存未命中且从 DynamoDB 表中读取没有产生匹配的项目，DAX 会在相应的项目或查询缓存中添加逆向缓存条目。在缓存 TTL 持续时间到期或发生直写之前，此条目将一直保留。DAX 继续返回此逆向缓存条目以供将来的请求使用。

  如果逆向缓存行为不符合您的应用程序模式，请在 DAX 返回空结果时直接读取 DynamoDB 表。还建议您设置较低的 TTL 缓存持续时间，以避免缓存中出现长期的空结果，并提高与表的一致性。

# 设置 DAX 集群的容量
<a name="dax-cluster-sizing"></a>

DAX 集群的总容量和可用性取决于节点类型和数量。集群中的节点越多，其读取容量就会增加，但写入容量不会增加。较大的节点类型（最大 r5.8xlarge）可以处理更多写入请求，但是当节点发生故障时，节点太少可能会影响可用性。有关设置 DAX 集群容量的更多信息，请参阅[DAX 集群大小调整指南](DAX.sizing-guide.md)。

以下各节讨论了不同的容量设置问题，在为创建可扩展且具成本效益的集群以平衡节点类型和数量时应考虑这些方面。

**Topics**
+ [规划可用性](#dax-sizing-availability)
+ [规划写入吞吐量](#dax-sizing-write-throughput)
+ [规划读取吞吐量](#dax-sizing-read-throughput)
+ [规划数据集大小](#dax-sizing-dataset-size)
+ [计算大致的集群容量需求](#dax-sizing-cluster-capacity)
+ [按节点类型估算集群吞吐能力](#dax-sizing-cluster-throughput-capacity)
+ [扩展 DAX 集群中的写入容量](#dax-sizing-scaling-write-capacity)

## 规划可用性
<a name="dax-sizing-availability"></a>

在调整 DAX 集群容量时，应首先关注其目标可用性。集群服务（如 DAX）的可用性是集群中节点总数的一个维度。由于单节点集群无法容忍故障，因此其可用性等于一个节点。在 10 节点集群中，丢失一个节点对集群总体容量的影响微乎其微。这种损失不会对可用性产生直接影响，因为剩余的节点仍然可以完成读取请求。为了恢复写入，DAX 会快速提名一个新的主节点。

DAX 基于 VPC。它使用子网组来确定可以在哪些[可用区](https://www.amazonaws.cn/about-aws/global-infrastructure/regions_az/)运行节点，以及可以使用子网中的哪些 IP 地址。对于生产工作负载，强烈建议您使用在不同可用区至少具有三个节点的 DAX。这将确保即使单个节点或可用区出现故障，集群仍有多个节点可以处理请求。一个集群最多可以有 11 个节点，其中一个是主节点，10 个是只读副本。

## 规划写入吞吐量
<a name="dax-sizing-write-throughput"></a>

所有 DAX 集群都有一个用于处理直写请求的主节点。集群节点类型的大小决定其写入容量。添加额外的只读副本不会增加集群的写入容量。因此，在创建集群时应考虑写入容量，因为以后无法更改节点类型。

如果您的应用程序需要通过 DAX 直写来更新项目缓存，请考虑增加集群资源使用量以简化写入操作。针对 DAX 的写入消耗的资源大约是缓存命中读取的 25 倍。这可能需要比只读集群更大的节点类型。

有关确定是直写还是绕写更适合您的应用程序的更多指导，请参阅[针对写入的策略](DAX.consistency.md#DAX.consistency.strategies-for-writes)。

## 规划读取吞吐量
<a name="dax-sizing-read-throughput"></a>

DAX 集群的读取容量取决于工作负载的缓存命中率。由于 DAX 在发生缓存未命中时从 DynamoDB 读取数据，因此它消耗的集群资源大约是缓存命中时的 10 倍。要增加缓存命中率，请增加缓存的 [TTL](dax-config-considerations.md#select-ttl-duration-caches) 设置以定义项目存储在缓存中的时间段。但是，除非更新是通过 DAX 写入的，否则较长的 TTL 持续时间会增加读取较旧项目版本的机会。

要确保集群有足够的读取容量，请按[横向扩展集群](dax-cluster-operations.md#dax-cluster-horizontal-scaling)中所述横向扩展集群。添加更多节点会将只读副本添加到资源池中，而移除节点会降低读取容量。在为集群选择节点数量及其大小时，请同时考虑所需的最小和最大读取容量。如果您无法通过横向扩展具有较小节点类型的集群来满足您的读取要求，请使用更大的节点类型。

## 规划数据集大小
<a name="dax-sizing-dataset-size"></a>

每种可用节点类型都有一组内存大小，以供 DAX 缓存数据。如果节点类型太小，则应用程序请求的工作数据集将无法存储在内存中，从而导致缓存不命中。由于较大的节点支持较大的缓存，因此请使用大于需要缓存的估计数据集的节点类型。更大的缓存也会提高缓存命中率。

对于重复读取次数很少的缓存项目，可能会获得越来越少返回。计算经常访问的项目的内存大小，并确保缓存足够大，足以存储该数据集。

## 计算大致的集群容量需求
<a name="dax-sizing-cluster-capacity"></a>

您可以估计工作负载的总容量需求，以帮助您选择适当大小和数量的集群节点。要进行此估计，请计算变量：*每秒标准化请求*（标准化 RPS）。此变量表示您的应用程序需要 DAX 集群提供支持的总工作单元，包括缓存命中率、缓存未命中率和写入次数。要计算标准化 RPS，请使用以下输入：
+ `ReadRPS_CacheHit` – 指定导致缓存命中的每秒读取次数。
+ `ReadRPS_CacheMiss` – 指定导致缓存未命中的每秒读取次数。
+ `WriteRPS` – 指定将通过 DAX 的每秒写入次数。
+ `DaxNodeCount` – 指定 DAX 集群中的节点数。
+ `Size` – 指定正在写入或读取的项目的大小，以 KB 为单位，向上舍入到最接近的 KB。
+ `10x_ReadMissFactor` – 表示值为 10。当出现缓存未命中时，DAX 使用的资源大约是缓存命中时的 10 倍。
+ `25x_WriteFactor` – 表示值为 25，因为 DAX 直写占用的资源大约是缓存命中的 25 倍。

可以使用以下公式计算标准化 RPS。

```
Normalized RPS = (ReadRPS_CacheHit * Size) + (ReadRPS_CacheMiss * Size * 10x_ReadMissFactor) + (WriteRequestRate * 25x_WriteFactor * Size * DaxNodeCount)
```

例如，考虑以下输入值：
+ `ReadRPS_CacheHit` = 50,000
+ `ReadRPS_CacheMiss` = 1,000
+ `ReadMissFactor` = 1
+ `Size` = 2 KB
+ `WriteRPS` = 100
+ `WriteFactor` = 1
+ `DaxNodeCount` = 3

通过在公式中替换这些值，可以按如下方式计算标准化 RPS。

```
Normalized RPS = (50,000 Cache Hits/Sec * 2KB) + (1,000 Cache Misses/Sec * 2KB * 10) + (100 Writes/Sec * 25 * 2KB * 3)
```

在此示例中，计算得到的标准化 RPS 的值为 135,000。但是，这个标准化 RPS 值并没有考虑将集群利用率保持在 100% 以下或节点丢失因素。建议您考虑额外容量。为此，请确定两个乘法因子中较大的一个：目标利用率或节点丢失容忍度。然后，将标准化 RPS 乘以更大的因子，即可获得*每秒的目标请求数*（目标 RPS）。
+ **目标利用率**

  由于性能影响会增加缓存未命中率，因此不建议以 100% 的利用率运行 DAX 集群。理想情况下，应将集群利用率保持在 70% 或以下。为此，请将标准化 RPS 乘以 1.43。
+ **节点丢失容忍度**

  如果某个节点出现故障，您的应用程序必须能够在其余节点之间分配其请求。要确保节点的利用率保持在 100% 以下，请选择足够大的节点类型以吸收额外流量，直到出现故障的节点恢复在线状态。对于节点较少的集群，当一个节点出现故障时，其他节点必须能够容忍更大的流量增长。

  如果主节点发生故障，DAX 会将故障自动转移到一个只读副本并指定该副本作为新的主节点。如果副本节点发生故障，DAX 集群中的其他节点仍能够处理请求，直到发生故障的节点恢复为止。

  例如，出现节点故障的 3 节点 DAX 集群需要在剩下的两个节点上增加 50% 的容量。这需要乘法因子为 1.5。相反，出现节点故障的 11 节点集群需要在其余节点上增加 10% 的容量或乘以因子 1.1。

可以使用以下公式计算目标 RPS。

```
Target RPS = Normalized RPS * CEILING(TargetUtilization, NodeLossTolerance)
```

例如，要计算目标 RPS，请考虑以下值：
+ `Normalized RPS` = 135,000
+ `TargetUtilization` = 1.43

  由于我们的目标是使最大集群利用率达到 70%，因此将 `TargetUtilization` 设置为 1.43。
+ `NodeLossTolerance` = 1.5

  假设我们使用的是 3 节点集群，则将 `NodeLossTolerance` 设置为 50% 容量。

通过在公式中替换这些值，可以按如下方式计算目标 RPS。

```
Target RPS = 135,000 * CEILING(1.43, 1.5)
```

在此示例中，由于 `NodeLossTolerance` 的值大于 `TargetUtilization`，因此我们使用 `NodeLossTolerance` 计算目标 RPS 的值。这使我们的目标 RPS 为 202,500，这是 DAX 集群必须支持的总容量。要确定集群中需要的节点数，请将 Target RPS 映射到[下表](#dax-sizing-cluster-throughput-capacity)中的相应列。在这个目标 RPS 为 202,500 的示例中，您需要具有三个节点的 dax.r5.large 节点类型。

## 按节点类型估算集群吞吐能力
<a name="dax-sizing-cluster-throughput-capacity"></a>

使用 [Target RPS formula](#Target-RPS-formula)，您可以估算不同节点类型的集群容量。下表显示了节点类型为 1、3、5 和 11 的集群的大致容量。这些容量并不能取代使用您自己的数据和请求模式对 DAX 执行负载测试的需求。此外，这些容量不包括 [t-type](DAX.Burstable.md) 实例，因为它们缺少固定的 CPU 容量。下表中所有值的单位均为标准化 RPS。


| 节点类型（内存） | 1 个节点 | 3 个节点 | 5 个节点 | 11 个节点 | 
| --- | --- | --- | --- | --- | 
| dax.r5.24xlarge（768GB） | 1M | 3M | 5M | 11M | 
| dax.r5.16xlarge（512GB） | 1M | 3M | 5M | 11M | 
| dax.r5.12xlarge（384GB） | 1M | 3M | 5M | 11M | 
| dax.r5.8xlarge（256GB） | 1M | 3M | 5M | 11M | 
| dax.r5.4xlarge（128GB） | 600k | 1.8M | 3M | 6.6M | 
| dax.r5.2xlarge（64GB） | 300K | 900k | 1.5M | 3.3M | 
| dax.r5.xlarge（32GB） | 150k | 450k | 750k | 1.65M | 
| dax.r5.large（16GB） | 75K | 225k | 375k | 825k | 

由于每个节点的最大限制为 100 万 NPS（每秒网络操作数），因此 dax.r5.8xlarge 或更大的节点类型不会提供额外的集群容量。大于 8xlarge 的节点类型可能不会影响集群的总吞吐能力。但是，此类节点类型有助于在内存中存储更大的工作数据集。

## 扩展 DAX 集群中的写入容量
<a name="dax-sizing-scaling-write-capacity"></a>

每次写入 DAX 会在每个节点上消耗 25 个标准化请求。由于每个节点的 RPS 限制为 100 万，因此 DAX 集群限制为每秒 40,000 次写入，不考虑读取量。

如果您的用例需要在缓存中每秒写入超过 40,000 次，则必须使用单独的 DAX 集群并在其中对写入进行分片。与 DynamoDB 类似，您可以对正在写入缓存的数据的分区键进行哈希处理。然后，使用模数来确定要将数据写入哪个分片。

以下示例计算输入字符串的哈希值。然后用 10 计算哈希值的模数。

```
def hash_modulo(input_string):
    # Compute the hash of the input string
    hash_value = hash(input_string)

    # Compute the modulus of the hash value with 10
    bucket_number = hash_value % 10

    return bucket_number

#Example usage
if _name_ == "_main_":
    input_string = input("Enter a string: ")
    result = hash_modulo(input_string)
    print(f"The hash modulo 10 of '{input_string}' is: {result}.")
```

# 部署集群
<a name="dax-deploy-cluster"></a>

创建新 DAX 集群所需的配置不仅仅是 DynamoDB 所需的配置。这些配置特别针对网络设置，因为 DAX 是基于 [Amazon VPC](https://docs.amazonaws.cn/vpc/latest/userguide/what-is-amazon-vpc.html) 的。这使您能够完全控制自己的虚拟网络环境，包括资源放置、连接和安全。本节介绍创建集群期间所需设置的最佳实践。

有关选择集群节点的更多信息，请参阅[设置 DAX 集群的容量](dax-cluster-sizing.md)。

**Topics**
+ [配置网络](#dax-cluster-config-network)
+ [配置安全性](#dax-cluster-config-security)
+ [参数组](#dax-cluster-parameter-group)
+ [维护时段](#dax-cluster-maintenance-window)

## 配置网络
<a name="dax-cluster-config-network"></a>

DAX 使用[子网组](DAX.concepts.cluster.md#DAX.concepts.cluster.security)来确定它可以在哪些可用区中运行节点，以及可以使用子网中的哪些 IP 地址。为了最大限度减少应用程序和 DAX 之间的延迟，您的应用程序服务器和 DAX 集群的子网及可用区应相同。

建议将 DAX 节点分布在多个可用区中。可以使用默认选项“自动分配”执行此操作。

有关设置 VPC 的最佳实践，请参阅《Amazon VPC 用户指南》**中的[开始使用 Amazon VPC](https://docs.amazonaws.cn/vpc/latest/userguide/vpc-getting-started.html)。

## 配置安全性
<a name="dax-cluster-config-security"></a>

本节讨论应为使用 DAX 的应用程序实施的安全措施。本节还简要讨论了 DAX 为数据加密提供的支持。

**IAM**  
DAX 和 DynamoDB 具有单独的[访问控制](DAX.access-control.md)机制。DAX 需要 IAM 角色才能访问您的 DynamoDB 表。此角色应遵循最低权限原则，仅授予对特定表和 DynamoDB 操作（例如 [GetItem](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_GetItem.html) 和 [PutItem](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_PutItem.html)）的访问权限。有关 DAX 提供的访问控制机制的更多信息，请参阅 [DAX 访问控制](DAX.access-control.md)。

**加密**  
创建 DAX 集群时，可以配置静态加密和传输中加密。此设置默认处于启用状态。建议保留默认加密设置，除非业务要求禁用默认设置。有关更多信息，请参阅 [DAX 静态加密](DAXEncryptionAtRest.md) 和 [DAX 传输加密](DAXEncryptionInTransit.md)。

## 参数组
<a name="dax-cluster-parameter-group"></a>

DAX 在集群中的每个节点上应用一组名为[参数组](https://docs.amazonaws.cn/amazondynamodb/latest/APIReference/API_dax_ParameterGroup.html)的配置。可以在创建集群后更改此配置。

DAX 参数组包含项目缓存和查询缓存的 TTL 设置。默认 TTL 持续时间为 5 分钟。可以将 TTL 持续时间改写为任何大于或等于 1 毫秒的整数值。

在运行的 DAX 实例使用参数组时，无法修改参数组。可以在 DAX 集群停机期间更改参数组值。

## 维护时段
<a name="dax-cluster-maintenance-window"></a>

为了允许偶尔对节点进行软件升级和修补，可以为 DAX 集群配置每周[维护时段](DAX.concepts.cluster.md#DAX.concepts.maintenance-window)。在此时段中，DAX 会对节点执行滚动更新。在更新期间，拥有多个节点的集群不会失去集群的可用性，但是在节点返回之前，集群容量会减少。如果您的组织具有可预测的低使用率时间段，请考虑将维护时段手动设置为该时间。

# 管理集群操作
<a name="dax-cluster-operations"></a>

DAX 会为您处理集群的维护和运行状况。但是，您需要提供操作输入才能横向或纵向扩展集群以匹配您的使用模式。本节介绍扩展 DAX 集群时建议采用的流程。

**Topics**
+ [横向扩展集群](#dax-cluster-horizontal-scaling)
+ [纵向扩展集群](#dax-cluster-vertical-scaling)

## 横向扩展集群
<a name="dax-cluster-horizontal-scaling"></a>

扩展 DAX 集群涉及调整其容量以满足吞吐量需求。通过在集群运行时增加或减少集群中的节点（副本）数量，可完成此调整。此过程称为[横向扩展](DAX.cluster-management.md#DAX.cluster-management.scaling.read-scaling)，有助于将工作负载分配给更多节点，或者在需求较低时整合到更少的节点。

可以使用 Amazon CLI 中的 `decrease-replication-factor` 或 `increase-replication-factor` 命令横向缩减和扩大 DAX 集群。

**增加复制因子（横向扩展）**  
增加 DAX 集群的复制因子会为该集群添加更多节点。下面的示例展示如何使用 `increase-replication-factor` 命令。

```
aws dax increase-replication-factor \
    --cluster-name yourClusterName  \
    --new-replication-factor desiredReplicationFactor
```
+ 在此命令中，`cluster-name` 参数指定集群的名称。例如，*yourClusterName*。
+ `new-replication-factor` 参数指定扩展后要添加到集群中的节点总数。其中包括主节点和副本节点。例如，如果您的集群当前有 3 个节点，并且您想再添加 2 个节点，请将 `new-replication-factor` 的值设置为 5。

**降低复制因子（横向缩减）**  
降低 DAX 集群的复制因子会从集群中移除节点。在需求低迷时段，移除节点可以帮助降低成本。下面的示例展示如何使用 `decrease-replication-factor` 命令。

```
aws dax decrease-replication-factor \
    --cluster-name yourClusterName  \
    --new-replication-factor desiredReplicationFactor
```
+ 在此命令中，`cluster-name` 参数指定集群的名称。例如，*yourClusterName*。
+ `new-replication-factor` 参数指定扩展后集群中减少的节点数。此数字必须小于当前的复制因子，并且必须包括主节点。例如，如果您的集群有 5 个节点，而您想要移除 2 个节点，请将 `new-replication-factor` 的值设置为 3。

### 横向扩展注意事项
<a name="dax-horizontal-scaling-considerations"></a>

在计划横向扩展时，请考虑以下几点：
+ **主节点** - DAX 集群包括一个主节点。复制因子包括该主节点。例如，复制因子为 3 表示一个主节点和两个副本节点。
+ **可用性** - 添加或删除 DAX 节点会改变集群的可用性和容错能力。更多的节点可以提高可用性，但也会增加成本。
+ **数据迁移** – 当您增加复制因子时，DAX 会自动处理新节点集之间的数据分布。当一个新节点开始提供流量时，其缓存已经过预热。但是，在此过程中，在数据迁移期间可能会对性能产生暂时的影响。

在扩展期间和扩展之后，请务必密切监控 DAX 集群，以确保它们按预期运行，并根据需要进行进一步调整。

## 纵向扩展集群
<a name="dax-cluster-vertical-scaling"></a>

要纵向扩展现有集群的节点大小，需要创建一个新集群并将应用程序流量迁移到该新集群。迁移到具有不同节点的新集群需要完成几个步骤，以确保平稳过渡，同时最大限度减少对应用程序性能和可用性的影响。

要创建用于纵向扩展节点大小的新集群，请考虑以下几点：
+ **访问当前设置** - 查看当前 DAX 集群的指标，以确定所需的新节点大小和数量。使用此信息作为输入来确定所需集群的大小。有关信息，请参阅[设置 DAX 集群的容量](dax-cluster-sizing.md)。
+ **设置新的 DAX 集群** – 使用所确定的节点类型和数量创建新的 DAX 集群。除非需要进行调整，否则可以使用[参数组](dax-deploy-cluster.md#dax-cluster-parameter-group)中的现有配置设置。
+ **同步数据** - 由于 DAX 是 DynamoDB 的缓存层，因此无需直接迁移数据。但是，在向新 DAX 集群发送流量之前，新的 DAX 集群在内存中不会有任何工作数据集。
+ **更新应用程序配置** - 更新应用程序配置以指向新的 [DAX 集群端点](DAX.concepts.cluster.md#DAX.concepts.cluster-endpoint)。可能需要更改代码或更新环境变量，具体取决于应用程序的配置。

  为了减少切换到新集群时带来的影响，请从一小部分应用程序队列向新集群发送金丝雀流量。为此，可以缓慢推出应用程序更新，或者在 DAX 端点前使用基于权重的路由 DNS 条目。
+ **监控和优化** - 切换到新的 DAX 集群后，请密切监控其性能[指标和日志](DAX.Monitoring.md)中是否存在任何问题。准备好根据更新的工作负载模式调整节点数量。

  在新集群正确缓存工作数据集之前，您将看到更高的缓存未命中率和延迟。
+ **停用旧集群** – 当您确定新集群能够按预期运行时，请安全地停用旧 DAX 集群以避免产生不必要的成本。

# 监控 DAX
<a name="pres-guide-monitor-dax"></a>

您可以监控关键[指标](dax-metrics-dimensions-dax.md#dax-metrics-dimensions)（如缓存命中率），以确保获得最佳 DAX 集群性能、诊断问题并确定何时需要扩展集群。定期检查关键指标可以帮助您根据自己的工作负载要求扩展集群，从而保持性能、稳定性和成本效益。有关监控 DAX 的更多信息，请参阅[生产监控](dax-production-monitoring.md)。

下表列出了您应监控的一些关键指标：
+ **缓存命中率** – 显示 DAX 如何有效地提供缓存数据，从而减少访问底层 DynamoDB 表的需要。集群很少出现缓存未命中表明缓存效率良好。但是缓存命中率低则表明您可能需要重新访问缓存 TTL 设置，或者工作负载不适合缓存。

  可使用 Amazon CloudWatch 计算 DAX 集群的缓存命中率。比较 `ItemCacheHits`、`ItemCacheMisses`、`QueryCacheHits` 和 `QueryCacheMisses` 指标以获得此比率。以下公式显示了如何计算缓存命中率。要使用此公式计算此比率，请将缓存命中率除以缓存命中率和未命中率之和。

  ```
  Cache hit ratio = Cache hits / (Cache hits + Cache misses)
  ```

  缓存命中率是一个介于 0 和 1 之间的数字，以百分比表示。百分比越高表示总体缓存利用率越高。
+ **ErrorRequestCount** – 节点或集群报告的用户错误导致的请求计数。`ErrorRequestCount` 包括受到节点或集群节流的请求。监控用户错误可以帮助您识别应用程序中的扩展错误配置或热门项目/分区模式。
+ **操作延迟** – 监控对 DAX 集群的读取和写入操作的延迟可以帮助您识别性能瓶颈。延迟增加可能表明您的 DAX 集群配置、网络存在问题或者需要扩展。
+ **网络消耗** – 密切关注 `NetworkBytesIn` 和 `NetworkBytesOut` 指标，以监控 DAX 集群的网络流量。网络吞吐量的意外增加可能意味着客户端请求数增加或查询模式效率低下，从而导致传输更多数据。

  监控网络消耗可帮助您管理 DAX 集群的成本。它还可以确保网络不会成为影响集群性能的瓶颈。
+ **逐出率** - 显示从缓存中移除项目以便为新物品腾出空间的频率。如果逐出率随着时间的推移而增加，表明您的缓存可能太小或缓存策略无效。

  在 CloudWatch 中监控 `EvictedSize` 指标，以确定缓存大小是否适配工作负载。如果被逐出的总大小持续增长，则可能需要纵向扩展 DAX 集群以容纳更大缓存。
+ **CPU 利用率** – 节点或集群的 CPU 使用率百分比。这是监控任何数据库或缓存系统的关键指标。CPU 利用率高可能意味着您的 DAX 集群可能过载，需要扩展以应对不断增长的需求。

  监控 DAX 集群的 `CPUUtilization` 指标。如果您的 CPU 利用率一直接近或超过 70-80%，请考虑按照以下章节中所述[纵向扩展 DAX 集群](#dax-cluster-scale-monitoring-data)。

  如果发送到 DAX 的请求数超过节点容量，DAX 将限制其接受额外请求的速率。它通过返回 ThrottlingException 来做到这一点。DAX 持续评估集群的 CPU 利用率，确定在保持正常集群状态的情况下可处理的请求数。

  可以监控 DAX 发布到 CloudWatch 的 `ThrottledRequestCount` 指标。如果经常看到这些异常，应考虑纵向扩展集群。

## 使用监控数据扩展 DAX 集群
<a name="dax-cluster-scale-monitoring-data"></a>

可以通过监控 DAX 集群的性能指标，来确定是需要扩展还是缩减该集群。
+ **纵向或横向扩展** – 如果 DAX 集群的 CPU 利用率高、缓存命中率低（优化缓存策略后）或操作延迟较高，则应纵向扩展该集群。添加更多节点（也称为横向扩展）可以帮助更均匀地分配负载。对于每秒写入请求增加的工作负载，可能需要选择功能更强大的节点（纵向扩展）。
+ **缩减** – 如果您一直看到 CPU 利用率低且操作延迟低于阈值，则可能表明资源预调配过度。在这种情况下，可以缩减节点数以降低成本。在低利用率期间，可以将节点数减少到 1，但不能完全关闭集群。