【数据建模】设计一个DynamoDB数据模型,用于管理图形化的层次关系 -- 设计数据模型
视频号
微信公众号
知识星球
本节详细介绍分层数据模型设计概念,从设计原则、术语、原则开始,并定义用于在DynamoDB中存储组件数据的必要数据库属性。然后讨论了基表和全局二级索引(GSI)配置以及关键设计概念。最后,您可以看到在所有可用的汽车组件都存储在DynamoDB表中之后,基础表和GSI是什么样子的。
设计原则
作为最佳实践,最好定义设计原则。以下原则适用于本指南的用例。
- 设计一个可扩展和健壮的系统。确保性能足够高,延迟足够低,以符合数据检索服务级别协议(SLA)。
- 通过确保系统支持所有已识别的数据访问模式,遵守所有业务要求。
- 通过使用高效的写、读和数据存储机制,降低运营成本。
- 通过创建尽可能少的表和GSI,降低维护和操作复杂性。
- 通过分离实体项并应用行级细粒度访问模式和其他常见安全措施,确保数据安全性和可访问性。
术语
本指南使用以下术语。
- 分区键维护表内的数据分区和数据分片。
- 排序键支持分区内细粒度的项目级数据访问。
- 主键是DynamoDB中的唯一标识符。主键由分区键和排序键组成。
- 基本表是存储数据的主要DynamoDB表。
- 全局辅助索引(GSI)用于索引目的,以提供更快的数据查找。
- 在GSI中,分区键和排序键可能与基表中的键不同。
- 一次最多可以为单个DynamoDB表创建20个GSI。最大数量是AWS服务软限制。如果你需要更多的GSI,你可以要求增加。
- 每个GSI单独存储数据。您必须分别分配读和写单元,这会产生额外的成本。
- 每个GSI可以随时创建或销毁。创建或销毁GSI不会影响基表。
- 我们建议使用GSI而不是本地辅助索引(LSI)进行索引。
设计原则
使用以下方法设计了示例用例数据模型:
- 要有效地管理不同的实体,请使用DynamoDB单表设计模式。这意味着整个系统将有一个用于存储所有类型数据的DynamoDB表。
- 单表设计使您能够更灵活地维护一对多和多对多数据关系,而无需在多个DynamicDB表中复制数据。
- 使用单个表可降低维护复杂性和成本。
- 要了解有关单表设计的更多信息,请参阅DynamoDB文档中的“使用AmazonDynamoDB创建单表设计”博客文章和管理多对多关系的最佳实践。
- 为了在表分区内实现统一的数据分布,请使用更细粒度和分布式的属性作为分区键。有关更多信息,请参阅设计分区键以分配工作负载。
- 为了更快、更高效地查询并克服热分区问题,请使用适当的GSI Sharding跨分区分发数据。本指南中的示例对GSI2分区密钥使用随机分区。
- 对GSI1和GSI2中具有不同排序键的多个实体使用相同的分区键。
- 使用复合排序键。
- 使用两个GSI:
- GSI1,用于使用ParentID索引数据,以便您可以使用父ID进行查询
- GSI2,用于使用GraphId索引数据,以便您可以使用组件ID查询所有下级组件
- 仅使用分区键查询基表GSI1和GSI2。如果您不知道分区键,则无法执行数据查询。
- 我们建议在没有有力有效案例的情况下,不要在DynamoDB中使用SCAN操作。SCAN操作非常昂贵。
- 如果您必须经常使用SCAN操作,我们建议您重新设计数据模型。
- 仅限将数据插入基表。然后,GSI将以最终一致的方式自动同步。
定义属性
下一步是定义在DynamoDB表中存储组件信息所需的属性。每个属性用于在实体中存储键值对数据。实体可以包含任意数量的属性。我们建议使用较短的名称作为属性名称,因为在读取单元中将考虑属性的长度(字节大小)。对于本指南中的示例,定义了以下属性。
Attribute name |
Description |
---|---|
|
每个组件的唯一标识符(例如,CM1、CM2)。 |
|
父组件ID。例如,CM2的ParentID是CM1。 |
|
图形或树标识符。此示例有一棵树,其中顶部组件是CM1。实际上,可以有多棵树。 |
|
图中组件的路径。例如,CM8的路径是CM1|CM2|CM4|CM8。 |
设计基表、GSI和键
定义基表和全局辅助索引(GSI)的分区键。
- 遵循键设计最佳实践,在本例中使用ComponentId作为基表的分区键。由于它是唯一的,ComponentId可以提供粒度。DynamoDB使用分区键的哈希值来确定物理存储数据的分区。唯一的组件ID生成一个不同的哈希值,这可以促进表内数据的分布。可以使用ComponentId分区键查询基表。
- 要查找组件的直接子级,请创建一个GSI,其中ParentId是分区键,ComponentId是排序键。您可以使用ParentId作为分区键来查询此GSI。
- 要查找组件的所有递归子级,请创建一个GSI,其中GraphId是分区键,Path是排序键。您可以通过使用GraphId作为分区键和排序键上的BEGINS_WITH(Path,“$Path”)运算符来查询此GSI。
DynamoDB表键配置
Partition key |
Sort key |
Mapping attributes |
|
---|---|---|---|
Base table |
|
|
|
GSI1 |
|
|
|
GSI2 |
|
|
|
在表中存储组件
下一步是将每个组件存储在DynamoDB基表中。从示例树中插入所有组件后,将得到一个基本表,如下所示。
ComponentId |
ParentId |
GraphId |
Path |
---|---|---|---|
CM1 |
CM1#1 |
CM1 |
|
CM2 |
CM1 |
CM1#1 |
CM1|CM2 |
CM3 |
CM1 |
CM1#1 |
CM1|CM3 |
CM4 |
CM2 |
CM1#1 |
CM1|CM2|CM4 |
CM5 |
CM2 |
CM1#1 |
CM1|CM2|CM5 |
CM6 |
CM3 |
CM1#1 |
CM1|CM3|CM6 |
CM7 |
CM3 |
CM1#1 |
CM1|CM3|CM7 |
CM8 |
CM4 |
CM1#1 |
CM1|CM2|CM4|CM8 |
CM9 |
CM4 |
CM1#1 |
CM1|CM2|CM4|CM9 |
CM10 |
CM5 |
CM1#1 |
CM1|CM2|CM5|CM10 |
您可以使用此基表通过使用组件ID来查找组件的信息。
基表中的数据形成顶层组件(CM1)的层次树。要在DynamoDB中生成可扩展树,请在顶级组件ID上附加POST-FIX,例如#N。在本例中,CM1的GraphId为CM1#N,其中N是0–N范围内的随机数。N值是根据树的大小定义的,用于GSI中正确的数据分布。这种POST-FIX有助于避免热分区问题,它支持使用多个客户端并行运行查询,这有助于减少查询包含100多个组件的大树时的响应时间。
因为本例中的树很小,所以本例使用N=1。这将创建一个单独的分区CM1#1来存储整个树。如果使用N=5,它将创建五个分区来存储该树。在这种情况下,您必须查询所有五个分区以检索所有组件。然后,您必须合并所有数据以获得最终结果。
Path值是通过使用管道(|)运算符串联祖先来生成的。例如,从顶部开始的CM5路径是CM1|CM2|CM5。
GSI1索引
要检查组件的所有直接子级,需要使用ParentId作为分区键,使用ComponentId作为排序键来创建索引。以下数据透视表表示GSI1索引。您可以使用此索引通过使用父组件ID检索所有直接的子组件。例如,您可以了解汽车(CM1)中有多少电池可用,或者模块(CM4)中有哪些电池可用。
ParentId |
ComponentId |
---|---|
CM1 |
CM2 CM3 |
CM2 |
CM4 CM5 |
CM3 |
CM6 CM7 |
CM4 |
CM8 CM9 |
CM5 |
CM10 |
GSI2索引
以下数据透视表表示GSI2索引。它使用GraphId作为分区键,Path作为排序键进行配置。使用GraphId和排序键(Path)上的begins_with操作,可以在树中找到组件的完整谱系。
GraphId |
Path |
ComponentId |
---|---|---|
CM1#1 |
CM1 CM1|CM2 CM1|CM3 CM1|CM2|CM4 CM1|CM2|CM5 CM1|CM2|CM4|CM8 CM1|CM2|CM4|CM9 CM1|CM2|CM5|CM10 CM1|CM3|CM6 CM1|CM3|CM7 |
CM1 CM2 CM3 CM4 CM5 CM8 CM9 CM10 CM6 CM7 |
- 14 次浏览