服务
关于
CloudProse博客
聚光灯

使用DynamoDB进行数据建模的十大规则

DynamoDB是世界上功能最强大,增长最快的数据库之一。让'为正确使用它提供了一些启示。
亚历克斯·德布雷
亚历克斯·德布里 | 2020年4月8日

DynamoDB是目前增长最快的数据库之一。它是完全托管的,具有按使用量计费,并且非常适合无服务器计算。

但是,在DynamoDB中对数据进行建模与在传统关系数据库中进行建模有很大不同。而且,如果您尝试像关系数据库一样对DynamoDB表进行建模,那么您将遭受很大的伤害。

在本文中,我们将介绍我使用DynamoDB进行数据建模的十大技巧。从基本原理的理解到具体的设计技巧,再到计费建议,它们遍及整个领域。通过这些技巧,您将成功实现DynamoDB。

如果您发现自己喜欢这些内容,我将非常感谢并鼓励您 也看看我有关DynamoDB的新书!

让我们开始吧!

了解DynamoDB的单表设计基础

到目前为止,从传统的关系数据库迁移到DynamoDB时,您将进行的最大思维转变是接受“单表设计”。在关系数据库中,每个不同的实体都会收到自己的表,表的形状经过专门设计以保存该实体的数据。实体的每个属性都有对应的列,其中包含所需的值和约束。

对于DynamoDB,情况并非如此。您会将所有实体(客户,订单,库存等)塞入一个表中。除了唯一标识每个项目的主键外,没有必需的列和属性。

您的表将更像以下内容:

如果您是DynamoDB和NoSQL设计的新手,这可能看起来像象形文字,但请避免使用它。

花一些时间来了解 为什么需要DynamoDB中的单表设计。单表设计旨在以访问模式所需的形状有效地组织数据,以快速有效地使用数据。

您将没有关系数据库拥有的整洁,整洁,类似电子表格的数据。实际上,您的桌子看起来 “更像是机器代码,而不是简单的电子表格”。但是您将拥有一个可以大规模扩展而又不降低性能的数据库。

开始之前了解访问方式

引用文字:“如果您不知道要走的路,那么任何道路都会带您到达那里。” (刘易斯·卡洛尔)

使用单表设计,您可以设计表来处理访问模式。这意味着您在设计之前必须了解您的访问模式。

因此,许多开发人员都希望在知道如何使用DynamoDB表之前就对其进行设计。但是同样,这就是关系数据库的思维方式正在渗透到您的流程中。对于关系数据库,您首先要设计表,然后添加索引并编写查询以获取所需的数据。使用DynamoDB,您首先要询问如何访问数据,然后构建表来处理这些模式。

这需要周到的工作。它需要与项目经理和业务分析师合作,以全面了解您的应用程序。尽管这似乎使您放慢了脚步,但当您不必在应用程序增长时考虑扩展数据库时,您会很高兴地完成了工作。

首先建模,最后编码

作为开发人员,很难不直接进入代码。没有什么比从一无所有得到多巴胺的成功要多得多了。从说:“是的,我建造了!”

但是您需要抵制DynamoDB中的这种冲动。概述了访问模式后,请花时间为DynamoDB表建模。应该做的 您的代码。你可以用笔&纸张,Microsoft Excel或 适用于Amazon DynamoDB的NoSQL Workbench.

在对代码进行建模时,应该制作两个工件:

  1. 一个 实体图,它显示了表中每种实体类型的主键模式。
  2. 一个 访问模式图,其中列出了应用程序中的每种访问模式并描述了如何处理它(主表或辅助索引?您需要哪些参数?会有条件表达式或事务吗?)

例如,这是我书中一个示例的完整实体图:


在这种情况下,我仅使用电子表格列出每个实体类型和每个实体的主键模式。我的表中还有用于辅助索引的其他页面。

一旦完成了这些工件,就可以实施。这些工件将成为您的服务文档的重要补充。

适应非规范化

在学习关系数据建模时,我们听到了有关标准化的所有信息。不要在多个地方重复数据。第一个范式,第二个范式等。在标准化数据时,您可以将多个表连接在一起作为查询时间,以获得最终答案。

规范化是针对具有截然不同的假设的世界而建立的。在1980年代的数据中心中,存储非常昂贵,而计算却相对便宜。但是时代变了。存储价格便宜,而计算成本却很高。

诸如联接和复杂过滤器之类的关系模式会消耗宝贵的计算资源。使用DynamoDB,您可以针对当今的问题进行优化。这意味着通过避开连接来节省计算量。相反,无论是否通过 跨多个记录复制数据将相关记录直接存储在父记录中.

通过非规范化,数据完整性成为应用程序关注的焦点。您需要考虑何时可以更改这些重复的数据以及如何在需要时进行更新。但是,这种非规范化将为您提供比其他数据库更大的扩展范围。

确保主键的唯一性

数据建模中的一个常见要求是您拥有在整个应用程序中唯一的属性。例如,您可能不希望两个用户使用相同的用户名注册,或者您可能希望阻止两个具有相同OrderId的订单。

在DynamoDB中,表中的每个记录由表的主键唯一标识。您可以使用此主键来确保不存在具有相同主键的记录。为此,您可以使用 条件表达式 如果已经存在具有相同密钥的项目,以防止写入项目。

另一个警告:您只能在 单一属性 用您的主键。如果尝试通过将两个属性都构建为主键来在两个属性之间断言唯一性,则只能确保不存在具有相同属性的其他项 组合 两个属性。

例如,假设您需要一个用户名和一个电子邮件地址才能创建用户。在您的应用程序中,您要确保没有其他人具有相同的用户名 没有其他帐户使用相同的电子邮件地址。要在DynamoDB中处理此问题,您需要 在交易中创建两个项目 其中每个操作都断言不存在具有相同主键的项目。

如果使用此模式,表将最终如下所示:

避免热键

与大多数NoSQL数据库一样,DynamoDB通过将数据拆分到多个实例中来对数据进行分区(或“分片”)。每个实例仅保存数据的一个子集。这种划分机制是 NoSQL数据库比SQL数据库具有更大的扩展能力。如果您的数据全部在一台计算机上,则需要使用更多的RAM和CPU来扩展到越来越大的实例大小。在这种规模下,您将获得越来越少的回报,最终,您将完全达到单个实例的规模极限。

为了对数据进行分区,DynamoDB使用了以下概念: 分区键。分区键是主键的一部分,用于指示哪个实例应存储该特定数据。

即使采用这种分区策略,也需要确保避免使用热键。热键是DynamoDB表中的分区键,它比表中的其他键接收的流量要多得多。如果您的数据高度偏斜(例如,数据具有 Zipf分布,或者如果您对数据的建模不正确,可能会发生这种情况。

DynamoDB已完成 大量工作使热键对您来说不再是一个问题。这包括将表的总容量移到需要的键上,以便更好地处理数据的不均匀分布。

使用热键需要考虑的最大问题是分区限制。 DynamoDB中的单个分区不能超过3,000个RCU或1,000个WCU。那些是 每秒 限制,因此它们会变得很高,但是如果您具有大规模应用程序,则可以实现。

使用辅助索引处理其他访问模式

使用DynamoDB进行数据建模时,您的主键至关重要。如上所述,它将用于强制唯一性。它也用来 筛选和查询您的数据.

但是您在表中的特定项目上可能有多种冲突的访问模式。我经常使用的一个例子是一张表格,其中包含不同电影中的演员扮演的角色。您可能有一种访问方式来按演员名称来获取角色,而另一种访问方式来来获取特定电影中的角色。

二级索引使您可以处理这些其他访问模式。在表上创建二级索引时,DynamoDB将以重新设计的形状处理将所有数据从主表复制到二级索引的过程。在上面的电影角色示例中,我们的主表可以使用演员的姓名作为分区键,而辅助索引可以使用电影的名称作为分区键。这允许处理我们两种访问模式,而无需我们自己维护数据的两个副本。

将汇总构建到数据模型中

对象之间的关系,是否 一对多关系 或多对多关系在数据建模中很常见。您将拥有一个具有多个相关实体的实体(“父”)。示例包括客户订购(一个客户将随时间进行多个订单)或公司订购给员工(单个公司将有很多员工)。

Often, you'll want to display the total count of related entities when showing the parent item. But for some relationships, this count can be quite large. Think of the number of stargazers for the React repository on GitHub(超过146,000)或奥斯卡特别著名的自拍上转发的次数(超过310万!)。

显示这些计数时,每次计数数据中的所有相关记录以显示计数效率很低。而是,应在插入相关项目时将这些聚合存储在父项目上。

有两种方法可以处理此问题。首先,您可以使用 DynamoDB Transactions可在您创建相关项目的同时增加计数。当您有大量的父项分配并且想要确保相关项不存在时(例如,给定用户之前没有给此回购加注星标或转推过此推文),该功能非常有用。

第二种选择是使用 DynamoDB流。 DynamoDB流允许您将转盘更新转换为事件流,从而可以异步处理表。如果要更新的项目数量很少,则可能要使用DynamoDB流来批量增加增量并减少对表的写入总数。

将ISO-8601格式用于时间戳

通过DynamoDB,您可以使用多种不同的属性类型,包括字符串,数字和映射。

我经常遇到的一个问题是代表时间戳的最佳方法。您应该使用纪元时间戳,它是代表自1970年1月1日以来经过的秒数的整数,还是应该使用人类可读的字符串?

在大多数情况下,我建议您使用 ISO-8601时间格式. This is a string-based representation of the time, such as 2020-04-06T20:18:29Z.

The benefits of the ISO-8601 format are two-fold. First, it is human-readable, which makes it easier to debug quickly in the AWS console. The ISO-8601 example above is much easier to parse than its corresponding epoch timestamp of 1586204309. Second, the ISO-8601 format is still sortable. If you're using a composite primary key, DynamoDB will sort 所有 the items within a single partition in order of their UTF-8 bytes. The ISO-8601 format is designed to be sortable when moving from left-to-right, meaning you get readability without sacrificing sorting.

在下面的示例中,我们存储了来自IoT设备的传感器读数。分区键是SensorId,排序键是ISO-8601时间戳以进行读取:

现在考虑到这一点,应该两次避免使用ISO-8601时间戳,而建议使用纪元时间戳。首先是如果您正在使用 DynamoDB生存时间(TTL) 自动过期表中的项目。 DynamoDB要求您的TTL属性为类型编号的纪元时间戳,以便TTL起作用。

其次,如果您实际计划对时间戳进行数学计算,则应使用纪元时间戳。例如,假设您有一个可跟踪用户帐户用完时间的属性。如果您在应用程序中有一种方法可以让用户购买更多时间,则可能需要运行更新操作来增加该属性。如果用户购买了另一个小时的游戏时间,则可以将时间增加3600秒。这样一来,您无需直接回读时间戳就可以直接在时间戳上进行操作。

使用按需定价开始

我的最后一个提示是与帐单相关。 DynamoDB为操作提供两种不同的计费模式:预配置和按需。使用预置容量,您可以预先声明要用于表的读取容量单位和写入容量单位的数量。如果您的表超出了这些限制,则可以在表上看到受限制的读取或写入。

通过按需定价,您无需预先配置容量。您只需为向DynamoDB发出的每个请求付费。这意味着没有容量规划,也没有节流(除非您迅速扩展!)。

现在, 正如我的同事瑞安(Ryan)指出的,您确实需要为按需定价支付溢价。按需定价的价格将是同等配置表的近7倍 如果 您将获得100%的利用率。

但是,不太可能获得100%的利用率。即使在高峰时间也是如此,但如果您随时间推移查看利用率,则更是如此。除非您很好地配置自动缩放,否则在低时间您可能会获得30%或更少的DynamoDB表利用率。

在许多情况下,按需定价实际上可以直接为您节省过多的价格。即使不是这样,按需定价也可能降低总拥有成本,因为您不再担心容量计划,维护自动扩展基础架构或在凌晨3点响应节流警报。

我同事的话 贾里德·肖特(Jared Short),在这里很有启发性:使用按需定价,直到感觉不痛为止。如果您的DynamoDB账单在AWS账单和工程工资单的总和中不占很大比例,请不要浪费时间对其进行微调。

DynamoDB迅速成为越来越多开发人员的首选数据库。它的扩展特性,灵活的计费模型和无服务器友好的语义使其成为云原生应用程序的流行选择。

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