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

全局二级索引

一些应用程序可能需要使用很多不同的属性作为查询条件,来执行许多类型的查询。要支持这些要求,您可以创建一个或多个全局二级索引,针对这些索引发出 Query 请求。为进行说明,考虑使用一个名为 GameScores 的表跟踪一个移动游戏应用程序的用户和分数。GameScores 中的每个项目都通过分区键 (UserId) 和排序键 (GameTitle) 进行标识。下表显示了此表中项目的组织方式,(并未显示所有属性)

现在假设您要编写一个领先排名表应用程序以显示每个游戏的最高分数。指定了键属性(UserId 和 GameTitle)的查询十分高效;不过,如果应用程序需要仅基于 GameTitleGameScores 检索数据,则需要使用 Scan 操作。随着更多项目添加到表中,所有数据的扫描会变得缓慢且低效,从而难以回答以下问题:

  • 对游戏 Meteor Blasters 记录的最高分数是多少?

  • 哪个用户拥有 Galaxy Invaders 的最高分数?

  • 最高赢输比是多少?

要加快对非键属性的查询,您可以创建一个 全局二级索引。全局二级索引包含从基表中选择的一组属性,但是这些属性按与表主键不同的主键进行排列。索引键无需具有表中的任何键属性;它甚至无需具有与表相同的键架构。

例如,您可以创建名为 GameTitleIndex 的全局二级索引,其分区键为 GameTitle,排序键为 TopScore。基表的主键属性始终投影到某个索引,因此 UserId 属性也存在。GameTitleIndex 索引如下图所示:

现在,您可以查询 GameTitleIndex 并方便地获取 Meteor Blasters 的分数。结果按排序键值 TopScore 进行排序。如果您将 ScanIndexForward 参数设置为 false,则结果按降序返回,因此最高分数最先返回。

每个全局二级索引都必须有分区键,另外可以有可选的排序键。索引键架构可以与基表架构不同;您可以使用具有简单主键 (分区键) 的表,并创建具有复合主键(分区键和排序键)的全局二级索引,反之亦然。索引键属性可以包含基表中的任何顶级字符串、数字或二进制属性;不允许包含其他标量类型、文档类型和集类型。

您可以在需要时将其他基表属性投影到索引。当您查询索引时,DynamoDB 可以高效地检索这些已投影的属性;但是,全局二级索引查询无法从基表获取属性。例如,如果您查询 GameTitleIndex(如上图所示),则该查询无法访问除 TopScore 外的任何非键属性(但键属性 GameTitleUserId 会自动投影)。

在 DynamoDB 表中,每个键值都必须唯一。但是,全局二级索引中的键值无需唯一。为进行说明,假设一个名为 Comet Quest 的游戏难度特别高,许多新用户进行尝试,但是无法获得零以上的分数。下面是一些可用于表示这种情况的数据:

UserId GameTitle TopScore
123 Comet Quest 0
201 Comet Quest 0
301 Comet Quest 0

将这些数据添加到 GameScores 表后,DynamoDB 将它传播到 GameTitleIndex 中。如果我们随后以 Comet Quest 作为 GameTitle 并以 0 作为 TopScore 来查询索引,则将返回以下数据:

只有具有指定键值的项目才会出现在响应中;在该数据集中,项目未采用特定顺序。

全局二级索引仅跟踪其键属性实际存在的数据项目。例如,假设您向 GameScores 表添加了另一个新项目,但是仅提供了必需的主键属性:

UserId GameTitle
400 Comet Quest

因为未指定 TopScore 属性,DynamoDB 不会将此项目传播到 GameTitleIndex。因此,如果您针对所有 Comet Quest 项目查询 GameScores,则会获得以下四个项目:

GameTitleIndex 执行的相似查询仍会返回三个项目,而不是四个。这是因为,如果项目带有不存在的 TopScore,则不会传播到索引:

属性投影

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

创建 二级索引 时,您需要指定投影到索引中的属性。DynamoDB 提供了三个不同选项来实现这一目的:

  • KEYS_ONLY - 索引中的每个项目仅包含表分区键和排序键值以及索引键值。KEYS_ONLY 选项生成尽可能小的 二级索引。

  • INCLUDE - 除 KEYS_ONLY 中描述的属性外,二级索引 还包括您指定的其他非键属性。

  • ALL - 二级索引 包含源表中的所有属性。由于所有表数据均在索引中得以复制,因此 ALL 投影生成尽可能大的 二级索引。

在上图中,GameTitleIndex 没有任何其他投影的属性。应用程序可以在查询中使用 GameTitleTopScore;不过,无法有效确定特定游戏的最高分数用户,也无法确定最高赢输比。要支持对这些数据进行查询,最有效的方法是将这些属性从基表投影到全局二级索引,如下图所示:

因为非键属性 Wins 和 Losses 投影到索引,所以应用程序可以确定任何游戏或是任何游戏和用户 ID 组合的赢输比。

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

  • 如果只需要访问少量属性,同时尽可能降低延迟,就应考虑仅将键属性投影到全局二级索引。索引越小,存储索引所需的成本越少,并且写入成本也会越少。

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

  • 如果您需要频繁访问大多数非键属性,可以将这些属性 (甚至是整个基表) 投影到全局二级索引。这样可获得最大的灵活性;但是,存储成本会增加,甚至是加倍。

  • 如果您的应用程序并不会频繁查询表,但必须要对表中的数据执行大量写入或更新操作,就应考虑投影 KEYS_ONLY。这是最小的全局二级索引,但仍可用于查询活动。

查询全局二级索引

您可以使用 Query 操作来访问全局二级索引中的一个或多个项目。查询必须指定您要使用的基表名称和索引名称、查询结果中要返回的属性以及要应用的任何查询条件。DynamoDB 可按升序或降序返回结果。

考虑 Query(为领先排名表应用程序请求游戏数据)返回的以下数据:

Copy
{ "TableName": "GameScores", "IndexName": "GameTitleIndex", "KeyConditionExpression": "GameTitle = :v_title", "ExpressionAttributeValues": { ":v_title": {"S": "Meteor Blasters"} }, "ProjectionExpression": "UserId, TopScore", "ScanIndexForward": false }

在此查询中:

  • DynamoDB 通过使用 GameTitle 分区键查找 Meteor Blasters 的索引项目,来访问 GameTitleIndex。具有此键的所有索引项目都彼此相邻存储,以实现快速检索。

  • 在此游戏中,DynamoDB 使用索引访问此游戏的所有用户 ID 和最高分数。

  • 因为 ScanIndexForward 参数设置为 false,所以结果按降序返回。

扫描全局二级索引

您可以使用 Scan 操作来检索全局二级索引中的全部数据。您必须在请求中提供基表名称和索引名称。通过 Scan,DynamoDB 可读取索引中的全部数据并将其返回到应用程序。您还可以请求仅返回部分数据并放弃其余数据。为此,请使用 Scan 操作的 FilterExpression 参数。有关更多信息,请参阅 Scan 的筛选表达式

表与全局二级索引之间的数据同步

DynamoDB 自动将每个全局二级索引与其基表同步。当应用程序对某个表写入或删除项目时,该表的所有全局二级索引都会使用最终一致性模型异步更新。应用程序绝不会直接向索引中写入内容。但是,您有必要了解 DynamoDB 如何维护这些索引。

在创建全局二级索引之后,您可以指定一个或多个索引键属性及其数据类型。这就意味着,无论您何时向基表中写入项目,这些属性的数据类型必须与索引键架构的数据类型匹配。在 GameTitleIndex 这一索引中,其中的 GameTitle 分区键定义为字符串数据类型,TopScore 排序键的数据类型则为数字。如果您尝试向 GameScores 表添加项目并为 GameTitleTopScore 指定其他数据类型,则 DynamoDB 会因数据类型不匹配而返回 ValidationException

在表中放置或删除项目时,表的全局二级索引会以最终一致性方式进行更新。在正常情况下,对表数据进行的更改会瞬间传播到全局二级索引。但是,在某些不常发生的故障情况下,可能出现较长时间的传播延迟。因此,应用程序需要预计和处理对全局二级索引进行的查询返回不是最新结果的情况。

如果向表中写入项目,无需指定任何全局二级索引排序键的属性。以 GameTitleIndex 为例,您无需指定 TopScore 属性的值就可以向 GameScores 表写入新项目。在此情况下,Amazon DynamoDB 不会向此特定项目的索引写入任何数据。

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

全局二级索引 的预配置吞吐量注意事项

创建全局二级索引时,必须根据该索引的预期工作负载指定读取和写入容量单位。全局二级索引的预配置吞吐量设置独立于其基表的相应设置。对全局二级索引执行的 Query 操作会占用索引 (而非基表) 的读取容量单位。在表中放置、更新或删除项目时,表的全局二级索引也会更新;这些索引更新会占用索引 (而非基表) 的写入容量单位。

例如,如果您对全局二级索引执行 Query 操作并超过其预配置读取容量,则您的请求会受到阻止。如果您对表执行大量写入活动,但是该表的全局二级索引没有足够写入容量,则对该表进行的写入活动会受到限制。

要查看全局二级索引的预配置吞吐量设置,请使用 DescribeTable 操作;将返回有关表的所有全局二级索引的详细信息。

读取容量单位

Global secondary index支持最终一致性读取,每个读取占用一半的读取容量单位。这意味着,单个全局二级索引查询对于每个读取容量单位,可以检索最多 2 × 4 KB = 8 KB。

对于 全局二级索引 查询,DynamoDB 的预配置读取活动的计算方式与其表查询的方式相同。唯一不同的是,本次计算基于索引条目的大小,而不是基表中项目的大小。读取容量单位的数量就是返回的所有项目的所有投影属性大小之和,该结果会向上取整到 4 KB 的下一个倍数。有关 DynamoDB 如何计算预配置吞吐量使用情况的更多信息,请参阅 读取和写入的吞吐量设置

Query 操作所返回的结果的大小上限为 1 MB,这包括所有属性名称的大小和所返回的所有项目的值。

例如,请考虑使用每项均包含 2000 字节数据的 全局二级索引。现在假设您对此索引执行 Query 操作,并且该查询返回了 8 个项目。匹配项目的总大小为 2000 字节 × 8 个项目 = 16000 字节;此数字随后会向上取整为最接近 4 KB 的界限值。由于全局二级索引查询具有最终一致性,因此总成本是 0.5 × (16 KB / 4 KB),即 2 个读取容量单位。

写入容量单位

在添加、更新或删除表中的项目,并且全局二级索引受此影响时,全局二级索引将占用为此操作预配置的写入容量单位。一次写入操作的预配置吞吐量总成本是对基表执行的写入操作以及更新全局二级索引所占用的写入容量单位之和。请注意,如果对表执行的写入操作不需要全局二级索引更新,则不会占用索引的写入容量。

为了成功完成表写入,表的预配置吞吐量设置及其所有全局二级索引必须具有足够写入容量以容纳写入内容;否则,对表的写入将受限制。

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

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

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

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

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

  • 如果对表的更新仅更改了索引键架构中投影属性的值,但不更改任何索引键属性的值,则需要执行一次写入以将投影属性的值更新到索引中。

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

全局二级索引的存储注意事项

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

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

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

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

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

  • 每个索引项目 100 bytes 的开销

要估算全局二级索引的存储要求,您可以估算索引中项目的平均大小,然后乘以基表中具有全局二级索引键属性的项目数。

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