本地二级索引 - Amazon DynamoDB
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅中国的 Amazon Web Services 服务入门

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

本地二级索引

某些应用程序只需要使用基表的主键查询数据。但是,在某些情况下,替代排序键可能会有所帮助。要向您的应用程序提供排序键的选择,您可以对 Amazon DynamoDB 表创建一个或多个本地二级索引,然后发出Query或者Scan对这些索引的请求。

方案:使用本地二级索引

例如,考虑使用Thread表中定义为 DynamoDB 中的代码示例创建表和加载数据。此表对于应用程序(如Amazon论坛。下表显示了此表中项目的组织方式,(并未显示所有属性。)


                包含论坛名称、主题、上次发布时间和回复次数列表的线程表。

DynamoDB 连续存储具有相同分区键值的所有项目。在本例中,假设特定的ForumName,aQuery操作可以立即找到该论坛的所有线程。在具有相同分区键值的项目组中,按排序键值有序地对项目进行排序。如果排序键(Subject),DynamoDB 可以缩小返回的结果范围-例如,返回 “S3” 论坛中具有Subject从字母 “a” 开始.

某些请求可能需要更复杂的数据访问模式。例如:

  • 哪些论坛主题获得的观点和回复最多?

  • 特定论坛中的哪个主题具有最多的消息?

  • 在特定时间段内,某个特定论坛上发布了多少主题?

要回答这些问题,Query行动是不够的. 相反,您将不得不Scan整个表。对于包含数百万个项目的表,这将占用大量预配置读取吞吐量,并需要很长时间才能完成。

但是,您可以在非键属性上指定一个或多个本地二级索引,例如Replies或者LastPostDateTime

A本地二级索引维护给定分区键值的替代排序键。本地二级索引还包含其基表中部分或全部属性的副本。您可以指定在创建表时将哪些属性投影到本地二级索引中。本地二级索引中的数据由与基表相同的分区键组织,但排序键不同。这样,您就可以跨此不同维度高效地访问数据项。为获得更高的查询或扫描灵活性,您可以为每个表创建最多个本地二级索引。

假设应用程序需要查找过去三个月内在某个论坛中发布的所有话题。如果没有本地二级索引,应用程序将不得不Scan对于,整个Thread表并放弃任何未在指定时间范围内的帖子。对于本地二级索引,Query操作可以使用LastPostDateTime作为排序键,并快速查找数据。

下图显示了一个名为LastPostIndex。请注意,分区键与Thread表,但排序键是LastPostDateTime


                LastPostIndex 表包含论坛名称、主题和上次发布时间的列表。

每个本地二级索引必须符合以下条件:

  • 分区键与其基表的分区键相同。

  • 排序键仅由一个标量属性组成。

  • 基表的排序键将投影到索引中,其中它充当非键属性。

在本例中,分区键为ForumName,并且本地二级索引的排序键为LastPostDateTime。此外,基表中的排序键值(在本示例中,Subject)投影到索引中,但它不是索引键的一部分。如果应用程序需要基于ForumNameLastPostDateTime,它可以发出Query请求针对LastPostIndex。查询结果按LastPostDateTime,并可以按升序或降序返回。查询还可以应用关键条件,例如,仅返回具有LastPostDateTime在特定的时间范围内。

每个本地二级索引自动包含其基表中的分区键和排序键;您可以选择将非键属性投影到索引中。当您查询索引时,DynamoDB 可高效地检索这些已投影的属性。查询本地二级索引时,查询还可以检索投影到索引中。DynamoDB 会自动从基表中提取这些属性,但延迟更大,预配置吞吐量成本也更高。

对于任何本地二级索引,每个不同的分区键值最多可以存储 10 GB 的数据。此图包括基表中的所有项目,以及索引中具有相同分区键值的所有项。有关更多信息,请参阅项目集合

属性投影

LastPostIndex,应用程序可以使用ForumNameLastPostDateTime作为查询标准。但是,要检索任何其他属性,DynamoDB 必须对Thread表。这些额外读取称为fetches,并且它们可以增加查询所需的预配置吞吐量总量。

假设您希望使用 “S3” 中所有线程的列表以及每个线程的回复数量填充一个网页,并按最近回复开始的最后一个回复日期/时间排序。要填充此列表,您需要以下属性:

  • Subject

  • Replies

  • LastPostDateTime

查询这些数据并避免获取操作的最有效方法是将Replies属性转换为本地二级索引,如此图所示。


                LastPostIndex 表,其中包含论坛名称、上次发布时间、主题和回复的列表。

AProjection是从表复制到二级索引的属性集。表的分区键和排序键始终投影到索引中;您可以投影其他属性以支持应用程序的查询要求。当您查询索引时,Amazon DynamoDB 可以访问投影中的任何属性,就像这些属性位于自己的表中一样。

创建二级索引时,需要指定将投影到索引中的属性。DynamoDB 为此提供了三种不同的选项:

  • 仅密钥— 索引中的每个项目仅包含表分区键、排序键值以及索引键值。这些区域有:KEYS_ONLY选项会导致尽可能小的二级索引。

  • 情况如:— 除了KEYS_ONLY,则二级索引将包括您指定的其他非键属性。

  • 全部— 二级索引包括源表中的所有属性。由于所有表数据都在索引中复制,因此ALL投影会产生尽可能大的二级索引。

在上图中,非键属性Replies投影到LastPostIndex。应用程序可以查询LastPostIndex而不是完整的Thread表中填充一个网页SubjectReplies, 和LastPostDateTime。如果请求任何其他非键属性,DynamoDB 将需要从Thread表。

从应用程序的角度来看,从基表中获取其他属性是自动且透明的,因此无需重写任何应用程序逻辑。但是,这种读取可以大大降低使用本地二级索引的性能优势。

当您选择要投影到本地二级索引的属性时,必须在预置吞吐量成本和存储成本之间做出权衡:

  • 如果只需要访问少量属性,同时尽可能降低延迟,就应考虑仅将键属性投影到本地二级索引中。索引越小,存储索引所需的成本越少,并且写入成本也会越少。如果您偶尔需要获取属性,则预配置吞吐量的成本可能会超过存储这些属性的较长期成本。

  • 如果您的应用程序频繁访问某些非键属性,就应考虑将这些属性投影到本地二级索引中。本地二级索引的额外存储成本会抵消频繁执行表扫描的成本。

  • 如果需要频繁访问大多数非键属性,则可以将这些属性(甚至整个基表)投影到本地二级索引中。这为您提供了最大的灵活性和最低的预配置吞吐量消耗,因为不需要提取。但是,如果您要投影所有属性,则存储成本将增长,甚至翻倍。

  • 如果您的应用程序不会频繁查询表,但必须对表中的数据执行大量写入或更新操作,就应考虑将仅密钥。本地二级索引的大小是最小的,但仍可用于查询活动。

创建本地二级索引

要在表上创建一个或多个本地二级索引,请使用LocalSecondaryIndexes参数CreateTableoperation. 表上的本地二级索引是在创建表时创建的。删除表时,会同时删除该表的任何本地二级索引。

必须指定一个非键属性以充当本地二级索引的排序键。您选择的属性必须是标量StringNumber,或者Binary。不允许使用其他标量类型、文档类型和集合类型。有关数据类型的完整列表,请参阅数据类型

重要

对于具有本地二级索引的表,每个分区键值有 10 GB 的大小限制。具有本地二级索引的表可以存储任意数量的项目,只要任何一个分区键值的总大小不超过 10 GB。有关更多信息,请参阅物料收集大小限制

您可以将任何数据类型的属性投影到本地二级索引中。这包括标量、文档和集。有关数据类型的完整列表,请参阅数据类型

从本地二级索引读取数据

您可以使用本地二级索引中的QueryScan操作。这些区域有:GetItemBatchGetItem操作不能对本地二级索引使用。

查询本地二级索引

在 DynamoDB 表中,每个项目的组合分区键值和排序键值必须是唯一的。但是,在本地二级索引中,排序键值不需要对于给定的分区键值是唯一的。如果本地二级索引中有多个具有相同排序键值的项目,则Query操作返回具有相同分区键值的所有项目。在响应中,匹配的项目不会以任何特定顺序返回。

您可以使用最终一致性读取或强一致性读取来查询本地二级索引。要指定所需的一致性类型,请使用ConsistentRead参数Queryoperation. 从本地二级索引进行的强一致性读取始终返回最新更新的值。如果查询需要从基表中获取其他属性,则这些属性将与索引保持一致。

考虑到以下数据从Query,请求来自特定论坛中的讨论主题的数据。

{ "TableName": "Thread", "IndexName": "LastPostIndex", "ConsistentRead": false, "ProjectionExpression": "Subject, LastPostDateTime, Replies, Tags", "KeyConditionExpression": "ForumName = :v_forum and LastPostDateTime between :v_start and :v_end", "ExpressionAttributeValues": { ":v_start": {"S": "2015-08-31T00:00:00.000Z"}, ":v_end": {"S": "2015-11-31T00:00:00.000Z"}, ":v_forum": {"S": "EC2"} } }

在此查询中:

  • DynamoDB 访问LastPostIndex,使用ForumName分区键查找 “EC2” 的索引项目。具有此键的所有索引项目都彼此相邻存储,以实现快速检索。

  • 在此论坛中,DynamoDB 使用索引来查找与指定LastPostDateTime条件。

  • 由于Replies属性投影到索引中,DynamoDB 可以检索此属性,而不会占用任何额外的预配置吞吐量。

  • 这些区域有:Tags属性不会投影到索引中,因此 DynamoDB 必须访问Thread表并获取此属性。

  • 返回结果,按LastPostDateTime。索引条目按分区键值排序,然后按排序键值排序,Query按照存储顺序返回它们。(您可以使用ScanIndexForward参数按降序返回结果。)

由于Tags属性不会投影到本地二级索引中,DynamoDB 必须占用额外的读取容量单位才能从基表中获取此属性。如果你需要经常运行这个查询,你应该将Tags进入LastPostIndex以避免从基表提取。但是,如果您需要访问Tags只有偶尔,为预测额外的存储成本Tags到索引中可能不值得。

扫描本地二级索引

您可以使用Scan从本地二级索引检索所有数据。您必须在请求中提供基表名称和索引名称。随着Scan,DynamoDB 会读取索引中的全部数据并将其返回到应用程序。您还可以请求仅返回部分数据并放弃其余数据。若要执行此操作,请使用FilterExpression参数ScanAPI。有关更多信息,请参阅Scan 的筛选表达式

项目写入和本地二级索引

DynamoDB 会自动保持所有本地二级索引与各自的基表同步。应用程序绝不会直接向索引中写入内容。但是,您有必要了解 DynamoDB 如何维护这些索引。

当您创建本地二级索引时,请指定一个属性作为索引的排序键。您还可以为该属性指定数据类型。这就意味着,无论您何时向基表中写入项目,如果项目定义索引键属性,则其类型必须与索引键架构的数据类型匹配。如果是LastPostIndexLastPostDateTime排序键定义为String数据类型。如果您尝试将项目添加到Thread表并指定不同的数据类型LastPostDateTime(例如Number),则 DynamoDB 会返回一个ValidationException因为数据类型不匹配。

基表中的项目与本地二级索引中的项目之间不需要一对一的关系。事实上,这种行为对于许多应用程序都是有利的。

相较于索引数量较少的表,拥有较多本地二级索引的表会产生较高的写入活动成本。有关更多信息,请参阅本地二级索引的预配置吞吐量注意事项

重要

对于具有本地二级索引的表,每个分区键值有 10 GB 的大小限制。具有本地二级索引的表可以存储任意数量的项目,只要任何一个分区键值的总大小不超过 10 GB。有关更多信息,请参阅物料收集大小限制

本地二级索引的预配置吞吐量注意事项

在 DynamoDB 中创建表时,您可以为表的预期工作负载预配置读取和写入容量单位。该工作负载包括对表的本地二级索引的读取和写入活动。

要查看预配置吞吐量容量的当前速率,请参阅Amazon DynamoDB 定价

读取容量单位

查询本地二级索引时,占用的读取容量单位数取决于访问数据的方式。

与表查询一样,索引查询可以使用最终一致性读取或强一致性读取,具体取决于ConsistentRead。一个强一致性读取占用一个读取容量单位;最终一致性读取只占用一半的读取容量。因此,通过选择最终一致性读取,您可以减少读取容量单位费用。

对于仅请求索引键和投影属性的索引查询,DynamoDB 计算预配置读取活动的方式与对表查询使用的方式相同。唯一不同的是,本次计算基于索引条目的大小,而不是基表中项目的大小。读取容量单位的数量就是返回的所有项目的所有投影属性大小之和;然后将结果向上舍入到下一个 4 KB 边界。有关 DynamoDB 如何计算预配置吞吐量使用情况的更多信息,请参阅管理 DynamoDB 预置容量表的设置

对于读取未投影到本地二级索引的属性的索引查询,DynamoDB 除了从索引读取投影属性之外,还需要从基表中获取这些属性。当您将任何非投影属性包含在Select或者ProjectionExpression参数Queryoperation. 读取会导致查询响应中的额外延迟,并且还会导致更高的预配置吞吐量成本:除了前面介绍的从本地二级索引读取内容之外,您还需要为读取的每个基表项目收取读取容量单位的费用。此费用是用于从表中读取每个整个项目,而不仅仅是请求的属性。

返回的结果大小上限为Query操作为 1 MB。这包括所有属性名称的大小和所返回的所有项目的值。但是,如果针对本地二级索引的查询导致 DynamoDB 从基表中获取项目属性,则结果中数据的最大大小可能会较低。在这种情况下,结果大小为以下总和:

  • 索引中匹配项目的大小,四舍五入到下一个 4 KB。

  • 基表中每个匹配项目的大小,每个项目分别向上舍入到下一个 4 KB。

使用此公式,查询操作返回的结果大小上限为 1 MB。

例如,假设每个项目的大小为 300 字节的表。该表上有一个本地二级索引,但只有 200 个字节的每个项目投影到索引中。现在假设你Query此索引,查询需要为每个项目提取表,并且查询返回 4 个项目。DynamoDB 总结了以下几点:

  • 索引中匹配项目的大小:200 字节 × 4 项 = 800 字节;然后四舍五入为 4 KB。

  • 基表中每个匹配项目的大小:(300 字节,四舍五入为 4 KB)×4 项 = 16 KB。

因此,结果中数据的总大小为 20 KB。

写入容量单位

添加、更新或删除表中的项目时,更新本地二级索引会占用表的预置写入容量单位。一次写入操作的预配置吞吐量总成本是对表执行的写入操作以及更新本地二级索引所占用的写入容量单位之和。

向本地二级索引写入项目的成本取决于多个因素:

  • 如果您向定义了索引属性的表中写入新项目,或更新现有的项目来定义之前未定义的索引属性,只需一个写入操作即可将项目放置到索引中。

  • 如果对表执行的更新操作更改了索引键属性的值(从 A 更改为 B),就需要执行两次写入操作:一次用于删除索引中之前的项目,另一次用于将新项目放置到索引中。 

  • 如果索引中已有某一项目,而对表执行的写入操作删除了索引属性,就需要执行一次写入操作删除索引中旧的项目投影。

  • 如果更新项目前后索引中没有此项目,此索引就不会额外产生写入成本。

所有这些因素都假定索引中每个项目的大小小于或等于 1 KB 项目大小(用于计算写入容量单位)。如果索引条目大于这一大小,就会占用额外的写入容量单位。您可以考虑查询需要返回的属性类型并仅将键属性投影到索引中,从而最大程度地减少写入成本。

本地二级索引的存储注意事项

当应用程序向表中写入项目时,DynamoDB 会自动将适当的属性子集复制到应包含这些属性的所有本地二级索引。您的Amazon帐户需要支付在基表中存储项目以及在表的任何本地二级索引中存储属性的费用。

索引项目所占用的空间大小就是以下内容之和:

  • 基表的主键 (分区键和排序键) 的大小 (按字节计算)

  • 索引键属性的大小(按字节计算)

  • 投影的属性(如果有)的大小(按字节计算)

  • 每个索引项目的 100 字节开销

要估算本地二级索引的存储要求,您可以估算索引中项目的平均大小,然后乘以基表中的项目数。

如果表包含的某个项目未定义特定属性,但是该属性定义为索引排序键,则 DynamoDB 不会将该项目的任何数据写入到索引中。

项目集合

注意

本节仅涉及具有本地二级索引的表。

在 DynamoDB 中,项目集合是指在表中具有相同分区键值的任何项目组及其所有本地二级索引。在本节中使用的示例中,Thread表为ForumName,以及LastPostIndex也是ForumName。所有具有相同ForumName是同一个项目集合的一部分。例如,在Thread表和LastPostIndex本地二级索引,有一个论坛的项目集合EC2和论坛的不同项目集合RDS

下图显示了论坛的项目集合S3

在此图中,项目集合包含ThreadLastPostIndex其中,ForumName分区键值为 “S3”。如果表上有其他本地二级索引,那么这些索引中具有ForumName等于 “S3” 也将成为项目集合的一部分。

您可以在 DynamoDB 中使用以下任何操作来返回有关项目集合的信息:

  • BatchWriteItem

  • DeleteItem

  • PutItem

  • UpdateItem

这些操作中的每个操作都支持ReturnItemCollectionMetrics参数。当您将此参数设置为SIZE,您可以查看索引中有关每个项目集合的大小的信息。

下面是一个输出的示例,由此访问UpdateItem操作Thread对于,将使用ReturnItemCollectionMetrics设置为SIZE。已更新的项目具有ForumName值为 “EC2”,因此输出包含有关该项目集合的信息。

{ ItemCollectionMetrics: { ItemCollectionKey: { ForumName: "EC2" }, SizeEstimateRangeGB: [0.0, 1.0] } }

这些区域有:SizeEstimateRangeGB对象显示此项目集合的大小介于 0 到 1 GB 之间。DynamoDB 会定期更新此大小估计值,因此下次修改项目时,数字可能会有所不同。

物料收集大小限制

任何项目集合的最大大小为 10 GB。此限制不适用于没有本地二级索引的表。只有具有一个或多个本地二级索引的表才受影响。

如果项目集合超过 10 GB 限制,则 DynamoDB 将返回ItemCollectionSizeLimitExceededException,并且您将无法向项目集合添加更多项目或增加项目集合中项目的大小。(仍然允许对项目集合的大小进行读取和写入操作。) 您仍然可以将项目添加到其他项目集合。

要减小项目集合的大小,您可以执行以下操作之一:

  • 删除具有问题分区键值的所有不必要的项目。从基表中删除这些项目时,DynamoDB 还会删除具有相同分区键值的所有索引条目。

  • 通过删除属性或减小属性的大小来更新项目。如果将这些属性投影到任何本地二级索引中,DynamoDB 还会减小相应索引条目的大小。

  • 使用相同的分区键和排序键创建新表,然后将项目从旧表移动到新表。如果表具有不经常访问的历史数据,这可能是一种很好的方法。您也可以考虑将此历史数据存档到 Amazon Simple Storage Service (Amazon S3)。

当项目集合的总大小低于 10 GB 时,您可以再次添加具有相同分区键值的项目。

作为最佳实践,我们建议您对应用程序进行测量,以监控项目集合的大小。执行操作的一种方法是将ReturnItemCollectionMetrics设置为参数SIZE每当你使用BatchWriteItemDeleteItemPutItem,或者UpdateItem。您的应用程序应检查ReturnItemCollectionMetrics对象,并在项目集合超过用户定义的限制(例如 8 GB)时记录错误消息。设置一个小于 10 GB 的限制将提供一个预警系统,以便您知道项目集合正在及时接近限制,以便对其执行某些操作。

项目集合和分区

每个项目集合的表和索引数据存储在单个分区中。参考Thread表示例中,所有基表和索引项具有相同ForumName属性将存储在同一个分区中。“S3" 项目集合将存储在一个分区,“EC2” 存储在另一个分区中,“RDS” 存储在第三个分区。

您应该设计应用程序,以便在不同的分区键值之间均匀分布表数据。对于具有本地二级索引的表,应用程序不应在单个分区上的单个项目集合中创建读取和写入活动的 “热点”。