YugabyteDB 2.0版本的核心功能之一是与PostgreSQL兼容的YugabyteDB SQL(YSQL)API。在这篇文章中,我们将从性能和可扩展性两个方面,比较YSQL与其他两个兼容PostgreSQL的分布式SQL数据库——Amazon Aurora PostgreSQL和CockroachDB。
SQL基准测试表明,YSQL的可扩展性是Amazon Aurora能达到的最大吞吐量的10倍。此外,对于类似的硬件配置,YSQL和Amazon Aurora相比,吞吐量提高了近2倍,延迟却只有后者的一半。
分布式SQL数据库将RDBMS的SQL语言和事务处理功能与NoSQL数据库特有的云原生功能,例如高可用性,可伸缩性,容错性和地理分布,结合在一起。
基准设置
下表总结了这些数据库的设计要点。我们在这里明确不考虑Aurora PostgreSQL的多主机设置,因为它损害了数据的一致性。
写性能在本文中,我们将研究这些数据库以下几个方面的性能和可扩展性:
扩展写
扩展读
扩展连接
分布式事务
以下所有基准测试都是在AWS云的俄勒冈区进行的。YugabyteDB 2.0部署在一个跨多可用区的类型为i3.4xlarge的三节点群集上(每个节点上有16个vCPU)。整个部署架构可以显示为下面的UI屏幕截图。
CockroachDB(版本19.1.4)配置与YugabyteDB相同。Aurora PostgreSQL部署在类型为db.r5.4xlarge的双节点上(每个节点上有16个vCPU)。一个节点是主节点,另一个节点是备用节点,用于在其他可用区中进行快速故障转移。此设置如下所示。
请注意,CockroachDB仅支持串行化隔离,而YugabyteDB和Amazon Aurora同时支持串行化和快照隔离。Amazon Aurora甚至支持较低的提交读隔离级别,这也是它的默认设置。这篇文章中的基准测试使用所有数据库中的默认设置,对于只有简单插入和非聚集索的程序来说,这些默认设置足以保证正确性。
写性能
在这个基准测试中,我们将5000万的唯一键值数据用预编译绑定的INSERT语句,插入了具有256个线程并发写入的数据库。在此期间,没有对数据库的读操作。基准结果如下所示。
这些数据仅仅是展示YSQL性能的开始,YugaByteDB的核心存储引擎DocDB同时支持YSQL和YCQL,具有更高的吞吐量。和YSQL很相似并且运行在DocDB上的半关系型YCQL API更加成熟,因此性能更好,如下图所示。
我们正在YSQL查询层中进行其他改进,以实现更好的性能来以匹配YCQL。
写扩展
那当我们需要扩展时怎么办呢?
我们已经在上表中指出,AWS Aurora无法水平扩展写入。在Aurora中扩展写入的唯一方法是垂直扩展,这意味着必须使单个节点更坚固。就vCPU而言,Aurora的最大扩展写IOPS,取决于vCPU的最大可用节点。
YugabyteDB每秒写入量超过100万
由于YugabyteDB既具有高性能又具有水平可扩展性,因此我们准备用一个实验将每秒的写操作扩展到百万级别。实验选择了型号为c5.4xlarge的实例,部署在单可用区,拥有100个节点的YugabyteDB集群上(每个节点16个vCPU和1TB存储)。该集群名为MillionOps,如下图所示。
Aurora PostgreSQL 每秒168K的写入瓶颈
上述基准测试结果(每秒写入28K)是运行在具有16个 vCPU(db.r5.4xlarge实例)的机器上。可是Aurora可用的最大实例具有96个vCPU(db.r5.24xlarge实例),资源比上述基准测试所用的16个vCPU(db.r5.4xlarge实例)多6倍。假设写入随着机器大小扩展,那么具有多个表的Aurora数据库的最大写入吞吐量的上限为每秒168K。即使Amazon Aurora可以存储多达64TB的数据,但这种吞吐量瓶颈仍将在可用存储的利用方面带来实际挑战。在达到写吞吐量上限之后,唯一的选择是在应用程序层将数据层手动分片,这是一项复杂的工作。
相比之下,YugabyteDB集群的每秒写入量随节点数线性扩展。一个具有12个节点的YugabyteDB集群能够超过上面提到的每秒168K的写入吞吐量。下图比较了这两个数据库的写扩展性能。
读扩展
两种数据库都能实现读扩展,然而:
Aurora中读扩展实现的同时,提供了过时的读数据,牺牲了数据的一致性。
如果必须在Aurora中查询只读副本,那么应用程序设计可能会变得更加复杂。
让我们看看如何在这些数据库中实现读扩展。
为了扩展数据库,Aurora PostgreSQL文档描述了以下内容。
我们已经发现了实例扩展会带来写入吞吐量的上限。让我们来看看Aurora中的读扩展。读和写在Aurora中是单独分开的节点在执行。为了进行读扩展,应用程序要负责从多个读端点中进行显式地读取。
首先,这意味着应用程序需要在设计中明确包含要连接的端点。这降低了应用程序的开发速度,因为要连接到哪个端点成为应用程序体系结构的一部分,并且可能不是一件容易的事,尤其是在设计故障转移方案的时候。
其次,更重要的问题是,从副本中读取数据将返回过期的数据,这可能会损害数据的一致性。为了读到真实的数据,应用程序必须从主节点读取数据(这个主节点还处理所有写操作)。由于单个节点需要保证读一致,因此这个架构的读吞吐量取决于最大节点的性能(类似于我们对写性能所做的分析)。
相比之下,YugabyteDB将所有节点视为完全相同。这可以通过以下方式改善性能:
应用程序只需要连接到集群中的一个随机节点,其余的由数据库处理。数据库的所有节点都可以放在一个负载均衡器后面。
执行读取时,群集的所有节点都可以参与,因此读取吞吐量要高得多。
用集群感知的JDBC驱动程序消除负载均衡器
为了进一步简化操作,我们正在研究标准JDBC驱动程序的集群感知版本,称为YugabyteDB JDBC。这些驱动程序可以连接到集群的任何一个节点,并从由YugabyteDB自动维护的集群成员中“发现”所有其他节点。
诸如节点添加,删除和故障之类的事件被异步推送到这些客户端驱动程序,从而导致应用程序时候获得最近的集群成员身份信息。使用支持群集的JDBC驱动程序,我们不再需要手动更新负载均衡器后面的节点列表或管理负载均衡器的生命周期,从而使基础结构变得更加简单和敏捷。
扩展连接
扩展连接数是PostgreSQL普遍关心的问题。Aurora PostgreSQL数据库的连接数是有限制的。下表从AWS文档中总结了不同实例大小下,建议采用的数据库连接数。
该表显示,即使在最大的Aurora PostgreSQL数据库中,建议的最大连接数也才为5000(尽管文档中提到的理论最大值为262,142)。这限制了具有许多微服务和大规模的云原生应用程序的性能。
YugabyteDB可以在集群中的每个节点上指定连接数。每个节点的默认连接数是300(可配置),在我们的示例中设置3个节点,最多可获得900个连接。但是扩展连接很容易。通过选择6个具有8个vCPU的实例(而不是3个具有16个vCPU的实例),我们有效地将连接数增加了一倍,达到1.8K,同时保持资源不变!同样,通过选择具有8个vCPU的24个实例(大致相当于具有96个vCPU的最大Aurora群集),部署可以扩展到超过1万个连接。
分布式事务——延迟VS扩展性
YugabyteDB是一个可以水平写扩展的数据库。这意味着集群的所有节点都同时处于激活的状态(而不是像Aurora那样仅是一个主节点)。为了实现水平写的可伸缩性,数据被无缝地分成小块,称为分片,然后将他们分布在集群的所有节点上。
当YugabyteDB需要执行分布式事务时,它需要在不同的分片上执行写操作,最终是对远程节点的RPC调用。这样的结果是,数据库可能必须通过网络执行RPC调用才能处理用户终端的事务,这会同时影响到最终用户看到的延迟和吞吐性能。使用Amazon Aurora,整个事务在主节点上进行处理,没有远程RPC调用。
这成为两种设计的基本架构折衷,因此在选择之前需要仔细考虑。但是原始性能数据是什么样的呢?为了确定这一点,我们执行了一个基准测试,将500万唯一键值数据插入到一个具有非聚集索引列的数据库表中。在此期间没有对数据库的读操作。
使用基准测试分析权衡方案
以下是这些分布式PostgreSQL数据库中非聚集索引基准测试的结果。这些基准测试使用128个写线程并行写了500万个事务(每个事物写两个键)。这些基准测试是通过上述列出的标准配置执行的。
YugabyteDB在执行涉及主表和索引的多分片分布式事务之前,需要进行3-4次远程RPC调用。这导致了相对更高的延迟和更低的吞吐量。在上述基准中,YugabyteDB事务的写延迟为22ms,而Aurora PostgreSQL仅为6ms。此外,一个3节点(16 vCPU)YSQL集群的写吞吐量仅为5.3K,而Aurora PostgreSQL的写吞吐量却为20K。
让我们来看看当扩展上述写入的工作量时会发生什么。我们在前一节已经讨论过,Aurora PostgreSQL最多只能扩展到96个内核,或者说,Aurora PostgreSQL所有通过应用程序和索引执行在各种表上事务的写入上限为每秒120K。通过YugabyteDB,一个63节点的集群每秒可以传递120K事务,一个106节点的集群每秒可以传递200K事务。
这意味着,如果您的数据库实例永远不需要处理超过每秒120K的事物量,Aurora PostgreSQL会是一个很棒的选择。如果将来扩大规模很重要,那么YugabyteDB会是一个更好的选择。
注意,本节中的分析仅适用于写入事务,读取不受此分析影响。