有效的数据排序与 Amazon DynamoDB 数据库博客

公司动态

25

使用 Amazon DynamoDB 进行有效数据排序

by Lee Hannigan于2024年1月10日在高级 (300), Amazon DynamoDB,最佳实践永久链接评论 分享

关键点

Amazon DynamoDB 提供高度的可扩展性和性能,支持不同的工作负载。理解分区键和排序键在数据模型中的重要性。通过使用全局二级索引GSI,可以有效管理访问模式和数据排序。针对电子商务和游戏数据的两个示例模型展示了动态数据的处理。

Amazon DynamoDB 提供高可扩展性和性能,适用于不同工作负载的应用程序。尽管 DynamoDB 在高效分配数据方面表现优秀,但它遵循特定的排序顺序,这取决于选择的架构。本文将展示两种示例数据模型,一种用于存储电子商务订单信息,另一种用于存储游戏分数。我们将使用这些数据模型探索 DynamoDB 如何自然而有效地排列项目,并深入探讨建立自定义排序的有效策略。

在探讨详细内容之前,了解 DynamoDB 中分区键和排序键的重要性至关重要,以及我们如何利用其优势构建高效且可扩展的数据模型。

分区键和排序键特性

DynamoDB 中的复合主键由两个属性组成:分区键和排序键。分区键值作为输入经过内部哈希函数,决定项存储的特定分区DynamoDB 内部的物理存储。具有相同分区键值的项目会一起存储,并根据其排序键值进行排序。

在同时拥有分区键和排序键的表中,多个项目可以共享相同的分区键值,但具有相同分区键的项目必须有不同的排序键值。

排序

排序键,也称为范围键,负责确定项目在分区内存储的顺序。当您对 DynamoDB 表进行查询或扫描时,排序键使您能够根据排序键值以特定顺序检索数据。

共享相同分区键值的项目将根据排序键进行组织。排序机制取决于排序键的数据类型:

数据类型排序机制数字按数值顺序排列,确保数值比较简单高效。字符串按 UTF8 字节顺序排列,适合词典排序。二进制按字节级顺序排列,处理每个字节为无符号数据。

条件

DynamoDB 表中的排序键是优化查询效率的强大工具。通过结合排序键与条件,可以执行精确且高效的查询,仅检索所需的数据。例如,可以使用条件获取可排序属性范围内的项目,比如日期。这使得目标检索成为可能,减少扫描的数据量,从而改善查询性能。通过战略性地设计数据模型并有效利用排序键,您可以针对不同的访问模式量身定制查询,高效访问最重要的数据。

电子商务数据模型示例

为了更清晰地理解排序如何与分区键相关联,让我们可视化这个概念。DynamoDB 将数据存储在项目中类似于行,每个项目都有一个唯一标识符,称为分区键,作为在不同分区之间分配数据的主要方式。该模型使用排序键,决定每个分区内项目的排序。我们的 DynamoDB 表包含用户订单,userID 作为分区键,订单日期作为排序键。由于 DynamoDB 没有原生的日期数据类型,因此我们的排序键使用一个ISO8601 字符串格式。

对于分区键 userID,DynamoDB 根据用户 ID 在分区间分配用户数据。在每个分区内,DynamoDB 根据排序键即订单日期对数据进行排序。对此进行可视化后,可以将数据想象成像一个文件柜:

每个抽屉代表一个分区,凭借唯一的 userID 识别。每个抽屉分区内都有按其订单日期排序的文件项目。

以下表格阐明了我们的示例用例。我们可以将具有相同分区键但不同排序键的一组项目称为项目集合。

这个数据模型使我们能够在任何指定时间范围内查询用户的订单。例如,我们可以有效获取“user123”在三个月内下的所有订单。下面是如何使用AWS 命令行界面 (CLI) 执行该请求的示例:

bashaws dynamodb query tablename OrdersTable keyconditionexpression #PK = PK and #SK between start and finish expressionattributevalues {PK{SUSER#user123}start{S20230801} finish{S20231131}} expressionattributenames {#PKPK #SKSK}

现在让我们想象额外的业务需求,包括以下访问模式:

获取过去 24 小时的所有订单获取过去 7 天的所有订单获取过去 1 个月的所有订单获取过去 3 个月的所有订单

我们已经看到 DynamoDB 如何根据排序键值在项目集合内部维护排序。为了满足这些新的访问模式,需要一个跨所有分区键的排序顺序。

解决方案概览

为了建立一个跨所有分区键的排序顺序,一个关键的观察是我们缺少一个能够将数据分组到统一项目集合的属性。

如果从过去检索所有订单不是常规请求的访问模式,我们可以使用扫描操作,并过滤结果以匹配所需的时间范围。然而,这种方法在性能和成本上可能是低效的。因此,如果这种访问模式经常被请求,我们需要一种替代方法。

利用全局二级索引

全局二级索引 (GSI) 是一种 DynamoDB 特性,维护基表数据的最终一致性副本。GSI 允许根据主键以外的属性高效查询表。它为查询和过滤数据提供灵活性,支持并行查询,并且对于优化查询性能和适应不同访问模式至关重要。

现在我们了解了 DynamoDB 如何在项目集合内维持顺序,我们可以设计一个替代架构,以 GSI 支持我们额外的访问模式。

方法 1非最优

认识到项目集合有效组织数据的能力,我们已经实现了一个使用日期属性的全局二级索引GSI,按一天的粒度进行分组。这使我们能够高效地组合每个特定日期的订单。为此,我们在数据结构中引入了一个名为 gsi1pk 的附加属性,以存储所需的日期值。

如果您需要通过向每个项目添加额外属性来增强当前数据模型,则需要执行回填操作。有关此操作的详细指南,请参见我们的详尽博客文章。

现在,我们能够高效查询特定日期的数据,例如获取 20231003 的所有订单。虽然这种方法在单日查询上有效,但我们的用例要求处理更广泛的日期范围。例如,获取整个星期的数据会需要进行七个并行请求,每天一个。尽管对于一星期而言可以管理,但是随着日期范围的扩大,所需请求的数量将成线性增长,这可能影响可扩展性。

获取 20231003 至 20231006 的所有订单的示例命令如下:

bashaws dynamodb query tablename OrdersTable indexname GSI1 keyconditionexpression #gsi1pk = gsi1pkval expressionattributevalues {gsi1pkval{S20231003}} expressionattributenames {#gsi1pkgsi1pk}

aws dynamodb query tablename OrdersTable indexname GSI1 keyconditionexpression #gsi1pk = gsi1pkval expressionattributevalues {gsi1pkval{S20231004}} expressionattributenames {#gsi1pkgsi1pk}

aws dynamodb query tablename OrdersTable indexname GSI1 keyconditionexpression #gsi1pk = gsi1pkval expressionattributevalues {gsi1pkval{S20231005}} expressionattributenames {#gsi1pkgsi1pk}

aws dynamodb query tablename OrdersTable indexname GSI1 keyconditionexpression #gsi1pk = gsi1pkval expressionattributevalues {gsi1pkval{S20231006}} expressionattributenames {#gsi1pkgsi1pk}

请求数量:4返回项目:4消耗容量:2

尽管这个方法简单易行,但随着查询日期数量的增加,它的扩展性较差。一个显著的缺点是,即使对于像 20231005 这样的没有数据的日期,也需要进行请求,导致不必要的成本,而没有产生任何相关信息。

方法 2最优

一种改进的策略是利用排序键,使我们能够有效地使用条件。在此方法中,我们为 GSI 分区键 gsi1pk 选择一个固定值,有效地将所有数据整合到一个项目集合中。排序键被定义为精确到毫秒粒度的 ISO 8601 时间戳字符串。这些时间戳已经在我们的项目中作为 SK 属性存储。

请注意,我们创建了一个统一的项目集合,存储在 gsi1pk 分区键属性下,常量值为 1。因此,现在我们的所有项目根据订单创建时间戳按字典顺序排列。

现在让我们重复示例,从 20231003 到 20231006 获取所有订单的命令如下:

bashaws dynamodb query tablename Orders indexname GSI1 keyconditionexpression #gsi1pk = gsi1pkval AND #SK between from AND to expressionattributevalues {gsi1pkval{S1} from{S20231003} to{S20231007}} expressionattributenames {#gsi1pkgsi1pk #SKSK}

请求数量:1返回项目:4消耗容量:05

这种方法不仅提高了效率,还提供了更高的灵活性。如果我们的业务需求演变为包括其他访问模式,例如检索过去 30 分钟的所有订单、获取最近的 100 个订单或访问最旧的 100 个订单,我们的数据模型赋予我们高效执行这些查询的能力。

使用单一固定值作为分区键会引入性能瓶颈,我们将在本文后面对其进行讨论。

游戏数据模型

DynamoDB 常用于存储游戏信息,例如分数和玩家信息。其可扩展性和性能能力使其非常适合游戏应用。灵活的架构设计使得可以在不复杂的数据库修改下调整游戏机制,同时其低延迟操作确保实时更新,是管理游戏应用中的排行榜、玩家资料和成就的可靠选择。一个示例数据模型可能如下所示:

在这个数据模型示例中,我们观察到一个简单的主键,定义为代表用户唯一标识符的分区键,即 userId。这种设计对围绕 userId 的简单键值查询有效,例如检索 user0011 的得分或更新 user30046 的得分。

云梯最新版安卓版下载

想象一下新的用例需要生成展示前10名和前50名用户的排行榜。虽然在分区键中引入一个“得分”属性似乎是直观的想法,但该方法遇到两个显著的挑战,使其不切实际。在 DynamoDB 中,不能修改主表中的主键,这限制了对分数值的高效更新。例如,不能使用 UpdateItem 来更改主键属性,而必须删除该项目然后使用 PutItem 引入具有所需属性的新项目。更重要的是,DynamoDB 对排序的限制在于该上下文中的每个用户项目实际上会构成一个只包含单个项目的项目集合,从而削弱了所需排行榜功能的可行性。

我们可以在这里使用类似于我们的电子商务订单表的解决方案,创建一个具有静态分区键值的 GSI,以便将所有用户项目放在单个项目集合中,并使用得分属性作为 GSI 排序键。因此,我们在数据模型中包含 gsi1pk 作为一个属性:

有效的数据排序与 Amazon DynamoDB 数据库博客

现在我们的 GSI 将数据分组在一起,按玩家得分升序排列:

鉴于目前的用例是提供最高得分用户,我们必须以降序读取索引。为此,我们可以使用查询 API 中的ScanIndexForward 属性并将其设置为 False。

bashaws dynamodb query tablename GameTable indexname GSI1 keyconditionexpression #gsi1pk = gsi1pkval expressionattributevalues {gsi1pkval{S1}} expressionattributenames {#gsi1pkgsi1pk} noscanindexforward limit 10

这个解决方案使我们能够高效

驱动成本效率:Salesforce 如何创新与节省开支关键要点通过持续的成本优化,企业可以最大化云服务的价值。本文提供了三大资源,帮助企业识别及执行云成本优化的机会,确保在提高效率的同时,推动创新和业务价值的提升。通过不断进行成本优化,您能够最大化云计算给企业带来的价值。您可以构建现代且可扩展的应用...

提升您的营销解决方案:利用 Amazon Personalize 和生成性 AI关键要点生成性 AI 正在改变企业运作的方式,提升数据驱动的决策能力和用户体验。Amazon Personalize 与生成性 AI 的结合能实现个性化且富有吸引力的客户沟通,提升营销效果。本文介绍如何通过实例来利用这两...