我们是如何做到的:InfluxDB 3.0 数据摄取和压缩收益
作者:Rick Spencer / 产品,公司
2023年10月4日
导航到
几周前,我们发布了一些 性能测试,这些测试显示了 InfluxDB 3.0 在性能上的提升,比 InfluxDB 以前的版本——以及其他数据库——提高了几个数量级。有两个关键因素影响了这些提升:1. 数据摄取,2. 数据压缩。这就引出了一个问题,我们是如何在我们的核心数据库中实现如此大的改进的?
本文旨在解释我们是如何实现这些改进的,供有兴趣的人士了解。
TSM 回顾
要了解我们的最终目标,了解我们的起点非常重要。推动 InfluxDB 1.x 和 2.x 的存储引擎是我们所说的 InfluxDB 时间结构合并树(TSM)。让我们简要地看看 TSM 中的数据摄取和压缩。
TSM 数据模型
有关 TSM 数据模型的信息有很多,但如果你想快速了解,请查看 TSM 文档中的信息这里。以此为前提,让我们将注意力转向数据摄取。
TSM 摄取
TSM 使用倒排索引将元数据映射到磁盘上的特定系列。如果写操作添加了新的测量或标签键/值对,则 TSM 更新倒排索引。TSM 需要将这些数据写入磁盘的正确位置,基本上是在写入时进行索引。整个过程需要大量的 CPU 和 RAM。
TSM 引擎按时间分片文件,这使得数据库能够执行保留策略,移除过期的数据。
TSI 的引入
在 2017 年之前,InfluxDB 在启动时计算倒排索引,在写入期间维护它,并保持在内存中。这导致了非常长的启动时间,因此 2017 年秋季,我们引入了时间序列索引(TSI),它本质上是一个持久化到磁盘的倒排索引。然而,这也带来了另一个挑战,因为 TSI 在磁盘上的大小可能变得非常大,尤其是在高 基数 用例中。
TSM 压缩
时间序列数据库(TSM)使用行程长度编码进行压缩。这种方法对于数据时间戳在固定间隔发生的情况下的度量用例非常高效。InfluxDB能够存储开始时间和时间间隔,然后在查询时根据行数计算每个时间戳。此外,TSM引擎还可以对实际字段数据进行行程长度编码。因此,在数据变化不频繁且时间戳规律的情况下,InfluxDB可以非常高效地压缩数据。
然而,时间戳间隔不规则或数据几乎每次读取都发生变化的用例会降低压缩效果。在高度基数用例中,TSI会进一步复杂化,因为它可能变得非常大。这意味着InfluxDB达到了实际压缩限制。
InfluxDB 3.0的引入
当我们着手设计InfluxDB 3.0时,我们决心解决与摄入效率和压缩相关的这些限制,并消除基数限制,以使InfluxDB适用于更广泛的时序用例。
3.0数据模型
我们从重新思考数据模型开始。虽然我们保留了将数据分离到数据库中的概念,而不是持久化时间序列,但InfluxDB 3.0通过表来持久化数据。在3.0的世界里,“表”与InfluxDB 1.x和2.x中的“度量”相对应。
InfluxDB 3.0按照每天在磁盘上对每个表进行分片,并以Parquet文件格式持久化这些数据。在磁盘上可视化这些数据看起来像一系列Parquet文件。数据库的默认行为每十五分钟生成一个新的Parquet文件。之后,压缩过程可以将这些文件合并成更大的文件,其中每个文件代表单个度量的某一天的数据。需要注意的是,我们将每个Parquet文件的大小限制在100兆字节,因此重用户可能每天有多个文件。
InfluxDB 3.0的Parquet文件分区和数据模型示例
InfluxDB 3.0保留了标签和字段的概念;然而,在3.0中它们扮演着不同的角色。一组唯一的标签值和时间戳标识了一行,这使InfluxDB能够在写入时通过UPSERT更新它。
现在我们已经简要介绍了新的数据模型,让我们转向它是如何使我们能够提高数据摄入效率和压缩的。
替代分区选项
我们设计了InfluxDB 3.0以执行分析查询(即跨大量行进行汇总的查询),并针对此目的优化了默认分区方案。然而,用户可能总是需要查询某些度量的标签值子集。例如,如果每个查询都包含特定的客户ID或传感器ID。由于它的索引数据和持久化到磁盘的方式,TSM处理此类查询非常好。但InfluxDB 3.0并非如此,因此使用默认分区方案的用户可能会在这些特定查询中遇到性能下降。
InfluxDB 3.0中的解决方案是自定义分区。自定义分区允许用户根据标签键和标签值定义分区方案,并为每个分区配置时间范围。这种方法使用户能够在InfluxDB 3.0中实现类似查询性能,同时保留其摄入和压缩优势。
InfluxDB 3.0摄入路径
与之前的版本相比,InfluxDB 3.0 的数据模型更简单,因为每个测量将所有数据组合在一起,而不是按系列分开。这简化了数据摄取过程。当 InfluxDB 3.0 处理写入时,它验证模式,然后在内存中为该测量的任何其他数据找到结构。然后数据库将数据附加到测量,并向用户返回成功或失败。同时,它将数据附加到预写日志(WAL)。我们称这些内存结构为“可变批次”或“缓冲区”,原因将在下文清楚说明。
与其他数据库相比,包括 InfluxDB 1.x 和 2.x,这个过程需要更少的计算资源,因为与其他数据库不同
- 摄取过程不会对数据进行排序或以其他方式排序;它只是简单地附加它。
- 摄取过程将数据去重延迟到持久化。
- 摄取过程使用缓冲树作为索引。这个索引存在于每个特定的摄取实例中,并标识出特定查询所需的数据。我们能够通过分层/分片锁定和引用计数来消除竞争,使得这个缓冲树具有极高的性能。
当可变批次耗尽内存时,InfluxDB 将缓冲区中的数据持久化为对象存储中的 Parquet 文件。默认情况下,如果内存缓冲区未被填满,InfluxDB 会每 15 分钟将所有数据持久化为 Parquet。这是 InfluxDB 3.0 对数据进行排序和去重的点。将这项工作从“热/写入路径”中推迟,可以保持延迟和方差较低。
以这种方式简化摄取过程意味着数据库在热/写入路径上做的工作更少。结果是,将昂贵的操作移动到持久时间,因此总体上需要最少的 CPU 和 RAM,即使是对于重大的写入负载。
完整性方面,还有一个定期刷新的写前日志(WAL),确保所有写入都是立即持久的,并在容器失败的情况下用于重新构成可变批次。InfluxDB 3.0 只在非干净关闭或崩溃情况下重新播放 WAL 文件。在“幸福路径”(例如,升级)中,系统会优雅地停止,刷新(即,持久化)所有缓冲数据到对象存储,并删除所有 WAL 条目。因此,启动速度快,因为 InfluxDB 不必重新播放 WAL。此外,在非复制部署中,本来会坐在离线节点 WAL 磁盘上的数据实际上在对象存储中,并且可读,保留了读取可用性。
领先边缘查询
我们还优化了 InfluxDB 3.0 以查询数据的前沿。大多数查询,特别是时间敏感的查询,都查询最近写入的数据。我们称这些为“前沿”查询。
当查询到来时,InfluxDB 将数据转换为 Arrow,然后由 DataFusion 查询引擎查询。在所有被查询的数据已经存在于可变批次中的情况下,摄取器以箭头格式向查询者提供数据,然后 DataFusion 快速执行任何必要的排序和去重,然后再将数据返回给用户。
在部分或全部数据不在读取缓冲区中的情况下,InfluxDB 3.0 使用其存储在快速关系型数据库中的 Parquet 文件目录来找到需要加载到内存中以箭头格式读取的特定文件和行。一旦加载到内存中,DataFusion 就能够快速查询这些数据。
压缩
一系列压缩过程有助于维护数据目录。这些过程通过排序和去重优化存储的 Parquet 文件,从而提高在磁盘上查找和读取 Parquet 文件的高效性。
数据压缩
我们选择Apache Arrow生态系统,包括Parquet,用于InfluxDB 3.0的原因有几个。这些格式从一开始就是为了支持在大数据集上进行高性能分析查询而设计的。因为它们是为列式数据结构设计的,所以也能实现显著的压缩。我们自豪地成为Apache Arrow社区的一员,并为这些技术做出贡献,该社区持续改进这些技术。
Parquet文件格式
因此,由于InfluxDB 3.0从Arrow的列式结构开始,它继承了显著的压缩优势。使用Parquet会进一步增强这些优势。此外,由于InfluxDB是一个时序数据库,我们可以对时序数据进行一些假设,从而最大限度地利用这些压缩技术。
例如,假设在InfluxDB 3.0中保留标签键/值对的概念意味着对这些列的字典编码会产生最佳的压缩。在这种情况下,字典编码为每个标签值分配一个数字。这个数字在磁盘上占用很少的字节。然后,Parquet可以对每个标签键列运行长度编码这些数字。在此基础上编码方案,InfluxDB可以应用通用压缩(例如gzip、zstd)以进一步压缩数据。
Arrow和Parquet的整体组合导致了显著的压缩增益。当将这些增益与InfluxDB 3.0依赖于对象存储进行历史数据的事实相结合时,用户可以以更少的成本存储更多的数据。
总结
希望您觉得这个关于InfluxDB 3.0如何实现如此令人印象深刻的摄取效率和压缩的解释很有趣。结合计算和存储的分离,客户可以相对于其他数据库(包括InfluxDB 1.x和2.x)实现显著的总拥有成本优势!
试用InfluxDB 3.0看看这些性能和压缩增益如何影响您的应用程序。