关于 NoSQL DBaaS 的性能比较的思考:MongoDB Atlas、AWS DynamoDB 和 Couchbase Cloud

首先

在各种软件公司开始提供其服务作为云上托管服务的趋势中,关于NoSQL数据库也似乎已经出现了多种服务。(以下列出的条目名称是从Google搜索结果中获取的,也按照搜索量的顺序排列)

    • MongoDB Atlas Database – Cloud DBaaS for MongoDB

 

    • Redis Enterprise Cloud – Fully Managed Cloud Service

 

    • couchbase cloud™ – database-as-a-service

 

    Multi-cloud DBaaS built on Apache Cassandra™

2020年末,Altoros公司发布了对NoSQL文档数据库(包括Couchbase Cloud,MongoDB Atlas,AWS DynamoDB)作为托管服务进行性能评估的结果。

以YCSB为基准的NoSQL数据库作为服务的性能评估:Couchbase Cloud、MongoDB Atlas和AWS DynamoDB。

我打算将我作为一个经常使用Couchbase Server的人注意到的问题(简而言之,我感到不自然的地方)写成一篇文章。

在深入了解白皮书内容之前,首先我们要了解Couchbase的架构特点。

Couchbase中的数据检索特殊性

这件事本身就需要写另一篇文章来详细说明,但在这里,我会尽量简化和整理要点。

查询/索引服务的特殊性

在Couchbase Server中,查询服务负责响应对数据库的搜索请求,而索引服务被用来支持查询服务的运行。这两项服务作为可选项与核心数据服务独立存在,如果选择不使用,可以选择不存在(未启动)的状态。
从基于关系型数据库管理系统(RDBMS)和许多其他NoSQL数据库的经验和知识出发,这似乎并不难理解。

主键索引的特殊性

Couchbase有主要索引和副本索引(也可以称之为“第二索引”)。然而,它们的意义与其他数据库(如RDBMS)有着显著的不同。

这种差异在最佳实践指南中的一个提示中明显体现出来。其中提到“不要在生产环境中创建主要索引”。指南中写道:

主键索引扫描可以看作是RDB中的全表扫描。

Couchbase的主要索引是在“表”级别上创建的、无需指定特定“键”的索引。

对于Couchbase而言,创建次要索引是使用作为搜索条件(尤其是作为搜索目标数据)的“列”作为“键”(有时是多个键)来构建的。

「永远不要在正式场合使用」的主索引的存在意义仅仅是为了在开发过程中即使没有创建和优化索引的情况下,也能够尝试查询操作。

有一个选择,即为了满足分析师执行即席查询的要求,另外提供了一种名为分析服务的工具。

微波扫描的特殊性

Couchbase的核心数据服务是以键值存储方式构建的,读取访问是通过(文档的)键(即文档ID)来进行的。此外,并不存在以键来指定一定范围作为范围的(低级)API作为键的访问接口(这并不意味着没有有效使用多个键进行访问的方法,稍后将进行说明)。对于熟悉列式NoSQL等的人来说,这可能感觉有些不同。

然而,通过理解Couchbase的架构,可以将其解决。

首先,Couchbase是一个分布式系统(“哦,我知道的”)。

②并且,Couchbase存储的文档是基于哈希值进行分布的(“这并不是特别罕见的”)。

在Couchbase中,没有专门承担客户端接口角色的节点(”…”)。

④ Couchbase的客户端(SDK)使用数据和节点映射,直接访问存储数据的节点(”嗯…这样就可以了?”)。

所以,Couchbase沒有範圍掃描的API(請問您能理解嗎?)。

最后的③和④只是表达了同一个事物的两面,本来应该是一个句子的,但是③是作为对于熟悉分布式系统(数据存储)某种类型的人的先入之见的回应,④则是作为Oracle Coherence等一类内存数据库的架构,在某种类型中并不罕见的架构来表达。

对Altoros的绩效评估感到不舒服。

違和感即涉及到前文提到的範圍掃描需求的一部分(工作量E:短範圍掃描)。以下是用於進行此驗證的訪問方法清單。

image.png

对于Couchbase的访问是通过使用”键”来实现的,而且是通过使用一种叫做N1QL的查询语言来执行的。换句话说,它依赖于查询服务和索引服务。

在Couchbase中实现范围扫描的合适方法是什么?

如果要通过Couchbase进行基于“键”的范围扫描,我认为在API调用端进行以下控制是合理的(进行苹果到苹果的比较)。

同期模型的例子

    protected List<Object> get() {  
        for (String key : keys) {
            GetResult result = collection.get(String.valueOf(key));
            listResults.add(result);
        }
        return listResults;
    }

异步的示例

    protected List<Object>  get() {
        ReactiveCollection reactiveCollection = collection.reactive();

        Flux<Object> resultFlux = Flux.fromArray(keys.toArray())
            .flatMap( k -> reactiveCollection.get(String.valueOf(k)));

        List<Object> listResults = resultFlux.collectList().block();
        return listResults;
    }

确实,仅凭上述的代码是无法实现获取所有id值大于某个特定值的查询的(而且没有考虑键的设计前提)。
然而,在获取数据时对于键的范围,不考虑键的值(例如使用UUID)是不合理的。
如果键是简单的递增,那么可以通过当键不存在时停止处理的方式进行足够的应对,而且从根本上说,键的值应该在上限和下限确定的情况下被预先确定,这是典型的情况吧。

为什么Couchbase能够在性能方面超越其他数据库,而不依赖于键序列?

在MongoDB中,我们在白皮书中提到的并且也有说明的问题是,为了键的分片,我们可以选择“范围(range)”和“哈希(hash)”。根据以上的性能比较,我们采用了范围分片。

MongoDB Atlas通过分片键来分发数据。这个数据库支持两种类型的分片键:基于范围的和基于哈希的。基于范围的分片支持更高效的范围查询。给定一个基于分片键的范围查询,查询路由器可以很容易地确定哪些块与这个范围重叠,并将查询路由到只包含这些块的分片上。然而,基于范围的分片可能导致数据分布不均匀,这可能会抵消分片的一些好处。

在Couchbase中,数据分片的逻辑是统一的哈希。尽管Couchbase必须访问更多的节点来访问一系列的数据,与区间分区相比,但它仍然可以实现与MongoDB相同的性能。这是因为Couchbase的体系结构是一个内存优先的体系结构(与Oracle Coherence或Redis相比较更合适),并且不受磁盘限制。

在选择中有一种选择是内存数据库(Oracle Coherence、Redis、以及Couchbase),与数据永久性的关系是什么。

image.png

尽管在上面提到了”同等的性能”,但我在这里的主张是,即使通过Range Scan,也可以通过直接使用数据服务的API而不经过查询服务,可以实现比上图中明显更高的性能。
作为证据,或者说除了在这里详细介绍的工作负载之外,我们还能够产生如下不同维度的成果。

这个工作负载A是通过直接指定键来进行比较的,由于”内存优先架构”的存在,可以说这个性能比较是基于这种架构导出的。

image.png

以下的JOIN工作负载是对多个文档进行连接操作进行比较的结果,并且利用了查询/索引服务的次要索引的优势。

image.png

最后

我认为对于工程师而言,比起了解性能比较的结果,更重要的是理解其原因。

在进行性能比较的立场上,选择实现适当性能的方法是重要的,并且只需要负责报告结果。提及架构上的特点和差异已超出了性能评估报告的范围。
然而,在本次报告中,有一些地方认为如果对内部架构有深入了解,就可以选择更合适的验证方法,最终形成了本文的内容。

出于这个意义,对每个数据库的评价超出了本文的目的,但为了那些感兴趣的人,最后引用一下原文关于总体评价的内容(尽管只是摘录部分内容,请有兴趣的人务必参考原文的全部内容)。

附录。引用和翻译的综合评价。

Couchbase Cloud相较于其他数据库在所有评估的工作负载中展现出更好的性能。在查询方面,Couchbase Cloud提供足够的功能来处理部署的工作负载。此外,Couchbase Cloud的查询引擎支持对大型数据集进行聚合、过滤和JOIN操作,无需为每个特定的查询建模数据。随着集群和数据集的增长,Couchbase Cloud确保这些操作在可扩展性方面满足令人满意的水平。

Couchbase Cloud在所有评估的工作负载中表现出优越的性能,与其他数据库相比。在查询方面,Couchbase Cloud提供足够的功能来处理部署的工作负载。此外,Couchbase Cloud的查询引擎支持大规模数据集的聚合、筛选和JOIN操作,并且不需要为每个特定查询重新建模数据。即使群集和数据集的规模增大,Couchbase Cloud也能保证在所有这些操作上具有足够的可扩展性水平。

MongoDB Atlas的表现相对不错。MongoDB具备可扩展性,能够处理日益增长的数据和集群扩展。在此基准测试中,我们观察到的唯一问题是MongoDB在分片集合方面不支持自动JOIN操作,这导致了JOIN负载下的负面影响和性能问题。

MongoDB Atlas在很大程度上取得了相當好的結果。MongoDB可以說是足夠可擴展以應對不斷增加的數據量和集群擴展。在這個基準測試中,MongoDB唯一的問題是它不支持在分片集合上進行JOIN操作,這對JOIN工作負載產生了不良影響和性能下降。

亚马逊 DynamoDB 与其他数据库有很大的区别,因为它看起来像是一个没有适当调优的纯服务。只有两个参数可以改变:读和写的吞吐容量。在这种情况下,读和写的容量是根据其他数据库每个工作负载的成本进行计算的。不幸的是,亚马逊 DynamoDB 没有提供有竞争力的结果。它产生了大量的失败请求。此外,亚马逊 DynamoDB 没有参与多个工作负载,因为必须改变数据模型才能获得具有竞争力的结果,因此无法与其他数据库进行比较。

亚马逊 DynamoDB与其他数据库有明显的区别。这是因为它表现得像是一个纯粹的服务,似乎与适当的调优无关。可更改的参数仅限于读取容量和写入容量。在本例中,读取和写入容量根据其他数据库的成本计算。不幸的是,亚马逊 DynamoDB没有提供有竞争力的结果。它还导致了大量请求的失败。此外,为了获得有竞争力的结果,需要修改数据模型,而且由于无法与其他数据库进行比较,因此无法将其包含在一些工作负载评估中。

bannerAds