重构 InfluxDB 3:使用 Rust 与 Apache Arrow 打造现代时序数据库
Source: InfoQ - Backend
要点
InfluxDB 3是对核心数据库引擎的全面重构,旨在通过提升基数、提供更经济的对象存储以及添加SQL支持来扩展现有产品的功能。团队没有沿用原有的技术栈,而是选择了FDAP技术栈(Arrow Flight、DataFusion、Apache Arrow和Parquet)。Rust因其卓越的性能、内存安全性和“无畏并发”而被选为InfluxDB 3的核心开发语言,这解决了之前使用Go语言实现InfluxDB时遇到的各种挑战。最新版本增强了分析查询功能,支持无限基数数据,并将SQL定位为主要查询语言,从而更高效地与第三方工具集成。在未来的更新中,我们将集成Apache Iceberg,使InfluxDB中采集的数据可供数据湖和数据仓库(包括Databricks和Snowflake)使用。
四年前,InfluxData开始研发InfluxDB的新核心,以应对现代时间序列负载日益增长的需求。这个决定并非轻率作出,而是由支持特定客户需求的需要驱动的。这些需求包括无限的基数、更便宜的对象存储(用于历史数据)、对SQL语言的支持,以及对我们文件格式和标准的更广泛的生态系统支持。
这些努力的成果是InfluxDB 3,一个为时间序列优化的实时、列式数据库,使用Rust语言和FDAP堆栈(Apache Flight、DataFusion、Arrow和Parquet)构建。新核心代表了我们核心数据库技术的显著改进。它移除了基数限制,以便用户可以引入大量时间序列数据,还能实现无限扩展,具备SQL查询能力、分层数据存储和快速分析查询。
这篇文章重点介绍了这些核心技术选项和InfluxDB 3提供的新功能。
从一开始,InfluxDB的愿景就不只是做一个时间序列数据库。我们想要一个能够大规模存储原始事件、处理所有形式的观测数据,并按需生成时间序列以满足多样化的分析任务的平台,无论是实时还是历史背景。我们的目标是创建一个能够管理时间序列、观测和历史数据的数据库,将其从单纯的指标存储转变为高级分析的全面解决方案。
InfluxDB的演变
当我们在2016年发布InfluxDB 1.0时,它是用Go语言编写的,并且有一个我们从头开始构建并特别对指标负载优化的自研存储引擎。我们称之为时间结构化合并树(TSM),是日志结构化合并树(LSM)的一个变体。TSM的设计将时间序列存储与内存中倒排索引配对,将元数据映射到底层的时间序列。
这为低至中等基数负载提供了出色的性能,但随着倒排索引中元数据量的增加,数据库性能受到了影响。其他时间序列数据库也广泛采用了类似的结构,如Facebook Gorilla、Prometheus等。
基数限制一直是时间序列数据库面临的挑战,因为包含大量个别时间序列的数据集会导致性能下降。我们引入了时间序列索引(TSI)来解决这个问题,将索引扩展到跨越磁盘和内存。该功能在2017年11月发布的1.4版本中正式发布,我们可以支持基数在数千万到可能的一亿的负载。尽管如此,它仍然无法为我们所需的分析查询提供足够的性能。
我们在2020年的下一个版本,InfluxDB 2.x,通过新的脚本语言、UI和多租户、基于使用的SaaS平台扩展了核心功能。然而,用户需要的是无限的基数、更实惠的对象存储和增强的查询性能,而不是新的脚本语言。实现这些需求需要在底层架构上进行根本性的转变。
2020年11月,我们宣布了InfluxDB IOx(最终成为InfluxDB 3),我们的计划是重新设计数据库以满足高分辨率负载的需求。我们认识到,要完全实现我们对InfluxDB的愿景,需要对数据库进行彻底的重新架构,我们之前已经确定了几种可能改变游戏规则的OLAP技术。在验证了这些好处之后,我们使用Rust和Apache Arrow、Apache DataFusion、Apache Parquet和Arrow Flight重建了新核心。我们创造了FDAP堆栈这个术语来描述这个堆栈。随着时间的推移,这种基础工具的选择演变成了一个复杂的分析系统构建堆栈。这些构建块是开放数据系统、实时分析、数据湖和数据仓库架构的未来。
InfluxDB 3的发布提供了一个强大、灵活且高性能的数据库,满足了我们处理所有形式的时间序列、观测和历史数据的原始愿景,具有无与伦比的性能和规模。我们还整合了客户请求的功能:支持高基数数据、同时对多个时间序列进行分析查询、更经济的用于历史数据的对象存储,以及其他增强功能。我们的目标是与更广泛的第三方工具集成,并主要支持SQL作为一流的查询语言。
为未来重建:为什么选择Rust?
在2020年初,我对Rust非常感兴趣,并认为它将是实现高性能、服务端软件的首选语言。当我们评估InfluxDB的未来时,很明显,实现我们的长期愿景需要根本性的重构。考虑到这一点,我们选择使用2020年最好的工具来构建,而不是2013年我们创建InfluxDB 1.0时可用的最佳语言,即Go。Go的简单性和性能为我们服务得很好;它非常适合构建InfluxDB所需的并发、分布式系统。我们将继续在InfluxData的许多项目中使用它。
然而,当涉及到v3时,使用Rust构建核心以实现最大效率是至关重要的。Rust提供了卓越的性能、内存安全性,以及Rust社区所说的无畏并发性"。Rust为无畏并发性提供的保证是颠覆性的,消除了在其他语言(比如Go)中难以追踪和修复的一整个类别的错误。与Go类似,Rust没有受到与C/C++系统相关的众所周知的安全和稳定性问题的困扰。Rust使用一种新颖的“借用检查器”系统在编译时验证安全性,这比Go的内存管理器更快。Rust的包管理与crates.io"及其强大的生态系统使我们能够利用现有的库和工具,加快了我们的开发过程。
最后一点,DataFusion查询引擎用Rust编写,这使我们决定将其作为InfluxDB 3的查询引擎,而不是采用用C++编写的引擎。DataFusion与Rust的性能优势和高级SQL执行能力的一致性使其成为理想选择。这种集成允许InfluxDB 3利用Rust的性能优势和DataFusion的SQL能力,增强了数据库的查询和处理能力。
FDAP堆栈:InfluxDB 3的核心组件
我们在开发InfluxDB 3时的一个目标是尽可能多地使用其他地方的开源库和工具。我们希望围绕更大的社区项目构建,为它们做出贡献,而不是在每个数据库组件上重新发明轮子。
当我们开始围绕FDAP技术重建InfluxDB基础时,我们赌它们会获得一个积极的社区,这将显著促进它们的成熟度、性能和功能。当我们在2020年选择这些项目作为数据库核心时,还很难预料它们会像过去四年多以来那样被如此广泛地采用和贡献。
Apache Arrow Flight
对于Core和Enterprise,我们采用了Flight SQL作为高性能查询选项。Flight SQL因其能够快速从像InfluxDB这样的分析数据库传输数百万行数据而在数据科学社区越来越受欢迎。我们还增强了我们的HTTP接口,以支持查询并以JSONL、CSV和Parquet响应格式返回结果。Parquet因其能够高效传输大型数据集而脱颖而出,使其成为扩展数据工作流程的理想选择。
DataFusion
DataFusion是一个基于Rust的SQL解析器、规划器、优化器和执行引擎,它使用Apache Arrow作为其内存模型。它提供了高性能的查询处理,包括流式向量化执行、自动并行化和查询优化。
我们的工程师团队在InfluxDB 3的开发过程中与一个大型全球社区紧密合作,对DataFusion做出了重大贡献。当我们在2020年中期开始InfluxDB重构时,该项目仍处于早期开发阶段。然而,由于数百人多年的开发和无数的工程小时,它已经取得了显著进展。它最近被指定为Apache软件基金会的顶级项目"。性能一直是DataFusion社区的核心关注点,速度吸引了来自活跃且不断增长的社区的显著贡献。
DataFusion随着额外的功能增强和性能改进而成熟,这些功能直接集成到InfluxDB 3和其他基于DataFusion的系统中。这种一致性使我们能够与一群热情的工程师合作,高效快速地在DataFusion中开发先进的数据库技术。
Apache Arrow
Apache Arrow是InfluxDB 3用于高效数据表示和处理的列式内存数据格式。Arrow在核心使用最佳实践和缓存高效的列式布局标准化内存中的数据表示。它简化了复杂数据类型的表示,提高了性能和易用性。
当我们开始使用Arrow的Rust实现arrow-rs"时,它相对较新。随着时间的推移,InfluxData和其他许多贡献者投入了大量时间和专业知识,使其成为最好的Arrow实现之一。这包括许多高度优化的计算内核和完整的类型支持。如果我们试图自己重新实现这一点,我们不太可能在InfluxDB 3中拥有这样的功能。
Apache Parquet
Parquet是与其他系统和InfluxDB 3进行批量数据交换的主要格式。Arrow是用作Flight的内存和跨线格式,而Parquet实现了高效的批量数据传输和与外部工具和系统(如lakehouses、Apache Iceberg和许多数据仓库)的无缝集成。
这标志着从InfluxDB作为一个独立系统向一个为互操作性而设计的系统转变。像Snowflake、Databricks和Spark这样的工具可以直接读取Parquet文件,从而可以更容易地集成,而不需要转换层。
通过采用Parquet作为支持新兴表格标准如Apache Iceberg和Delta Lake(并具有灵活性以采用其他标准,如XTable)的标准交换格式,InfluxDB 3正成为现代数据生态系统中的一级公民,构建时旨在与更广泛的数据湖和分析栈集成。
每个组件都为其特定功能精心设计,通过从坚实的基础开始,我们的团队可以将大部分精力集中在确保它们无缝协作上。它们支持了我们所需的高性能时间序列用例。实现这一点需要深入了解每个组件的能力和限制,以及工程解决方案,例如专门的时间序列优化",这些可以弥合这些差距,形成一个连贯且高效的系统。

图1:InfluxDB 3查询架构
重构的收益
选择FDAP堆栈显著提高了数据库的性能,实现了更快、更高效且灵活的架构。它在摄取效率、可扩展性、数据压缩、存储成本和高基数数据的查询性能方面带来了显著提升。通过消除基数限制,用户可以同时引入无限量的时间序列数据和针对许多时间序列的分析查询,即使数据复杂性和基数增加。
对于开发人员来说,在FDAP堆栈上构建意味着构建一个数据处理引擎,该引擎可以高效地处理查询,并管理数据存储和检索以最大化不同负载的性能。在操作上,它涉及设置一个系统,该系统可以处理并发数据读写,确保数据完整性和一致性,并提供强大的容错机制。在架构上,它是一个可以水平和垂直扩展的设计,可以无缝适应不同的负载和数据大小。
许多现代数据工具和技术可以与FDAP堆栈的各个部分集成以增强其能力。例如,需要快速查询执行的分析工具可以从DataFusion和Arrow的快速数据处理功能中受益。需要高效数据传输的系统可以利用Arrow Flight,而需要优化存储的系统可以利用Parquet。
Apache Iceberg和FIDAP堆栈
未来,我们将增加对Iceberg的支持。Iceberg为Parquet支持的数据集带来了表格级抽象和强大的元数据管理,使得可以直接从数据湖和仓库(如Databricks、Snowflake和AWS Athena)访问。通过将数据以Parquet格式写入并通过Iceberg暴露,InfluxDB 3使时间序列数据完全可查询,无需自定义连接器或转换层。
开源InfluxDB 3
有了这个基础,我们将InfluxDB 3的架构、性能和互操作性带给了开源社区。最新的InfluxDB 3 Core是一个基于Rust的、MIT/Apache 2许可的时间序列引擎,构建在与我们的商业产品相同的FDAP堆栈和无状态架构上。它专为提供开发人员强大的实时引擎以应对现代数据工作负载而设计,支持我们的企业级部署的相同创新。
InfluxDB 3 Core是一个高性能的实时数据引擎。这种专注的方法使Core能够为实时监控、数据收集和流分析用例提供卓越的性能。通过特别针对这种模式进行优化,我们实现了最后值查询的查询响应时间在10毫秒以下,小时范围查询在50毫秒以下。Core还将数据持久化到Parquet文件中,以供第三方系统长期存储和访问。
InfluxDB 3 Enterprise在Core的基础上增加了压实能力,使用户能够高效地在任何时间范围内进行查询,适用于需要历史分析的用户。这种分离允许Core保持其性能特性,而Enterprise处理管理历史数据的复杂要求。
Core和Enterprise的一个关键特性是它们的“无磁盘”架构,使用对象存储作为唯一的持久层。虽然它们可以使用本地磁盘,但它们与对象存储的无状态操作使数据能够无缝地被第三方系统访问,这些系统可以直接从对象存储中读取。
这两种产品还包括了新的处理引擎,允许用户定义Python脚本来收集、处理、转换和监控数据,直接在数据库内实时进行。插件API包括查询数据库、将数据写回数据库以及连接到Python生态系统中的任何第三方服务的能力。我们对插件系统将带来的众多可能性感到兴奋,特别是当它与快速的最近数据查询引擎和最后值缓存配对时。
InfluxDB 3.2和InfluxDB 3.3都带来了InfluxDB 3 Explorer,这是一个用于查询、探索和可视化数据的UI,以及用于处理引擎的托管插件,以处理常见的时间序列任务。最新版本InfluxDB 3.4适用于Core和Enterprise,并包括用于设置和工作流程的自动化功能。

图 2:使用WAL刷新插件的写入路径:在处理引擎中定义的插件可以拦截预写式日志事件,使得在数据刷新到对象存储之前能够进行实时处理。
可扩展设计:为现代负载设计的无状态、无磁盘架构
Core和Enterprise版引入了一种无状态、基于组件的架构,将计算与存储分离,并完全运行在对象存储上——我们称之为“无磁盘”架构。虽然仍然支持本地磁盘,但两个版本都设计为将所有状态持久化到像S3、GCS或Azure Blob这样的服务。写入操作首先进入内存,在那里进行验证和缓冲,然后作为Parquet格式刷新到对象存储以确保持久性。一个可查询的缓冲区保存了最近的数据,使得低延迟查询无需等待文件。这种设计提供了操作简便性、水平可扩展性和开箱即用的持久性,无需复杂的集群逻辑或数据复制层。

图 3:InfluxDB 3 写入路径。
这种架构的核心是模块化服务,它们处理不同的职责:
摄取器处理实时写入负载。它们缓冲传入的数据,在内存中去重,并将其作为小的Parquet文件持久化到对象存储中。查询器执行低延迟的分析查询,扫描持久化的Parquet数据或查询由摄取器在内存中保存的热数据。压缩器通过将数据重新排列成按时间序列排序的更大时间块来优化数据布局,提高读取性能并启用长期历史分析。在对象存储上实现的中央目录(跨所有组件共享)跟踪模式元数据,允许系统在保持一致性和协调的同时完全无状态。
数据通过这些组件高效流动。摄取器在写入前验证模式并将数据按时间分区。表模式在写入时自动推断(用户不需要提前定义表或列)。自定义的多列排序合并去重策略(基于DataFusion操作符)在保持写入吞吐量的同时去除重复行。
为了保持数据老化时的查询性能,压缩器在后台持续将小文件合并成更大的、不重叠的Parquet文件。这种文件合并减少了存储开销,使查询期间能够快速修剪,并避免了不必要的重新计算。压缩级别有助于平衡频率和计算成本,确保系统在规模上保持性能。
查询器同时提供最近和历史数据。它们从对象存储中提取持久化的Parquet文件,并根据需要从摄取器查询最近的内存缓冲区。查询计划使用DataFusion构建,支持SQL和InfluxQL,并从DataFusion的矢量化执行、谓词下推和对Parquet的原生支持中受益。
这种架构的一个关键特性是最后值缓存(LVC)和不同值缓存(DVC),它们是为加速熟悉的时间序列查询模式而设计的内存优化。LVC保留了可配置标签层次结构的最新值,使得“最后已知值”查询的响应时间在10毫秒以内。DVC提供了标签值的超快速查找,提高了UI响应性和探索性工作流程。
按设计,这种架构从关键路径中消除了本地状态,简化了升级,实现了动态扩展,并允许组件独立恢复。这种关注点分离还使得企业部署中的工作负载隔离成为可能,其中摄取、查询、处理和压缩组件可以分别进行扩展和调度。
综上所述,这些能力使得InfluxDB 3 Core可以作为一个轻量级、针对最近数据优化的实时分析引擎运行。由于这些性能特性,我们设置了一个配置选项,将查询计划限制为432个Parquet文件,对应于72小时的时间范围,时间块是十分钟。Enterprise版取消了这个限制,因此提供了长期查询性能、高可用性和高级多节点部署功能。这在Enterprise版中是可能的,因为它的压缩器将十分钟的文件重写为更大的时间块。
结语
包括Parquet和DataFusion在内的Apache Arrow生态系统将作为未来OLAP和大规模数据处理系统的基础。除了 nfluxDB 3 Core,我们还在这些标准上投入我们的开源工作——有时领导这些上游项目——以便社区继续增长,Apache Arrow项目集变得更易用,增加了新的功能和特性。我们获得了更可靠的软件,因为它已经在各种环境和用例中得到了验证。这就是开源的力量——一种封闭的专有系统根本无法匹敌的成熟度和韧性水平。
这标志着一个为期四年半的努力达到了高潮,我们从头开始重新构建系统,以满足现代时间序列工作负载的需求。有了InfluxDB 3,我们建立了一个基础,它可以处理所有形式的时间序列、观测和历史数据,并且具备我们的用户所需的规模、性能和灵活性。这不仅仅是为了解决今天的挑战,而是为了确保InfluxDB是为了解决开发者今天正在解决的问题以及他们接下来将面临的问题而构建的。
原文链接:
Engineering a Time Series Database Using Open Source: Rebuilding InfluxDB 3 in Apache Arrow and Rust"