暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Amazon DynamoDB的大型对象存储策略

原创 X丶 2022-10-10
417

所有行业的客户都使用Amazon DynamoDB作为关键任务工作负载的主要数据库。DynamoDB的设计目的是在任何规模下提供一致的性能。要利用这种性能,需要完成的最重要任务之一是数据建模。使用DynamoDB的成功与否取决于您如何定义和建模访问模式。数据建模的一个方面是如何在DynamoDB中处理大型对象。对于如何处理大于最大大小400 KB的项,必须有一个已定义的策略,以防止意外行为,并确保解决方案在扩展时保持性能。

在这篇文章中,我将向您展示在DynamoDB中处理大型对象的一些不同选项,以及每种方法的优缺点。我为每个选项提供了一些示例代码,以帮助您在自己的工作负载下开始使用这些方法。

在DynamoDB中,项是一组属性。每个属性都有一个名称和一个值。属性名和值都计入项目的总大小。在本文中,大对象指的是超过单个项当前最大大小(400 KB)的任何项。该项可以包含长字符串属性、二进制对象或DynamoDB支持的超过最大项大小的任何其他数据类型。

解决方案概述

这篇文章涵盖了多种方法,您可以使用它们在您的dynamodb支持的应用程序中为大型对象建模。您应该对DynamoDB有一定的了解。如果您刚刚开始学习,请务必先阅读《使用Amazon DynamoDB入门》。

您将探索的解决方案是:

选项1:默认行为

选项2:使用DynamoDB中的指针将大对象存储在Amazon Simple Storage Service (Amazon S3)中

选项3:将大型项目拆分为项目集合

选项4:压缩大型对象

部署示例

对于每种解决方案的示例,您可以部署本文附带的AWS无服务器应用程序模型(SAM)模板。您可以将这些示例作为您自己实现的参考。这些示例都是用Node.JS编写的,但是对于本文中使用的技术,在大多数流行的编程语言中都存在绑定。

要部署SAM模板,克隆这个GitHub存储库并遵循存储库中的说明。

选项1:默认行为

默认行为是拒绝超过最大项目大小的项目,这可能是一个完全有效的设计选择。在这种情况下,您将向调用方返回一条错误消息,指示项的大小太大,然后由调用方实现正确的行为,可以将项拆分为多个部分,将其发送到死信队列,或者返回指示项太大的异常。

在示例存储库中,可以通过运行模板部署的未编码写入AWS Lambda函数来观察这种行为。存储库包含一个超过最大项目大小的大型样例事件(超过最大项目大小的420 KB - 20 KB)。如果您使用这个有效负载调用函数,您将从DynamoDB得到一个ValidationException错误,并收到一条消息,表明项目已经超过了允许的最大大小,如下面的图1所示。
image.png

这种方法的好处是不需要实现定制的服务器端逻辑,因此成本和复杂性都很低。此外,出现此验证错误而失败的请求不会消耗表中的任何写容量单元(WCU)。缺点是这种方法可能不能满足用户的需求,所以建议使用本文中的其他策略。

选项2:使用DynamoDB中的指针将大对象存储在Amazon S3中

存储大对象的一种策略是使用替代存储平台并使用指向DynamoDB中的对象的指针。Amazon S3非常适合存储这些数据,因为它具有高持久性和低成本的特点。该实现将大对象写入S3桶,然后创建一个DynamoDB项,该项具有指向该对象的Amazon S3 URL的属性。然后,您可以使用该URL生成一个预先签名的URL以返回给调用者,这样做的额外好处是避免下载对象服务器端,从而节省计算资源。该体系结构如下面的图2所示。
image.png

这种模式在内容索引解决方案中很常见,其中内容本身可以是任意大小的,是半结构化的,并且存储在S3存储桶中。然后,DynamoDB形成一个索引,用于快速查找该数据存储在S3 bucket中的位置,详见构建和维护Amazon S3元数据索引。

在示例存储库中,创建了两个函数来演示这种模式:一个函数将项目写入S3 bucket和DynamoDB,另一个函数读取项目并生成预先签名的URL以返回给调用者。这使您可以安全地将对象下载到客户机,而不需要向服务器添加额外的负载。

如果您调用与前面使用的相同负载的写函数,您将看到这次写成功,如下面的图3所示:
image.png

然后,如果运行相应的read函数,就会得到一个预先签名的URL。这在接下来的图4中显示。
image.png

这个临时URL提供对存储在S3桶中的私有资源的访问,是授予对这些资源访问权的安全方式。如果您获取这个URL并将其粘贴到浏览器中,您将检索存储在S3 bucket中的对象,如下面的图5所示:
image.png

这种方法的一个优点是,您可以在Amazon S3中存储几乎任何大小的数据(每个对象最多可存储5 TB)。它还使你的DynamoDB成本更低,因为你消耗更少的存储空间和更少的读容量单元(RCU)和WCUs来读写项目,只存储URL在DynamoDB中。缺点是需要第二次调用来检索大对象,这增加了额外的延迟和解决额外调用的客户机复杂性。还有与S3的存储和检索相关的成本(更多信息,请参阅S3定价页面)。

选项3:将大型项目拆分为项目集合

另一种策略是将一个大型项拆分为具有相同分区键的项的集合。在这种模式中,分区键充当一个桶,将原始大对象的所有单独部分作为单独的项包含其中。

有多种方法可以将大型项目拆分为项目集合。如果对象中有自然划分—例如,如果它是一个具有许多属性的大型JSON对象—首选的方法是分割对象,以便将不同的属性集存储为不同的项。这意味着您可以选择性地只检索您需要的属性,从而减少I/O成本。例如,假设项目当前的结构如下表所示:

Partition Key Data
document-a {“name”: “John Doe”,“gender”: “female”,“company”:“AnyCompany”,“email”: “john@example.com”,“phone”: “+1 (846) 555-0100”,“notes”: "Lorem ipsum dolor … "}

如果这个条目是10kb,那么它需要1.5个RCU来完成一次最终一致的读。下表显示了如何将该项拆分为项集合:
image.png

使用这种结构可以检索任何单个属性,而不是每次最终一致的读取消耗0.5 RCU,潜在地减少了所需的表容量,从而降低了成本。还可以通过分区键检索整个项目集合。

然而,如果需要频繁检索或更新多个项,这种结构的成本可能很高,如果有许多单独的属性,则初始写成本可能很高。优化就是根据属性更新的频率将它们分组在一起。例如,如果描述一个人的所有属性都是相当静态的,但是notes属性经常变化,您可以像下表那样组织项目集合:
image.png

使用这种方法,所有不太频繁更改的属性都可以通过单个请求和0.5 RCU进行检索,但也可以通过单个WCU进行更新,因为它们累积在1kb以下。如果原始项目有数百甚至数千个属性,您可以创建多个这样的属性组,以帮助最小化所需的WCU。

如果notes字段仍然可能超过最大项大小,或者没有明确的方法来拆分对象(比如当对象是二进制对象时),另一种选择是将对象拆分为必要的尽可能多的部分,以便每个部分足够小,可以装入DynamoDB项中。然后在检索时将这些部分重新连接在一起,以透明地将其作为一个对象返回给调用者。

回到储存库示例,用于拆分项的write函数接受输入字符串,并将其拆分为所需的尽可能多的部分,以使每个部分适合DynamoDB中的最大项大小。然后,该函数用相同的分区键创建一个单独的项。该项由递增的部件号作为排序键唯一标识。排序键需要提供惟一性,有关详细信息,请参见使用排序键组织数据。

在本例中,为了简单起见,每个项目在循环中单独发送。您可以单独构造项目集合,并使用BatchWriteItem API操作在单个操作中将其写入DynamoDB,或者如果您希望所有项目都原子地成功或失败,则可以使用TransactWriteItem API操作。需要注意的是,这两个API操作每个请求都限制为25个条目(BatchWriteItem的聚合大小为16mb, TransactWriteItem的聚合大小为4mb),因此如果条目集合可能超过这些限制,您仍然需要将迭代器逻辑合并到代码中。

如下面的图6所示,运行此函数的结果显示了集合中具有相同分区键的两个项:
image.png

要读取对象,你必须重新组合各个部分。检索非常简单,因为DynamoDB允许您使用复合键的分区部分来检索带有该分区键的所有项。通过使用Query API操作检索结果,可以确保根据表的数字排序键按顺序处理各个部分。然后将这些部分重新连接到服务器端,将实现细节从调用方抽象出来。

此模式还允许预览项。返回项目集合中的第一个元素(在本例中是0部分),作为预览,可以在UI中选择一个按钮查看更多内容,然后返回整个字符串。这就减少了op,同时也减少了默认情况下只加载数据的预览部分以及在请求时才加载其余部分所需的成本。

这种方法的缺点是,在服务器端拆分和重组部分的复杂性很高,因此必须对这些功能进行充分测试,以避免数据丢失。另一个需要考虑的问题是返回组成原始对象的所有项所需的额外rcu。对于较大的对象,请考虑使用备选数据存储,如选项2中所述,或与选项4结合使用,以减少每个项目所需的容量。

选项4:压缩大对象

在最后一节中,您将探索如何压缩大型对象。您将了解可用于执行压缩的两种压缩算法以及每种算法的优缺点。

首先,让我们看看Node.JS标准库中内置的原生zlib压缩。在示例存储库中有一个zlib文件夹。在这个文件夹中有两个Lambda函数,一个用于写入数据,另一个用于读取数据。

使用与前面相同的有效负载,首先调用write函数,该函数使用gzip函数压缩项。当函数完成时,您会得到一条成功消息,如下面的图7所示。
image.png

然后,read函数将此字符串解压缩为当read函数完成时返回的原始详细有效负载,如下面的图8所示。
image.png

接下来,尝试快速压缩。同样,在这个时髦的文件夹下有两个函数——一个用于写入,一个用于读取。

调用write函数使用snappy node.js绑定来压缩大字符串并将结果较小的字符串写入DynamoDB:
image.png

然后read函数将字符串解压缩回原始:
image.png

如您在下表中所见,snappy的速度更快——在我尝试的100次执行中,snappy的平均每次调用为180毫秒,而zlib为440毫秒,但是它的压缩比相对较弱,只有50% (208 KB),而zlib为66% (139 KB)。这对于我们的用例来说很好,对于您的用例可能也是如此。请记住,在像这样的无服务器环境中,您是按毫秒计费的,因此如果执行速度是主要考虑的问题,那么snappy可能是一个不错的选择。Zlib虽然提供较慢的压缩,但它是一个本机实现。它不需要安装任何额外的包,因此,如果您正在禁止第三方软件的环境中工作,那么这就提供了一种快速而简单的解决方案,可能足以满足您的用例。这些差异如何影响你的应用程序成本的比较如下表所示:
image.png

基于在eu-west-1区域一个月内每秒执行100次512 MB Lambda函数

还有其他可以使用的压缩算法库,例如lzo和zstandard,它们提供了不同的性能特征。请记住,您在自己的项目中采用的不同算法库可能需要许可证。

压缩策略的一个缺点是,无论使用哪种算法,它都会增加计算时间的开销。这种方法也只适用于可以压缩数据以适应项目大小限制的情况,而您可能事先不知道项目大小限制。如果数据的大小是可预测的,或者在压缩比内有一个定义的上限,那么它可能适合作为一个独立的解决方案。对于超出条目限制数量级的数据,不太可能压缩到足以容纳它的程度。如果是这种情况,那么将压缩与前面讨论的项目分割解决方案结合使用是一个可行的选择。压缩的优点是简单,实现对调用者是透明的,减少了重构项或使用附加AWS服务来补充存储的需要。

清理

本文中描述的Lambda函数调用和DynamoDB使用应该属于Amazon Web Services (AWS)的这些服务的免费层,因此在超出免费使用层之前,您不应该为这些服务支付任何费用。之后,可以在DynamoDB定价页面和Lambda定价页面上找到这两个服务的定价细节。

如果部署了示例SAM模板,那么从CloudFormation控制台中删除AWS CloudFormation堆栈。

写在最后

根据您的工作负载的访问模式和特征,这些方法中的任何一种都可能适合。下面是一些最终的考虑,可以帮助您为您的工作负载选择正确的策略。

在Amazon S3中存储大对象

通过将工作负载分布到多个数据存储,您将失去事务一致性。AWS步骤函数可用于在分布式系统中建立一致性,例如通过使用saga模式。

如果您正在使用DynamoDB的全局表特性进行多region复制,请考虑将S3桶复制到其他region,特别是在灾难恢复场景中。这可以通过S3跨区域复制特性实现。

将大型项目拆分为项目集合

如果使用全局或本地二级索引作为DynamoDB表的一部分,请考虑是否需要将大属性投影到二级索引。使用基表查找大型对象部分,使用辅助索引查找其他访问模式,而不预测此属性,可以节省存储成本和将这些更改传播到索引所需的写容量。

在DynamoDB中的查询和扫描最多可以检索1 MB的数据。如果总结果大小大于此值,则必须进行多个分页调用或使用BatchGetItem API。

压缩

如果您需要为查询执行筛选表达式,那么使用压缩字符串就不可能实现这一点,因为DynamoDB不能本机解压对象。

结论

对于许多DynamoDB用例,您必须考虑如何处理大型对象。预先做出有意识的设计决策可以理解系统的限制和访问模式,并随着数据规模的增长提供可伸缩的性能解决方案。要了解采用DynamoDB时的更多设计考虑事项,请参阅如何确定Amazon DynamoDB是否适合您的需求。有关更多数据建模技巧,请参见使用NoSQL Workbench For Amazon DynamoDB进行数据建模。

原文标题:Large object storage strategies for Amazon DynamoDB
原文作者:Josh Hart
原文地址:https://aws.amazon.com/cn/blogs/database/large-object-storage-strategies-for-amazon-dynamodb/

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论