服务
关于
CloudProse博客
聚光灯

从关系数据库到单个DynamoDB表:分步探索

只是因为's NoSQL, doesn't mean it's non-relational
福雷斯特Brazeal 迷航10 191210 171202
阿甘(Forrest Brazeal) | 2019年1月2日

在我从AWS re:Invent 2018观看的所有会议中,我最喜欢的当然是 NoSQL专业知识的这种令人困惑的下降 来自AWS首席技术专家和认证的外太空向导Rick Houlihan。

认真地看那个视频,然后回到本文。您不会失望的。

Rick打破了蠕虫的盖子,我们设计DynamoDB表的许多人都试图避免这种蠕虫:DynamoDB不仅仅是简单项目查找的键值存储这一事实。如果设计正确,则单个DynamoDB表可以处理合法的多表关系数据库的访问模式,而不会费力。

当然,这句话“设计合理”是警告。里克的视频,以及 相关文件 我怀疑他有经验,对如何构造一个DynamoDB表的建议充满了建议,该表将在任意水平范围内匹配关系数据库的查询性能。

不过不要撒谎,这是沉重的负担,特别是对我们未经认证的外太空巫师来说。

因此,在这篇文章中,我想逐步详细研究一些DynamoDB单表设计注意事项。我们不会介绍所有可能的设计模式,但希望您会开始对可能的用例和不可避免的权衡取舍。我们将得出一个最终的问题:当关系数据库仍然像这样时,这是一个好主意吗? 就在那儿?

从RDB到DynamoDB:一个实际示例

合理的警告:我潜入深处! 如果您只是想跳到高层次的结论, 走捷径!

那么,我们应该动态化什么关系数据库?我决定使用我能想到的大多数SQL-y示例: 北风,这是用于在90年代教授Microsoft Access产品的经典关系数据库。

这是完整的罗斯文ERD。它虽然并不庞大,但至少与您可能希望使用DynamoDB支持的许多现代微服务的数据需求一样复杂。

瞧,罗斯文模式的样本数据是 以清理后的CSV格式提供 在Github上。我们将忽略几个辅助表来关注“八大”:类别,客户,员工,订单/订单明细,产品,托运人和供应商。

我已经包含了创建DynamoDB表和加载数据所需的所有代码,如本文中的全文所示。 这个Github回购。随时检查并继续玩吧!

一步步

现在,我们如何将ERD和CSV表转换为DynamoDB表?

第1步:定义您认为需要的访问方式

立即,我们遇到了DynamoDB和关系数据库之间的巨大差异:我们的数据模型将完全实用,而不是理论上自洽的。我们将根据需要处理的数据来专门制作表格,就像在屋顶上喷洒绝缘泡沫一样。

在现实世界中,我们会从应用程序团队,潜在用户等那里收集这些要求。但是,这并不是真正的用例,因此,我们必须通过查看ERD来发明一些访问模式。以下是我提出的一些任意查询要求:

  1. 通过员工编号获取员工
  2. 获取员工的直接报告
  3. 获取停产的产品
  4. 列出给定产品的所有订单
  5. 获取最近的25个订单
  6. 按名称获取托运人
  7. 通过联系人姓名获取客户
  8. 列出订单中包含的所有产品
  9. 按国家和地区获取供应商

所有这些都是简单的SQL查询,最多包含两个联接。 (我们将为以后的帖子保存写模式。)但是请记住,我们在DynamoDB中没有JOIN或GROUP BY。取而代之的是,我们必须以一种在表中“预先加入”数据的方式来构造数据。

步骤2:使用三个通用属性创建DynamoDB表:“分区键”,“排序键”和“数据”

这使我们进入了DynamoDB单表设计中最重要的规则之一:

属性名称与属性值没有关系。

我们的“键值存储”不仅架构少,而且在某种程度上,它也是无钥匙的。我们需要习惯于将DynamoDB项目上的属性名称视为任意名称。我们表上的“分区键”属性可能包含不同类型的值,具体取决于是订单,产品,员工还是其他:

我知道,将不同类型的数据存储在相同的属性中感觉很怪异和怪异。但是它实际上超级强大。这种技术称为 索引超载,这将使我们能够将大量访问模式压缩为非常少量的索引。

The three generic attributes will be used to support two indexes: the main table index which uses pk as the partition 和 sk as the sort key, 和 a global secondary index which uses sk as the partition 和 data as the sort.

无论如何,索引有什么大不了的? 通常,如果将自己限制为“获取”(对单个项目的键/值查找)和“查询”(对具有相同分区键但范围/排序键不同的项目进行条件查询),则DynamoDB的成本和性能将是最佳的)。扫描是您缓慢而昂贵的反模式,您可以在其中肆意地吞噬表格中的所有项目。有用的获取和查询要求... 有用的索引。所以我们到了。

我们将看到,这两个索引将打开大量的访问模式。表中的其他属性可以根据需要命名。它们在项目之间不必保持一致。但是,即使为每个项目的每个属性都指定了一个随机名称,它也完全不会影响表格的行为。 (这会使人难以阅读和理解表格的布局……我们将在下面进一步讨论。)

步骤3:在DynamoDB表中为每个实体(非联接)表中的每个记录创建一个项目

Each Customer, each Order, each Shipper record gets an item in our new table. In each of our cases, we’ll make the pk attribute correspond to the primary key of the relational record. The skdata attributes, though, we’ll vary based on the kinds of queries we need to write. See the breakdown below:

现在,我们省略了“ OrderDetails”联接表;下一步将获得特殊待遇。

让我们在这里注意一些技巧:

  • The Order, Product, 和 Supplier records use a static value as the partition key for GS1. This lets us look up 所有 items of a particular type (such as 所有 orders that match a date range) without resorting to an expensive scan operation. You can think of this as a workaround for the loss of our precious attribute keys: we’re using a value as a key instead.
  • 我们使用了一个称为 层次排序键 as the data field for the Customer 和 Supplier records. By combining 所有 the address details into one field, we can get country, region 和 city lookups for the price of a single GSI.
  • 我们已将“停产”值用作产品项GSI上的排序键。假设我们只填充停产产品的价值(原始的Northwind数据中不是这样),我们可以搜索停产产品而不必扫描整个“ PRODUCT”分区。这种技术称为 稀疏指数.

目前,我们基本上是在用数据玩俄罗斯方块,将不同的值滑入和滑出我们有限的GSI插槽,以获得最大的实用性。而且我们还没有完成,因为我们仍然必须...

为什么我们如此着迷于最小化全局二级索引? 在此表上添加大量索引会不会更容易?长期以来,答案是否定的。 DynamoDB表的硬限制为5个GSI。 DynamoDB最近将该限制提高到 软20,这意味着表上可能有未定义数量的GSI。

但是,许多GSI使写入的几何结构变得更加昂贵,每次更新项目时都会消耗额外的容量单位。因此,如果我们可以将查询压缩到尽可能小的索引范围,那么我们将在成本和性能上取胜。

步骤4:使用邻接表表示多对多关系

DynamoDB最佳实践借鉴了图论的概念 邻接表,这是个有点滑的概念。暂时搁置图形概念,您可以想到我们放在表中的所有项目,直到“节点”记录为止。它们对应于实体,例如客户和订单。现在,我们将创建一些额外的“边缘”记录,这些记录代表节点之间的多对多关系。

In the 北风 dataset, the many-to-many relationship we’ll focus on is expressed in the OrderDetails join table. An order can have many products, one product can appear in many orders, 和 the attributes of that relationship are expressed in OrderDetails. We’ll model this relationship by placing the OrderDetails records in the Order partition of our table.

为什么我们将所有这些东西又放到一张桌子上? DynamoDB文档 着重推荐 使用尽可能少的表,通常每个应用程序/服务使用一个表,除非您的访问模式差异很大。将您的相关数据放在一起可以为您带来Dynamo的性能和扩展优势,而不会因通过HTTP查询多个表并尝试在客户端“联接”而造成的延迟和沮丧。

就是说,我看到许多关系数据库应拆分为单独的DynamoDB表,因为同一数据库被用作各种无关数据的转储场。 Postgres数据库中的70 GB访问日志表不需要与产品和订单数据一起放在同一DynamoDB表中。

这对我们有什么帮助?现在,我们可以查询主表分区以按顺序获取所有产品。我们可以查询GS1 PK以对给定产品的所有订单进行反向查找。这是邻接列表模式。您可以使用罗斯文(Northwind)数据中的“ EmployeeTerritories”联接表自行进行尝试,此处未提供。如果您进一步进行访问,则可能需要将此访问模式分解为自己的GSI。

步骤5(可选):创建更多GSI以支持其他访问模式

信不信由你,即使有了我们在步骤2中使用的所有技巧,一个GSI可能也不足以支持所有可能的查询! (令人震惊的是,我知道。)好消息是,如果需要,您可以添加其他GSI,而不会完全破坏您精心组装的Tetris主板。 DynamoDB文档具有 一个很好的例子 添加具有特殊构造的分区和排序键的第二个GSI来处理某些类型的范围查询。

不过,在我们的例子中,主表分区加上一个GSI足以处理我们在步骤1中定义的所有用例。让我们分解一下查询:

分片呢? 我们一直在考虑如何简化单表查询,但不一定要考虑如何使其快速进行。即使有了DynamoDB的新功能 适应能力 功能上,您希望保持访问模式的平滑,以免单个分区上的负载不成比例。这通常涉及使用随机密钥创建索引。亚历克斯·德布里(Alex DeBrie)有 奇妙的故障 在他的DynamoDB指南中,了解它如何工作以及何时需要它。 (特别是对于具有静态分区键(例如“ ORDER”)的GSI来说,分片非常重要-现在将很多记录打包到一个分区中。)

你可以看到 所有这些查询的工作示例 在随附的存储库中使用AWS Python SDK。另外,我们保留了表中每个实体的单独键值查找,因此我们离DynamoDB的根源并没有太远。

什么 不能 我们的确是?

现在,我们有了一个基本蓝图,可以将关系数据库转换为单个DynamoDB表。但是请记住,这是一种喷雾方法来处理数据。就像绝缘材料在天花板的轮廓上变硬一样,我们的DynamoDB单表数据模型 非正式的死板的。不一定要适应新的访问模式。

例如,假设我们需要查看给定类别中的所有产品。 “产品”记录具有CategoryID,但目前未包含在我们的任何索引中。我们的选择是:

  1. 查询所有产品,按类别ID筛选(不是最佳查询),或
  2. 在我们现有的分区中划分一个新类别,以按类别ID为产品数据建立索引(创建更多重复数据,这可能更难管理),或者
  3. 创建一个新的GSI,其中产品ID为分区,类别ID为排序键(增加表成本)

如您所见,权衡比比皆是!只有您才能决定哪个选项最适合您的应用程序的长期健康状况以及开发人员的健康状况。添加的具有通用属性的GSI越多,在没有大量支持文档的情况下,阅读和理解此表的难度就越大。

实际上,一个经过优化的单表DynamoDB布局看起来更像是机器代码,而不是简单的电子表格-尽管创建它花了所有的定制,人工操作。

这就引出了最重要的问题:

在单个DynamoDB表中为关系数据库建模真的是一个好主意吗?

大约一年前,我写了一篇颇受欢迎的文章,名为 “为什么DynamoDB不适合所有人”。自那时以来,我提出的许多DynamoDB技术批评(缺少备份/还原等操作控制;热键持续存在的问题)由于真正令人敬畏的功能发布而被部分或完全解决了。 DynamoDB团队。

但是,该文章的中心论点仍然有效:正确使用DynamoDB是一个功能强大的工具,但是,如果您不知道自己在做什么,那将是疯狂的欺骗性用户友好指南。而且,您越深入关系模型等深奥的应用程序,就越能确定自己知道自己要进入的领域,就越能确保自己。尤其是像Amazon Aurora这样对SQL友好的“无服务器”数据库如雨后春笋般地发展时,您将拥有许多完全托管的选项,学习曲线更短。

也就是说,请记住,亚马逊最初的Dynamo纸是 根据观察结果 与它们庞大的Oracle数据库的大多数交互都是简单的键值读取,不需要JOIN或其他关系魔术。

同样,许多表面关系数据集可以归结为相对较少的使用模式。如果您可以按照本文中的步骤进行操作以识别并实现数据的这些模式,则DynamoDB的规模,性能和较低的运营开销似乎比以往任何时候都更具吸引力。

除非您知道,否则您还是Microsoft Access的忠实拥护者。

感谢Alex DeBrie,Jared Short和Andy Warzon在本文中提供了技术评论。

需要DynamoDB专业知识吗? 迷航10一直在那里,做到了。如果我们可以为您提供帮助,请随时 让我们知道.

作者
福雷斯特Brazeal 迷航10 191210 171202
阿甘(Forrest Brazeal)