Using Global Secondary Indexes in DynamoDB - Amazon DynamoDB

如果我们为英文版本指南提供翻译,那么如果存在任何冲突,将以英文版本指南为准。在提供翻译时使用机器翻译。

Using Global Secondary Indexes in DynamoDB

一些应用程序可能需要使用很多不同的属性作为查询条件,来执行许多类型的查询。为了支持这些要求,您可以创建一个或多个 global secondary indexes 和问题 Query 对这些索引的请求 Amazon DynamoDB.

Scenario: Using a Global Secondary Index

为进行说明,考虑使用一个名为 GameScores 的表跟踪一个移动游戏应用程序的用户和分数。GameScores 中的每一项使用一个分区键 (UserId) 和一个排序键 (GameTitle) 标识。下表显示了此表中项目的组织方式,(并未显示所有属性。)


                包含用户 ID、头衔、得分、日期和输赢次数的 GameScores 表。

现在假设您要编写一个排行榜应用程序以显示每个游戏的最高分数。指定键属性 (UserIdGameTitle) 的查询将会非常高效。但是,如果应用程序仅需要基于 GameTitleGameScores 检索数据,则需要使用 Scan 操作。随着更多项目添加到表中,所有数据的扫描会变得缓慢且低效。这会使得难于回答以下问题:

  • What is the top score ever recorded for the game Meteor Blasters?

  • Which user had the highest score for Galaxy Invaders?

  • What was the highest ratio of wins vs. losses?

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

例如,您可以创建 全局二级索引 名称 GameTitleIndex,带有 GameTitleTopScore。基本表的主要关键属性始终预计为指数,因此 UserId 属性也存在。GameTitleIndex 索引如下图所示。


                包含头衔、得分和用户 ID 的 GameTitleIndex 表。

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

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

您可以在需要时将其他基表属性投影到索引。当您查询索引时,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。如果我们使用COMETQuest查询指数 GameTitle 和0 TopScore,返回以下数据。


                包含头衔、最高得分和用户 ID 的表。

响应中仅显示具有指定键值的项。在该组数据中,项没有特定顺序。

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

UserId GameTitle
400 * Comet Quest

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


                包含 4 个头衔列表、最高得分和用户 ID 的表。

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


                包含 3 个头衔列表、最高得分和用户 ID 的表。

Attribute Projections

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

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

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

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

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

在上一图中, GameTitleIndex 只有一个预计属性: UserId。因此,虽然应用程序可以有效地确定 UserId 每个比赛的顶级评分员 GameTitleTopScore 在查询中,它无法有效确定最高评分人的获胜与损失比率。为此,它必须对基表执行额外查询,以获取每个得分榜选手的输赢数据。要支持对此数据进行查询,更高效的方法是将这些属性从基表投影到全局二级索引,如下图所示。

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

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

  • If you need to access just a few attributes with the lowest possible latency, consider projecting only those attributes into a 全局二级索引. The smaller the index, the less that it costs to store it, and the less your write costs are.

  • If your application frequently accesses some non-key attributes, you should consider projecting those attributes into a 全局二级索引. The additional storage costs for the 全局二级索引 offset the cost of performing frequent table scans.

  • If you need to access most of the non-key attributes on a frequent basis, you can project these attributes—or even the entire base table— into a 全局二级索引. This gives you maximum flexibility. However, your storage cost would increase, or even double.

  • If your application needs to query a table infrequently, but must perform many writes or updates against the data in the table, consider projecting KEYS_ONLY. The 全局二级索引 would be of minimal size, but would still be available when needed for query activity.

Reading Data from a 全局二级索引

您可以从 全局二级索引 使用 QueryScan 操作。TheThethe GetItemGetBatchItem 操作不能用于 全局二级索引.

Querying a 全局二级索引

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

考虑为排行榜应用程序请求游戏数据的 Query 返回的以下数据。

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

在此查询中:

  • DynamoDB accesses GameTitleIndex, using the GameTitle partition key to locate the index items for Meteor Blasters. All of the index items with this key are stored adjacent to each other for rapid retrieval.

  • Within this game, DynamoDB uses the index to access all of the user IDs and top scores for this game.

  • The results are returned, sorted in descending order because the ScanIndexForward parameter is set to false.

Scanning a 全局二级索引

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

Data Synchronization Between Tables and Global Secondary Indexes

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

全局二级索引继承基表的读/写入容量模式。有关更多信息,请参阅 更改读/写容量模式时的注意事项。)

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

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

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

相较于索引数量较少的表,拥有较多全局二级索引的表会产生较高的写入活动成本。有关更多信息,请参阅 Provisioned Throughput Considerations for Global Secondary Indexes。)

Provisioned Throughput Considerations for Global Secondary Indexes

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

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

注意

为了避免触发可能的限制,全局二级索引的预配置写入容量应等于或大于基表的写入容量,因为新更新将同时写入基表和全局二级索引。

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

Read Capacity Units

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

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

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

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

Write Capacity Units

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

要成功写入表,表及其所有全局二级索引的预配置吞吐量设置必须具有足够的写入容量来允许写入。否则,对表的写入将受到限制。

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

  • If you write a new item to the table that defines an indexed attribute, or you update an existing item to define a previously undefined indexed attribute, one write operation is required to put the item into the index.

  • If an update to the table changes the value of an indexed key attribute (from A to B), two writes are required, one to delete the previous item from the index and another write to put the new item into the index. 

  • If an item was present in the index, but a write to the table caused the indexed attribute to be deleted, one write is required to delete the old item projection from the index.

  • If an item is not present in the index before or after the item is updated, there is no additional write cost for the index.

  • If an update to the table only changes the value of projected attributes in the index key schema, but does not change the value of any indexed key attribute, one write is required to update the values of the projected attributes into the index.

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

Storage Considerations for Global Secondary Indexes

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

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

  • The size in bytes of the base table primary key (partition key and sort key)

  • The size in bytes of the index key attribute

  • The size in bytes of the projected attributes (if any)

  • 100 bytes of overhead per index item

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

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