Amazon DynamoDB
开发人员指南 (API Version 2012-08-10)
AWS 服务或AWS文档中描述的功能,可能因地区/位置而异。点 击 Getting Started with Amazon AWS to see specific differences applicable to the China (Beijing) Region.

针对项目的最佳实践

在 DynamoDB 中处理项目时,您需要考虑如何获取最佳性能,如何降低预置吞吐量成本,以及如何避免操作受限 (保持在读取和写入容量单位内)。如果您处理的项目超出 DynamoDB 中的限制 所述的项目大小上限,则您需要考虑如何处理该状况。本节提供解决这些问题的最佳实践。

使用一对多表而不是大型属性集合

如果您的表中的项目存储很多集合类型属性 (例如数字集或字符串集),请考虑删除该属性并将表分成两个表。要在这些表之间形成一对多关系,请使用主键。

创建表并加载示例数据 一节的 Forum、Thread 和 Reply 表就是这种一对多关系的绝佳示例。举例来说,在 Thread 表中,每个项目都对应一个论坛主题,在 Reply 表中存储了针对每个主题的一个或多个回复。

您无需使用单独的表将回复存储为项目,只需在同一表中存储话题和回复即可。对于每个话题,您可以将所有回复存储在一个字符串集类型的属性中;但是,使用单独的表存储话题和回复数据有以下几个方面的好处:

  • 如果将回复存储为表中的项目,则您可以存储任意数量的回复,因为 DynamoDB 表中可以存储任意数量的项目。

    如果您将回复存储为 Thread 表中的属性值,那么您会受到项目大小上限的约束,项目大小上限会限制存储的回复数量。(请参阅 DynamoDB 中的限制)

  • 当您检索某个 Thread 项目时,您不必过于关注预置吞吐量,因为您只需检索话题数据,而不是该话题的所有回复。

  • 通过查询,您只能检索表中的部分项目。将回复存储在单独的 Reply 表中后,您可以通过查询 Reply 表,只检索回复的子集 (例如,某个特定日期范围内的数据)。

    如果您将回复存储为集合类型的属性值,那么您必须始终检索所有回复,这样占用的预置吞吐量会比所需的吞吐量高。

  • 向进程添加新回复时,您只能向 Reply 表添加一个项目,此操作仅会针对单个 Reply 项目产生预置吞吐量成本。如果回复存储在 Thread 表中,无论您何时话题中添加单个用户回复,您都需要支付写入整个 Thread 项目 (包括所有回复) 的所有费用。

使用多个表支持多样化的访问模式

如果频繁访问 DynamoDB 表中的大量项目,但不是经常使用项目的所有较大属性,您可以将较小、访问频率较高的属性存储为单独表中的独立项目,从而提高效率并均衡工作负载。

例如,来看一下 创建表并加载示例数据 一节中介绍的 ProductCatalog 表。这个表中的项目包含基本产品信息 (例如,产品名称和描述)。此信息更改频率较低,但是应用程序每次显示用户的产品时都会使用该信息。

如果您的应用程序还需要跟踪快速更改的产品属性 (例如价格和供应范围),那么您可以将此信息存储在名为 ProductAvailability 的独立表中。此方法可以最大限度地降低更新的吞吐量成本。为举例说明,我们假设 ProductCatalog 项目的大小为 3 KB,且该项目的价格和供应范围 300 字节。在此情况下,这些快速更改属性的更新将占用三个容量单位,与更新其他产品属性的成本相同。现在,假设价格和公用范围信息存储在 ProductAvailability 表中。这样一来,更新信息将仅占用一个写入容量单位。

注意

有关容量单位的介绍,请参阅 读取和写入吞吐容量

如果您的应用程序还需要存储显示频率较低的产品数据,那么您可以将此信息存储在名为 ExtendedProductCatalog 的独立表中。此数据可能包含产品维度、音乐专辑的曲目列表或低于基本产品数据访问频率的其他属性。这样一来,应用程序将仅在向用户显示基本的产品信息时占用吞吐量,且仅在用户请求扩展的产品详细信息时占用额外的吞吐量。

以下是之前讨论的表的示例实例。请注意,所有表均具有一个用作主键的 Id 属性。

ProductCatalog

Id 职务 说明
21 “名著” “在去年的畅销书列表中,...”
302 “红色自行车” “经典红色自行车...”
58 “音乐专辑” “本周的最新流行专辑...”

ProductAvailability

Id 价格 QuantityOnHand
21 “5.00 USD” 3750
302 “125.00 USD” 8
58 “5.00 USD” “无穷大 (数字项目)”

ExtendedProductCatalog

Id AverageCustomerRating TrackListing
21 5
302 3.5
58 4 {"Track1#3:59", "Track2#2:34", "Track3#5:21", ...}

以下介绍将项目属性拆分成存储在不同表中的多个项目的一些优势和注意事项:

  • 读取或写入这些属性的吞吐量成本会降低。更新项目单个属性的成本取决于项目的总大小。如果项目较小,则您可以在访问每个项目时占用较少的吞吐量。

  • 如果您一直频繁访问较小的项目,系统将会更均匀地分配您的 I/O 工作负载。在表的同一部分检索较大项目会一次性占用大量读取容量;执行这样的操作会导致工作负载不平衡,进而导致操作受限。有关更多信息,请参阅 避免读取活动陡增

  • 要执行单个项目的读取操作 (例如 GetItem),吞吐量计算会向上取整至下一 4 KB 界限。如果您的项目小于 4 KB,且您只通过主键检索项目,那么将项目属性存储为单独的项目可能不会减少吞吐量。即使如此,QueryScan 等操作的吞吐量成本的计算方式也不同:系统会计算所有返回项目的大小的总和,并将最终的总和向上取整到下一 4 KB 边界。执行这些操作时,您可能仍需将较大属性移入单独的项目来降低吞吐量成本。有关更多信息,请参阅 项目大小和容量单位消耗

压缩大属性值

如果值很大,在将值存储到 DynamoDB 之前,您可以对其进行压缩。执行此操作可以降低存储和检索此类数据的成本。压缩演算法 (例如 GZIP 或 LZO) 会产生二进制数据输出。然后,您可以将此输出数据存储在 Binary 属性类型中。

例如,创建表并加载示例数据 一章中的 Reply 表中存储论坛用户写入的消息。这些用户回复可能包括非常长的文本字符串,这些内容最适合压缩。

有关演示如何在 DynamoDB 中压缩长消息的示例代码,请参阅:

在 Amazon S3 中存储大属性值

目前,DynamoDB 会限制您存储在表中的项目的大小。有关更多信息,请参阅 DynamoDB 中的限制。然而,您的应用程序可能需要在某一项目中存储超出 DynamoDB 大小上限的数据。要解决此问题,您可以将大属性存储为 Amazon Simple Storage Service (Amazon S3) 中的数据元,并在项目中存储数据元标识符。您还可以在 Amazon S3 在使用数据元元数据支持,将相应项目的主键值存储为 Amazon S3 数据元元数据。使用元数据有助于日后维护您的 Amazon S3 元数据。

例如,来看一下 创建表并加载示例数据 一节中介绍的 ProductCatalog 表。ProductCatalog 表中的项目存储有关物品价格、说明、书本作者以及其他产品维度的信息。如果您想要存储每个产品的图像,那么这些图像可能会非常大。您可以选择将图像存储在 Amazon S3 中,而不是 DynamoDB 中。

以下是使用此方法时需要注意的重要事项:

  • 由于 DynamoDB 不支持跨 Amazon S3 和 DynamoDB 的事务,因此您的应用程序必须处理故障,并负责清理孤立的 Amazon S3 数据元。

  • Amazon S3 限制数据元标识符的长度,因此您必须以符合此限制和其他 Amazon S3 约束的方式整理您的数据。有关更多信息,请参阅 Amazon Simple Storage Service 开发人员指南

将大属性划分到多个项目

如果您需要在单个项目中存储大于 DynamoDB 所允许上限的数据,那么您可以在多个项目中存储该数据,将其作为大型“虚拟项目”数据块。要获取最佳结果,请将数据块存储在具有简单主键 (分区键) 的独立表中,并使用批量操作 (BatchGetItemBatchWriteItem) 来读取和写入数据块。此方法有助于您在表分区中保持工作负载均匀分布。

例如,来看一下 创建表并加载示例数据 一章中介绍的 ForumThreadReply 表。Reply 表中的项目包含论坛用户写入的论坛消息。由于 DynamoDB 中规定项目大小的上限为 400 KB,因此每条回复的长度也有限制。对于大型回复,您可以将回复消息分为多个数据块,然后将每个数据块写入各自具有简单主键 (分区键) 的新 ReplyChunks 表的独立项目中,而不在 Reply 表中存储整个项目。

每个数据块的主键都是由其“父级”回复项目的主键、版本号和序列号组合而成。序列号决定了数据块的顺序。版本号确保当大型回复稍后更新时,系统会自动更新该内容。此外,在更新之前创建的数据块不会与更新后创建的数据块混淆。

您还需要更新带有数据块数量的“父级”回复项目,以便在需要检索回复的所有数据块时,您可以了解需要查找的数据块数量。

举例说明,以下内容介绍的是这些项目如何在 ReplyReplyChunks 表中显示:

Reply

Id ReplyDateTime 消息 ChunkCount ChunkVersion
"DynamoDB#Thread1" "2012-03-15T20:42:54.023Z" 3 1
"DynamoDB#Thread2" "2012-03-21T20:41:23.192Z" "short message"

ReplyChunks

Id 消息
"DynamoDB#Thread1#2012-03-15T20:42:54.023Z#1#1" “较长信息文本的第一部分...”
"DynamoDB#Thread1#2012-03-15T20:42:54.023Z#1#3" “...较长信息文本的第三部分”
"DynamoDB#Thread1#2012-03-15T20:42:54.023Z#1#2" “...较长信息文本的第二部分...”

以下是使用此方法时需要注意的重要事项:

  • 由于 DynamoDB 不支持交叉项目事务,因此您的应用程序需要在写入多个项目处理故障场景,并在读取多个项目时处理项目之间的不一致情况。

  • 如果您的应用程序一次性检索到大量数据,则会生成不均衡的工作负载,并导致意外操作的受限。在检索共享同一分区键值的项目时,此情况尤为明显。

通过使用具有简单主键 (分区键) 的独立表,对大型数据项目进行集块化处理可避免此问题,因为这样可以将每个大型数据块分散到表中。

一个切实可行但并非最佳的解决方案是将每个数据块存储在某个具有复合键,且分区键是“父级”项目主键的表中。选择这一设计的话,检索所有同一“父级”项目数据块的应用程序将会生成不均衡的工作负载,且各个分区的 I/O 分布也将不均衡。