比较Fauna和DynamoDB:体系结构和定价

2020-12-11 07:58:18

<-Back Fauna和DynamoDB都是无服务器数据库,但是它们的设计目标,体系结构和用例却大不相同。在这篇文章中,我将概述两种系统,讨论它们在什么地方发光以及在哪些地方不发光,并解释各种工程和产品决策如何为数据库用户创造根本不同的价值主张。

AWS DynamoDB是为响应Apache Cassandra的成功而开发的。 Cassandra数据库最初是在2008年由Facebook开源并放弃的。我在Twitter上的团队与Rackspace团队(后来成为DataStax)一起为该数据库做出了巨大贡献。

但是,在一段奇怪的历史变迁中,Cassandra本身受到了亚马逊2007年的一篇论文的启发,该论文涉及一个名为Dynamo的内部数据库,后者是最终一致的键值存储,用于高可用性购物车存储。亚马逊很早就开始关注购物车,而他们还没有开展Web服务业务。在Amazon内部,Dynamo论文以及DynamoDB的根源早于向外部客户提供数据库产品的任何概念。

DynamoDB和Cassandra都专注于两件事:高可用性和低延迟。为了实现这一目标,他们的初始版本牺牲了PostgreSQL等传统操作数据库甚至MongoDB可能带来的其他价值:事务性,模式,数据库规范化或文档建模,索引,外键,甚至是查询计划程序本身的想法。 DynamoDB通过使可序列化的单键写入可序列化并删除了巴洛克式的CRDT协调方案,确实在原始Dynamo架构上进行了改进,而在Cassandra上通过具有更人性化的API进行了改进。

DynamoDB的体系结构本质上是将Web服务器置于B树分区(认为是BDB数据库)的集合的前面,在这些B树分区中,文档始终被哈希化。文档是纵栏式的,但是没有架构。

在DynamoDB区域内,每个数据分区被复制3次。通过要求在写入时进行同步多数提交来保证持久性。一致性仅在单个分区内强制实施,实际上,这意味着单个文档,因为无法直接管理分区边界。写入始终首先通过领导者副本进行;读取可以来自最终一致模式下的任何副本,也可以来自强一致模式下的前导副本。

尽管DynamoDB最近添加了一些新功能,例如二级索引和多键事务,但是它们的局限性反映了DynamoDB的铁律:“一切都是表格”:

复制到其他区域是通过创建其他表来实现的,这些表异步地应用每个副本基于行的更改日志中的更改。

辅助索引是通过将数据异步投影到其他表中来实现的-它们不可序列化且不可事务处理。

事务性是通过多阶段锁定实现的-大概是DynamoDB保留了一个隐藏的锁定表,这直接反映在事务性的额外成本中。 DynamoDB事务不是ACID(它们不是完全隔离的或可序列化的),并且不能有效地替代关系事务。事务状态对副本甚至同一副本内的二级索引都不可见。

正如您可能从上面预测的那样,DynamoDB文献中绝对包含使用积极的NoSQL风格的非规范化的“单表设计”示例。通常不建议使用更复杂的功能。有道理的是,即使在复制和建立索引的方案中,单个查询必须与多个表进行交互(通常是多次),DynamoDB的定价也围绕单表,最终一致的用法进行设计。

其他挑战在于查询模型本身。与Fauna的查询语言FQL或SQL不同,DynamoDB的API不支持从属读取或查询内计算。 Fauna做到了,允许开发人员将复杂的业务逻辑封装在事务中,而不会造成任何一致性,延迟或可用性损失。

DynamoDB最适合最初设计的用例,即可以手动组织数据以匹配一组受限的预定查询模式的场景;来自单个区域的低延迟就足够了;并且多文档更新是例外,而不是规则。例如,锁存储,作为RDBMS等不同的,可伸缩性较小的数据库的持久缓存,或作为原始购物车用例的临时数据。

另一方面,Fauna的灵感来自我们在Twitter上的经验,该经验提供了全球实时的消费者互联网服务和API。我们的团队广泛使用MySQL,并为MySQL,Cassandra,Memcache,Redis和许多其他流行的数据系统做出了贡献。我们希望帮助人们快速开发功能并随时间轻松扩展功能,而不是专注于帮助人们优化已经大规模的工作负载。

我们希望使任何开发团队都可以在从小型到大型的整个过程中迭代其应用程序,而不必成为数据库专家,而将他们的时间花在缓存,非规范化,复制,体系结构重写以及其他一切不利于构建数据库的事情上。成功的软件产品。

为了实现此目标,Fauna使用独特的体系结构,即使使用全局复制,该体系结构也可确保所有副本和索引之间的低延迟和事务一致性,并提供一种独特的查询语言,该语言保留了关键关系概念,如ACID事务,外键,唯一约束和存储程序,同时还支持现代非关系概念,例如面向文档的建模,声明性过程索引和基于标准的GraphQL API。

数据副本以确定的顺序应用日志中的事务处理语句,从而确保ACID属性而无需其他协调。

通过跳过日志,只读事务可实现比写入更低的延迟,但还有其他技巧,可以保持完全一致。

与DynamoDB不同,Fauna在SQL RDBMS的相同领域中发挥着重要作用:对混乱的现实世界交互模式进行建模,这些交互模式开始时很简单,但是必须随着时间的推移而发展和扩展。与SQL不同,Fauna的API和安全模型是为现代移动,浏览器,边缘和无服务器应用程序设计的。

与DynamoDB一样,与RDBMS不同,Fauna透明地管理操作问题,例如复制,数据一致性和高可用性。但是,与DynamoDB的主要区别在于可伸缩性模型。 DynamoDB通过根据观察到的吞吐量和存储容量来预测性地拆分和合并分区来进行扩展。根据定义,这对于可预测的工作负载效果很好,而对于不可预测的工作负载效果则不太好,因为自动缩放更改需要时间。

另一方面,FaunaDB可动态扩展。作为API,包括计算和存储在内的所有资源随时都可能对所有用户可用。与操作系统多线程类似,Fauna在服务的所有用户之间持续调度,运行和暂停查询。对资源消耗进行跟踪和计费,并且我们的团队总体上扩展了每个区域的容量,而不是基于每个用户。

自然,这种设计的成本结构与DynamoDB之类的成本结构不同。例如,无法创建非复制的Fauna数据库或禁用事务。与DynamoDB一样,Fauna的计费定价根据您的工作负载实际消耗的资源而定。但是与DynamoDB不同,您不需为每个低级的读写操作,每个副本,每个索引付费,因为我们的基本情况是DynamoDB的异常情况:具有事务性多区域访问模式的规范化索引数据模型。

存在更高级别的抽象以提供更高级别的生产力。 Fauna提供的抽象级别比DynamoDB高得多,并且我们的定价也反映出这一点-默认情况下,它包含DynamoDB不提供的所有内容。在Fauna,我们希望为数据库提供尽可能高的抽象级别,以便您完全不必担心任何低级别的问题。

除了DynamoDB和Fauna之外,几乎所有其他数据库都作为托管的云基础结构交付,并按预配置计费,直接反映了供应商的成本以及这些成本。无服务器基础设施是相对较新的-S3可能是第一种具有无服务器计费模型的服务,可以被广泛采用-无服务器数据库甚至更新。 DynamoDB中的无服务器模型是一种改进。从本质上讲,它仍然是预配置的系统,并具有预测性自动缩放功能。

取而代之的是,迄今为止,无服务器性主要限于垂直集成的单用途API。这些API已通过Twitter间接获利,如Twilio那样按操作收费,或按第三方(如Stripe)之间通过API交换的价值的百分比计费。

众所周知,无服务器基础架构实际上是由服务器组成的。与垂直集成的API相比,它具有更复杂的记帐挑战,并且受以下因素的约束:

无服务器基础架构的多租户可从根本上改善客户体验。谁愿意为不使用的容量付费?谁想要由于他们没有提前购买足够的容量而导致其应用程序降级?这也是一种更好的供应商体验,因为没有供应商愿意浪费基础设施,而且可以更加环保。

但是,供应商在所有客户之间的总价必须涵盖静态基础结构成本,这些成本紧密耦合并且可以抵御变化。 (实际上,即使使用托管云服务,供应商也不能独立于需求按需升级和降级CPU,内存,磁盘和网络。)总价还必须与公认的业务价值相关联,并且必须根据每个客户随时间实现的价值进行适当分配。

与仅标记服务器的增量成本相比,此定价问题很难解决。让我们讨论DynamoDB和Fauna找到的解决方案。

经过仔细的分析和测试,我们相信以下公式正确总结了DynamoDB的定价,尤其是在美国大部分AWS地区的按需计费:

$ 0.25 *((最终一致读+索引读)* 0.5 +一致读+事务读* 2)

$ 1.25 *(一致写入+事务性写入* 2 +(一致写入+事务性写入)*索引计数)*复制数

读取操作假定数据大小为4K或更小;每增加4K,就会增加一次操作。写操作假定数据大小为1K或更小。值得注意的是,索引写入被视为完全独立的写入操作;它们不包含在文档的1K中。

通常,读取成本按文档数量乘以一致性级别来衡量。写入成本按文档数乘以索引数,再乘以副本数再加上一致性级别来衡量。

在DynamoDB设计的用例中,这种定价清晰明了。但是,如果您添加了诸如全局复制,索引和事务之类的新功能,定价将变得更加不透明,并且很难预先预测成本。

读取操作假定数据大小为4K或更小;每增加4K,就会增加一次操作。写操作假定数据大小为1K或更小。与DynamoDB不同,索引写入仅按大小收费,而不同时按索引大小和数量收费。如果文档写操作及其索引在1K的范围内,则索引不会收取额外费用。由于索引数据通常很小,因此只需几次写操作就可以更新许多索引,从而大大降低了成本。最后,复制没有单独的费用。在动物区系中,数据就是数据。

DynamoDB不支持查询中的任何类型的计算,但Fauna支持。因此,Fauna分别收取计算成本。在DynamoDB中,由于根本无法在数据库中完成任何特定工作负载的计算,因此必须在应用程序端在AWS Lambda之类的计算环境中完成,这需要自己付费。

替代Fauna计算的可能计算堆栈的效率差异非常大(AWS Lambda中的Ruby与EC2上的C ++?),无法进行任何常规比较。尽管如此,与数据本身位于同一位置的依赖于数据的计算通常是一件好事,所以Fauna在大多数实际场景中都表现良好。

但是,请注意,每个API调用至少要花费一次计算操作。在Fauna中,最好编写复杂的查询,以在单个请求中完成尽可能多的工作,而不是将其视为键值存储。这样可以最小化成本,最大化性能并保证交易的正确性。

因为价格更高,所以动物区系的定价要简单得多。当然,不利的一面是,如果您不想要更高级别的功能,则它可能比DynamoDB昂贵。您可以在无服务器环境中获得所需的费用!

让我们以最基本的示例开始。想象一下1998年以来某个网页上的点击量计数器。它不一定是最新的,它不必很快,但它必须随着时间而增加。我们将以默认的一致性级别和默认的复制配置对单个文档进行100万次读取和100万次写入。

在Dynamo中,这使我们在一个区域中实现了最终一致的读取和一致的写入。由于默认为读取,客户甚至无法读取自己的写入内容:有人可以刷新我们的网页,看到数量保持不变,这没有任何意义。但是他们可能不会注意到,毕竟,我们不想花很多钱来计算点击数。

在动物区系,我们得到的超出了我们的要求。我们获得全局复制,从而为客户端提供更高的可用性和更低的延迟。我们还可以使键之间保持一致,但是由于访问者不习惯于实时比较跨页计数,因此没人会知道其中的区别。但是,客户总是会读自己的文章,这很不错:刷新页面总是会使点击量增加。

对于Fauna来说,这似乎有点高,但是如果我们将每个请求以50个为一组批处理操作,则将分摊计算成本:

($ 0.50 + $ 2.25 / 50)* 1 +($ 2.50 + $ 2.25 / 50)* 1

现在,让我们做些有用的事情,例如原始的Dynamo用例,全球复制的购物车。与本文中描述的实现不同,我们不会将整个购物车存储为单个记录,也不会依赖复杂的CRDT方案来合并有冲突的购物车。我们现在拥有更好的技术,我们可以以更自然,更相关的方式将购物车建模为商品的集合。

为了确保用户始终可以快速访问他们的购物车,我们将为DynamoDB配置两个附加副本。我们还将在DynamoDB和Fauna中都添加三个索引,以便我们可以用三种方式对项目进行排序,而不管有多少项目。

而且,我们将对DynamoDB使用一致的读取,这样客户就不会在默认视图中看到缺失的物品。但是,DynamoDB索引永远不会保持一致。因此,对于DynamoDB,我们需要在通过索引读取项目后,通过它们的主键对项目进行重复一致的读取,以验证它们是否仍在购物车中。

对于一百万个购物车,我们在其中添加了一个商品然后查看了购物车,我们的成本是:

$ 0.25 *(1 * 0.5 +1)+ $ 1.25 *(1 +1 * 3)* 3

因为我们的索引值很小并且适合1K写操作限制,所以没有单独收取索引费用。我们也不需要为多区域复制支付额外的费用。

由于计算成本摊销,每个请求添加和查看多个项目将使Fauna的模型受益更多。让我们添加5个项目并在每个请求中查看结果:

$ 0.25 *(5 * 0.5 + 5)+ $ 1.25 *(5 + 5 * 3)* 3

最后,让我们想象一下,我们有一些类似典型的SaaS应用程序的产品,例如CRM。我们有一个帐户表,其中为所有可能的排序字段(DynamoDB的最大值,Fauna没有限制)定义了20个二级索引。我们还有一个包含10个索引的活动表和一个包含5个索引的用户表。仅查看默认帐户屏幕将查询7个索引和25个文档。一个典型的活动更新通过10个依赖项检查一次事务性地更新3个文档,并修改所有35个索引。

当然,我们已经将该数据全局复制到了DynamoDB中的另外两个区域。我们还将对从索引返回的所有数据进行一致性检查。

在动物区系中,我们不需要配置复制或进行任何其他的一致性检查。而且,我们也从Fauna的索引写入合并中受益匪浅。

考虑到这一点,一百万个帐户的屏幕浏览量和一百万个活动更新将使我们花费:

$ 0.25 *(7 * 0.5 + 7 + 25 + 10)+ $ 1.25 *(3 * 2 + 3 * 35)* 3

($ 0.50 *(7 + 25)+ $ 2.25)+($ 0.50 * 10 + $ 2.50 * 3 + $ 2.25)

即使我们假设Fauna由于所有索引都需要对每个文档执行多次写操作,结果也不会发生实质性变化。通过使用唯一约束而不是依赖项读取,还可以改善Fauna的查询模式,这将进一步降低成本。

我们看到的一个误解是DynamoDB在规模上很便宜,而Fauna则很昂贵,或者DynamoDB很快而Fauna却很慢。这是不正确的,尽管可以理解,因为许多人都评估了一个简单的用例并继续前进。

由于DynamoDB是为简单的用例而不是复杂的用例设计的,因此它总是赢得这种表面的比较。除了定价模型外,DynamoDB还存在其他许多痛点,例如手动分区和最终一致的DDL,这反映了其作为低级系统的根源。

这种情况与MongoDB不安全的提交丑闻相似,但没有那么恶性,后者的写出速度快于物理定律,因为MongoDB客户端在实际写之前就承认了它们。确实,不做某事总是比做某事更快,更便宜—但是,如果客户确实需要做某件事,他们必须承担实现负担,以断断续续的临时方式支持数据库级关注点他们的应用。

例如,如果为Dynamo中的全局部署配置了十几个索引,则与单个未索引区域的表相比,您的写入和存储成本将增加一个数量级。使它们成为事务性写入或开始进行从属读取,它们的上升速度甚至更大。另一方面,如果您尝试将Fauna大规模用作与应用程序相邻的持久性高速缓存,则可能会发现您为实际上不需要的数据复制和事务一致性支付了费用。

准确地说,DynamoDB和Fauna在用于正确目的时既快速又便宜,而在错误使用时则很昂贵。这似乎是一条通用规则,但实际上并非如此。即使是在托管云中,大多数数据库对于间歇性或可变工作负载(现实世界中的工作负载始终如此)而言,其成本却过高。这是无服务器模型的好处:对客户和供应商的浪费都减少了一个数量级。

在Fauna,我们认识到DynamoDB推动了分布式云数据库的发展,对此我们深表感谢。我们肩负着更大的使命。同时,我们知道作为关键行业,面向文档或基于SQL的任务关键型工作负载,作为一个行业,我们可以比所有现有数据库做得更好。

希望本文能使您对DynamoDB和Fauna的动机,架构和业务价值有更清晰的了解。而且,这种理解可以帮助您做出更明智的决策

......