MongoDB时间系列基准和评论
#mongodb #benchmark #questdb #timeseries

本文将QuestDB与市场上最受欢迎的数据库之一MongoDB进行了比较。我们从基准性能和用户体验方面查看两个数据库。

MongoDB vs QuestDB

时间序列数据近年来广受欢迎。它用于多种行业和用例中(如果您是这个空间的新手,请阅读我们的文章'What is Time-Series Data'。因此,我们已经看到了新的时序数据库的出现,要么有目的地构建以处理时间序列数据,或作为A之上的附加功能
通用数据库。时间序列数据通常使用专用工具处理。这是因为处理此类数据具有一些个人要求:

  • 时间序列数据通常是从市场数据提供商,传感器或消息经纪人(例如Kafka)等来源连续附加(流中)的。
  • 数据的重要性通常会随着时间的流逝而降低。较旧的数据通常不如更新的数据。
  • 最新数据通常需要实时处理和快速分析。

基于这些特征,时间序列数据库的评估应涵盖以下特征:

  • 高摄入速度以处理高基数的大量数据
  • 数据分析的快速查询“飞行”
  • 易于使用可以促进时间序列数据操纵的知名语言
  • 数据生命周期策略

MongoDB和QuestDB比较简介

MongoDB成立于2009年,是一个开源,NOSQL,面向文档的数据库,均受服务器端公共许可证(也可用商业许可证)。它领导DB-Engines上的文档商店(2023年2月)的排名。 MongoDB在C ++中实现,并在版本5.0中添加时间序列数据支持。

QUESTDB是一个由Apache许可证2.0许可的开源时间序列数据库。它专为高吞吐量摄入和快速的SQL查询而设计。它支持使用InfluxDB线协议,PostgreSQL线协议和用于批量进出口的REST API支持模式 - 不合时宜的摄入。

基于DB-Engines的简短比较:

的mongoDB连接器读取仅读取SQL查询 的专有协议
名称 mongodb QuestDB
主数据库模型 文档商店 时间序列DBMS
总排名 #5 #146
其他等级 #1文档商店 #11时间序列DBMS
实施语言 C ++ Java(低潜伏期,零GC),C ++
SQL# 通过bi 类似SQL的查询语言
API和其他访问方法 使用JSON InfluxDB线协议,Postgres电线协议,HTTP,JDBC

MongoDB的默认存储引擎是WiredTiger,它整合了B-Tree和LSM-Tree存储模型的概念。

与传统的RDBM不同,MongoDB中的数据不是在表,行和列中组织的,而是在包含字段的文档集合中。 MongoDB支持时间序列的集合,这是由内部集合支持的幕后刻板。

使用基于列的存储引擎,QuestDB将数据密集存储在数组中。它包装了SIMD指令,通过利用现代CPU来加快查询。数据用本地时间对时间进行排序,并存储在时间分区中。这些分区是仅附加和版本的。 QUESTDB仅在任何给定时间的查询中)启动记忆中最新版本的分区,并且随着插入更多的数据,查询性能不会放慢速度。

基准

像往常一样,我们将行业标准Time Series Benchmark Suite (TSBS)用作基准工具。不幸的是,TSB上游不支持MongoDB time series collections

因此,我们使用此pull request的补丁。

我们用于基准的硬件如下:

  • C6A.12XLARGE EC2实例,带有48 VCPU和96 GB RAM
  • 为最大设置配置的500GB GP3卷(16,000 IOPS和1,000MB/S吞吐量)

基准的软件侧为:

  • 人格22.04
  • MongoDB社区版6.0.4带有默认配置
  • QuestDB 7.0带有默认配置

摄取

为基准摄入,我们使用了以下命令:

$ ./tsbs_generate_data --use-case="cpu-only" --seed=123 --scale=4000 --timestamp-start="2016-01-01T00:00:00Z" --timestamp-end="2016-01-03T00:00:00Z" --log-interval="10s" --format="mongo" > /tmp/mongo_data

$ ./tsbs_load_mongo --file=/tmp/mongo_data --document-per-event=true --timeseries-collection=true --workers=10

在这里,我们使用了来自4,000个模拟主机的两天CPU数据的cpu-only方案。在两种情况下,我们都使用10个客户连接。

在MongoDB中,我们还使用MongoDB建议的时间序列收集来进行时间序列数据。

下图总结了摄入性能的结果:

Time Series Benchmarking Suite: Ingestion rate for MongoDB and QuestDB

要加载超过6.91亿个指标,MongoDB需要超过2,000秒,而QuestDB则需要不到90秒。 QuestDB的速度比MongoDB快24倍。

也值得注意的是,MongoDB建议分批摄入措施。为了获得更好的性能,这些措施由元数据而不是时间分组。否则,摄入效率较低。因此,摄入性能取决于元数据,并且对文档中字段的顺序敏感。插入以Bson格式完成,这是一种用于存储documents的序列化格式,并在MongoDB中进行远程过程调用。

相比之下,QuestDB的摄入不受批处理中的元数据的影响。这是由于QuestDB的column-based storage model

摄入数据对于开发人员而言易于错误。

查询性能

QUESTDB是为时间序列数据而设计的;因此,ThatQuestDB的摄入量优于MongoDB也就不足为奇了。但是,查询性能对于时间序列数据分析同样重要。

作为标准TSBS基准的一部分,我们比较了几种类型的查询的两个数据库,这些数据对于时间序列数据很受欢迎:

  • lastpoint:每个主机的最后读数
  • groupby-orderby-limit:最后5个汇总读数(跨时间),然后在随机选择的端点
  • 之前
  • high-cpu-all:一个指标在所有主机上都高于阈值的所有读数
  • double-groupby-1:在时间和主机上汇总,平均每小时每小时1个CPU度量为24小时
  • single-groupby-1-8-1:一个单位的一个指标上的简单汇总(最大),每5分钟一次,持续1小时
  • cpu-max-all-1:单个主机每小时每小时汇总所有CPU指标

这是一些示例命令,要生成和运行查询:

$ ./tsbs_generate_queries --use-case="cpu-only" --seed=123 --scale=4000 --timestamp-start="2016-01-01T00:00:00Z" --timestamp-end="2016-01-03T00:00:00Z" --queries=1000 --query-type="lastpoint" --format="mongo" > /tmp/mongo_query_lastpoint

$ ./tsbs_run_queries_mongo --file=/tmp/mongo_query_lastpoint --workers=10

再次,我们使用10个并发客户端连接进行比较。我们衡量每个查询所需的时间,每个查询以毫秒为单位:

Query performance comparison displayed as a table.

基准的两个结论:

  • MongoDB对于简单的聚合表现稍好一些 (single-groupby-1-8-1cpu-max-all-1)。
  • QuestDB在更复杂的查询中胜过MongoDB。

QUESTDB对简单聚合的表现不佳,可以部分解释以下事实:TSB并非设计用于使用绑定变量。相反,它发送了很多
唯一的查询使QuestDB的查询缓存冗余。此外,查询引擎在这些特定查询中使用字节码的生成,因此许多一次性查询对JVM Metaspace施加了压力。

对于更复杂的查询,QuestDB的更好性能在于其查询引擎:它利用柱状数据布局,SIMD instructions和多线程处理。

使用方便

如果您知道要寻找什么以及如何查询数据以揭示其他隐藏的趋势,则

时间序列数据可以提供很好的见解。理想情况下,数据库应鼓励交互式探索,以便开发人员可以轻松地尝试不同的查询迭代。因此,我们现在将将两个数据库的查询语言进行比较。

数据汇总的查询语言

mongoDB是NOSQL,并支持MongoDB查询语言(MQL),该语言基于JavaScript,是MongoDB独有的。学习曲线可能很陡。 MongoDB最近开始支持SQL,但是时间序列查询并非如此。

QuestDB中的查询是通过time-series extensions的标准SQL进行的。

查看MongoDB的documentation,我们找到了一个样本来计算样本数据集每小时每小时的交流单元的总消耗:

var pipelineBuildingsSummary = [
// Calculate each unit's energy consumed over the last hour for each reading
{"$setWindowFields": {
    "partitionBy": "$deviceID",
    "sortBy": {"timestamp": 1
    },
 "output": {
    "consumedKilowattHours": {
      "$integral": { "input": "$powerKilowatts", "unit": "hour", },
      "window": { "range": [-1, "current"], "unit": "hour", },
      },
    },
}},
// Sort each reading by unit/device and then by timestamp
{"$sort": { "deviceID": 1, "timestamp": 1, }},
// Group readings together for each hour for each device using
// the last calculated energy consumption field for each hour
{"$group": {
    "_id": {
      "deviceID": "$deviceID",
      "date": { "$dateTrunc": { "date": "$timestamp", "unit": "hour", } }, },
      "buildingID": {"$last": "$buildingID"},
      "consumedKilowattHours": {"$last": "$consumedKilowattHours"},
  }},
// Sum together the energy consumption for the whole building
// for each hour across all the units in the building
 {"$group": {
    "_id": {
      "buildingID": "$buildingID",
      "dayHour": {"$dateToString": {"format": "%Y-%m-%d %H", "date": "$_id.date"}}, },
      "consumedKilowattHours": {"$sum": "$consumedKilowattHours"},
  }},
// Sort the results by each building and then by each hourly summary
{"$sort": { "_id.buildingID": 1, "_id.dayHour": 1, }},
// Make the results more presentable with meaningful field names
{"$set": {
    "buildingID": "$_id.buildingID",
    "dayHour": "$_id.dayHour", "_id": "$$REMOVE",
  }},
 ];

使用QuestDB,查询语法更短,更简单:


SELECT timestamp, buildingID, SUM(powerKilowats)
FROM device_readings
SAMPLE BY 1h ALIGN TO CALENDAR;

任何熟悉SQL的开发人员都应了解此查询。唯一需要解释的部分是SAMPLE BY,该部分是专门设计用于基于时间单元的大型数据集的专门设计的。

此外,QuestDB还提供了FILL关键字,这是identify and fill missing data的众多方法之一,这是数据分析的常见第一步:


SELECT timestamp, buildingID, SUM(powerKilowats)
FROM device_readings
SAMPLE BY 1h FILL(LINEAR) ALIGN TO CALENDAR;

mongoDB提供两个聚合操作员,
\$densify\$fill,完成类似的任务。但是,它们并不像使用样品那样简单。操作员\$densify标识了丢失的数据并填写了数据,但不会重新访问数据集。

如果要填补空白和值,则需要使用一个致密,然后使用填充聚合,这将在管道中添加约20行代码。

其他考虑因素

我们涵盖了选择正确的时间序列数据库的两个主要因素:性能和可用性。但是,当使用时间序列数据库(TSDB)构建系统时,还有其他方面需要考虑。

生命周期政策

在大多数时间序列数据应用程序中,每个数据点的值随着时间的推移会降低。因此,我们还应该将数据生命周期管理视为数据库评估的一部分。

QUESTDB提供了detach partitions的能力,以便于更便宜的存储空间,并在需要时将其重新安装。此外,QuestDB工程团队正在为自动化此过程的功能自动化。

mongoDB允许auto-deletion of data。删除后,只能从backup中恢复数据。

在QuestDB中,如果您想删除而不是保留较旧的数据以备将来使用,则可以定期调用DROP PARTITION语句。

生态系统和支持

QuestDB是一个年轻的数据库。但是,QuestDB建立在
immensely popular SQL语言,与Postgres wire protocol兼容。此外,QuestDB具有一个充满活力和支持的社区:用户可以与工程团队建立QuestDB进行互动。

mongoDB拥有一个令人印象深刻的生态系统,并且已经存在了很长时间。作为一种更成熟的产品,MongoDB是最受欢迎的文档商店数据库。

结论

我们比较了MongoDB和QuestDB,评估了性能和用户体验。 QuestDB摄入数据的速度快24倍,大多数基准的查询表现更好。

我们相信SQL的简单性可以从时间序列数据中解锁见解。 QUESTDB提供的一些SQL扩展(例如样本)可以显着降低查询的复杂性并改善开发人员的用户体验。

我们很高兴看到通用数据库(例如MongoDB)将其脚趾浸入时间序列数据中。我们认为,时间序列数据足够独特,值得获得目的构建数据库,但是必须权衡与将另一个数据库添加到堆栈中的复杂性。

如果您对QuestDB与其他OLAP和时间序列数据库相比的性能感兴趣,请查看我们的benchmark articles