服务
关于
CloudProse博客
聚光灯

DynamoDB二级索引的最佳实践

为您的索引降价获得更多收益
 亚历克斯·德布雷
亚历克斯·德布里 | 2020年2月20日

AWS Data Hero,DynamoDB Expert和Trek10开发人员加速专家团队的最新成员指出,这是Alex DeBrie的第一篇Trek10帖子。

我很高兴地宣布Alex是Trek10的一部分 开发人员加速 专家组。

如果您喜欢在这里阅读的内容,并且学到了一些东西,请想象一下,与Alex和其他专注于培训您的团队并建立用例的专家在一个房间里几天能为您做些什么。

-Jared Short,Trek10开发人员加速

学到更多

DynamoDB已迅速成为开发人员构建无服务器的首选数据库&云原生应用程序。它的扩展特性,定价模型和一致的性能都是开发人员喜欢的属性。

DynamoDB要学习的最重要概念之一是如何使用二级索引。在本文中,我们将逐步介绍一些最佳实践,以充分利用DynamoDB中的二级索引。这包括:

  • 使用二级索引对可能更改的属性进行排序
  • 重载二级索引
  • 利用稀疏索引

如果您是DynamoDB的新手,并且不了解二级索引的工作原理,请确保您首先了解基础知识。看看 有关二级索引的AWS官方文档.

让我们开始吧!

使用二级索引对可能更改的属性进行排序

我要介绍的二级索引的第一个用例是需要对可能更改的属性进行排序时。

If you use a composite primary key in DynamoDB, each item you write to the table must have two elements in the primary key: a partition key 和 a sort key. You can think of these as comparable to SQL's GROUP BYORDER BY -- the partition key groups 所有 items with the same partition key together, 和 the items are ordered by the value in the sort key.

例如,假设您有一个票证跟踪应用程序。组织注册您的应用程序并创建票证。访问模式之一是允许用户按最新更新的顺序查看票证。

您决定按以下方式对表进行建模:

在此表设计中,组织名称是分区键,这为我们提供了“分组依据”功能。然后,票证上次更新的时间戳是排序键,该键为我们提供了“订购依据”功能。通过这种设计,我们可以使用DynamoDB的Query API来获取组织的最新票证。

However, this design causes some problems. When updating an item in DynamoDB, you may not change any elements of the primary key. In this case, your primary key includes the UpdatedAt field, which changes whenever you update a ticket. Thus, anytime you update a ticket item, we would need first to delete the existing ticket item, then create a new ticket item with the updated primary key.

我们造成了不必要的复杂操作,如果操作不当,可能会导致数据丢失。

Instead, let's try a different approach. For our primary key, let's use two attributes that won't change. We'll keep the organization name as the partition key but switch to using TicketId as the sort key.

现在我们的表如下所示:

Now we can add a secondary index where the partition key is OrgName 和 the sort key is UpdatedAt. Each item from the base table is copied into the secondary index, 和 it looks as follows:

请注意,这正是我们原始表格设计以前的样子。我们可以针对辅助索引使用Query API,以满足“获取最新更新的票证”访问模式。更重要的是,在更新项目时,我们不必担心复杂的删除+创建逻辑。将数据复制到辅助索引时,我们可以依靠DynamoDB来处理该逻辑。

重载二级索引

充分利用二级索引的第二种方法是使二级索引过载。这是一个更高级的主题,因此让我们退一步,解释一些背景概念。

在DynamoDB中对数据模型进行建模时,通常使用单个表来存储所有实体,这与RDBMS中的数据建模(其中每种类型的实体都有一个表)形成鲜明对比。

有关单表设计的更多背景,请查看我在 DynamoDB中单表设计的内容,原因和时间。有关实际使用的单表设计的详细示例,请查看 Forrest Brazeal出色的单表设计分步演练.

When modeling multiple entity types in a single table, you need to change how you design things. In our ticket tracking example above, we used OrgNameTicketId as the names of the partition key 和 sort key, respectively, in our primary key. However, if you're storing multiple entity types in a single table, it's unlikely each type has the same types of attributes.

Instead, we'll use more generic names for the elements in our primary key, such as PK for partition key 和 SK for sort key.

For example, imagine we have an application that includes both Organizations 和 Users. A User belongs to a particular Organization. We might have our PKSK values modeled as follows:

机构:

  • PK: ORG#<OrgName>
  • SK: ORG#<OrgName>

使用者:

  • PK: ORG#<OrgName>
  • SK: USER#<Username>

一个示例表如下所示:

在此表中,我们有两个组织:Facebook和Berkshire Hathaway。我们还有三个用户:沃伦·巴菲特,查理·芒格和谢丽尔·桑德伯格。

Notice how both types of entities are in the same table 和 use different patterns for the PKSK to co-exist. By using generic primary key attributes 和 storing different values there for different types of entities, we're 超载 我们的主键。对于不同的实体类型,属性意味着不同的事物。

您可以对二级索引执行相同的操作。如果您对组织和用户有其他访问模式,则无需创建针对两者的辅助索引。您可以创建一个二级索引来处理组织和用户的其他访问模式。

例如,假设我们有一个访问模式,我们想在一个请求中获取所有组织。另外,我们有一个访问模式,我们希望在创建日期之前按顺序获取特定组织中的用户。

For each item in our table, we could add GSI1PKGSI1SK values. Again, the names are generic since they will be used for different things.

For the Organization items, the GSI1PK will be ORGANIZATIONS, 和 the GSI1SK will be the Organization's name.

For the User items, the GSI1PK will be ORG#<OrgName> to group 所有 users in an Organization together. Then the GSI1SK will be the date the user was created to provide sorting.

现在,我们的基本表如下所示:

请注意,我们添加到每个项目中的红色轮廓属性。

如果切换到GSI1视图,我们的索引如下所示:

最重要的项目集合包括所有组织,接下来的两个集合分别包括Berkshire Hathaway和Facebook的用户。通过使二级索引键架构过载,我们在单个索引中满足了两种访问模式。

使用全局二级索引时,您需要为每个索引提供与基础表不同的读写能力。重载二级索引的最大好处之一是,您的预配置容量在多个用例之间共享。它减轻了您的整体运营负担,并可以节省您的钱。

利用稀疏索引

使用二级索引的最后一条建议是利用稀疏索引。首先让我们看看稀疏索引是什么,然后再看一下它在哪些方面有用。

创建二级索引时,将为索引定义一个关键架构。当您将一个项目写入基表时,如果DynamoDB具有该项目的二级索引的关键架构的元素,DynamoDB会将其复制到二级索引中。至关重要的是,如果一项没有这些元素,则不会将其复制到二级索引中。

这是稀疏索引背后的重要概念。稀疏索引是有意从表中排除某些项以帮助满足查询的索引。使用DynamoDB建模时,这可能是一种工具模式。

注意:如上所述,如果您使用重载索引,那么从技术上讲,许多二级索引可能是稀疏索引。假设您有一个包含三种不同实体类型的表-组织,用户和票证。如果其中两个实体有两个访问模式,而第三个实体只有一个访问模式,则这两个实体很可能会被投影到一个重载的二级索引中。从技术上讲,这是一个稀疏索引,因为它不包括基表中的所有项目。但是,它是稀疏索引模式的不太具体的版本。

当有明确的意图使用索引的稀疏性进行数据建模时,我喜欢将其称为稀疏索引。这在两种情况下最清楚地显示:

  1. 根据特定条件在实体类型内进行过滤

  2. 将单一类型的实体投影到二级索引中。

让我们依次看看这些。

使用稀疏索引为项目类型提供全局过滤器

使用稀疏索引的第一个示例是在过滤时 在实体类型内 根据特定条件。

在上一部分有关重载索引的示例中,我们有两种实体类型:组织和用户。想象一下,您有一个访问模式想要获取特定组织内所有具有管理员特权的用户。

如果组织中有大量用户,而我们想要的条件非常少见,那么读取所有用户并过滤掉不是管理员的用户将非常浪费。访问模式将很慢,并且我们将在废弃项目上花费大量读取容量。

相反,我们可以使用稀疏索引来提供帮助。为此,我们只向在其组织中具有管理员特权的用户项添加属性。

让我们在上一节中稍微修改一下示例。想象一下,我们仍然具有“组织”访问模式,但是我们不需要在创建用户之前按创建日期来获取用户。相反,我们只想获取组织内的管理员用户。

We'll keep the same GSI1PKGSI1SK pattern for Organizations. For Users, we'll still use ORG#<OrgName> as the GSI1PK, but we'll only have a value in GSI1SK if the user is an Administrator.

我们的表如下所示:

Notice that both Warren Buffett 和 Sheryl Sandberg have values for GSI1SK but Charlie Munger does not, as he is not an admin.

让我们看一下二级索引:

我们仍在使用重载的二级索引,并在单个索引中处理多个访问模式。但是,我们有意使用稀疏索引策略来过滤掉不是管理员的用户项目。

下一个策略有些不同。它没有使用重载索引,而是使用专用的稀疏索引来处理单一类型的实体。

使用稀疏索引来投影单一类型的实体

我想在其中使用稀疏索引的第二个示例是,如果我想将单一类型的实体投影到索引中。让我们看一个在哪里有用的例子。

想象一下,我有一个电子商务应用程序。我的应用程序中有几种不同的实体类型,包括进行购买的客户,指示特定购买的订单以及代表我可以出售的产品的InventoryItems。

我的表可能如下所示:

请注意,如前所述,该表包括Customers,Orders和InventoryItems,并且这些项目散布在表中。

我的营销部门有时会向所有客户发送营销电子邮件,以提醒他们热销产品或新产品。在基表中查找所有客户是一项昂贵的任务,因为我需要扫描整个表并过滤掉不是客户的项目。这浪费了很多时间,也浪费了我表的读取容量。

Instead of doing that, I'll add an attribute called CustomerIndexId on my Customer items. Now my table looks as follows:

Notice the Customer items now have an attribute named CustomerIndexId as outlined in red.

Then, I create a secondary index called CustomerIndex that uses CustomerIndexId as the partition key. Only Customer items have that attribute, so they are the only ones projected into that index.

二级索引如下所示:

Only the Customer items are projected into this table. Now when the marketing department wants to find 所有 Customers to send marketing emails, they can run a Scan operation on the CustomerIndex, which is much more targeted 和 efficient. By isolating 所有 items of a particular type in the index, our sparse index makes finding 所有 items of that type much faster.

同样,请注意,此策略不适用于索引重载。对于索引重载,我们使用二级索引以不同的方式对不同的实体类型建立索引。但是,此策略仅依赖于将单个实体类型投影到二级索引中。

只是一个味道

在本文中,我们学习了一些很好的策略,可以充分利用DynamoDB二级索引。

我们看到了二级索引是一种根据属性变化对数据进行排序的好方法。这是排行榜或查找最新更新的绝佳模式,因为您可以依靠DynamoDB来处理对商品进行重新排序所需的删除和重新创建。

我们了解了如何使二级索引过载,以减少表上的索引总数。最后,我们了解了稀疏索引如何通过在表上提供目标过滤器来启用专门的访问模式。

虽然我在DynamoDB数据模型中定期使用二级索引,但乐趣和功能并不仅限于此。有关更高级的主题和深入的指导,请查看我的后续文章 DynamoDB书 并提出问题或想法 @alexbdebrie @ trek10inc 在Twitter上!

喜欢您正在阅读的内容吗?

您可以得到与撰写我们的博客文章的人相同的人,这些人可以直接帮助您完成项目。您可以随时通过电子邮件发送 [email protected] 或通过我们的 联系页面 .

现在伸出手!

作者
 亚历克斯·德布雷
亚历克斯·德布里