ES95:面向 Elasticsearch 时间序列指标的自适应压缩

作者:来自 Elastic Salvatore Campagna

ES95 是 Elasticsearch 9.5 新推出的自适应时间序列编解码器,可将 @timestamp 的存储空间减少 92%,并将浮点数字段的存储空间最多减少 74%,无需任何配置。

最好的压缩策略,是能够理解你的数据的压缩策略。

可观测性工作负载天生就非常消耗存储,而存储数据的构成会同时决定成本和查询性能。ES95 引入了自适应压缩:它不会对每个数值字段都应用相同的编码方式,而是会自动选择最匹配每个字段结构的编码方式。结果是,doc-values 的总存储空间减少 33.6%,浮点型仪表指标的存储空间减少 19% 到 74%,@timestamp 的存储空间减少 92%。无需任何配置或迁移。

可观测性数据非常消耗存储

数据处理系统很少受限于计算速度。它们受限于数据字节的传输速度:从磁盘读取数据、在网络中传输数据,以及在内存层次结构中移动数据。压缩是存储引擎用 CPU 时间换取内存带宽的一种方式,通过消耗相对廉价的 CPU 周期,让更少的字节通过系统中通常受限的部分。在像 Elasticsearch 这样读密集型的系统中,这种权衡会在每次查询数据时获得回报,而且通常是在数据写入很久之后。

存储大小和查询性能是相互关联的;磁盘上的字节越少,每次范围查询、每次聚合以及每次仪表板加载需要读取的字节就越少。压缩不仅仅是为了节省存储空间。每一个从未被写入的字节,也意味着它永远不需要被读取。

正确的编码方式取决于数值本身的结构,而最大的收益来自利用数据中已经存在的结构,而不是对一串不透明的字节流进行压缩。很少有工作负载能够像可观测性指标一样清晰地展现这种结构。

单个主机每隔几秒就会上报数百个指标,包括 CPU 使用率、内存比例、请求延迟和网络吞吐量。将其扩展到数千台主机,并持续保留数周,数据字节会迅速累积。大部分数据具有结构,但并不统一:来自数千个并发时间序列的时间戳以近乎恒定的间隔到达,计数器单调递增,而 23.471.15 这样的仪表值则是短小的十进制测量值。

固定的压缩方式无法适应这种多样性。时间戳列和浮点型仪表列需要通过本质上不同的技术进行压缩,但如果编解码器对两者采用相同的方法,就必然会有其中一种处理得不够好。在 Elasticsearch 大部分时间序列编解码器的发展历史中,仪表值一直是这种权衡中处于劣势的一方。

旧编解码器无法利用的数据结构

Elasticsearch 将时间序列数值存储在 doc values 中:这是一种面向列的结构,同一字段的所有值在磁盘上彼此相邻。这种相邻性使压缩成为可能:编解码器会比较同一字段的连续值,寻找其中的模式,并加以利用。

图 1:面向列的存储会将同一字段的所有值相邻存储在磁盘上。

ES95 之前的时间序列编解码器对每个数值字段都应用相同的固定编码方式:先进行增量编码,然后依次进行归一化、GCD(最大公约数)归约和位打包。每种编码技术都会在能够发挥作用时启用,在无法带来收益时跳过。对于时间戳和整数计数器,这种方式非常有效。对于浮点型仪表值,它几乎找不到任何可以利用的规律。

原因在于它们的存储方式。为了支持范围查询,Elasticsearch 会将浮点值存储为能够保留数值顺序的整数。CPU 百分比读数变化 0.01,会转换为整数空间中数万亿级别的跳跃。编解码器看到这些巨大的跳跃,却没有进一步缩减其存储空间的策略。每个值的存储空间仍接近原始的 8 个字节。

对于它所获得的表示方式而言,编解码器所做的是正确的,但这种表示方式最初是为了查询而选择的,而不是为了压缩,而这两个目标在位级别上存在冲突。

固定格式的代价

针对浮点值的压缩阶段早已在路线图中,因此有趣的地方并不在于算法,而在于架构。

之前的编解码器将其压缩方式固化在存储格式中。添加新的编码方式意味着改变磁盘上现有字节的含义,这就迫使用户进行格式迁移,而在大型生产集群中,这种迁移可能持续数周甚至数月。随着时间推移,这种迁移负担本身会限制编解码器的发展。问题不再是 这是一个好的压缩方案吗?,而变成了 它值得再次进行格式迁移吗? 这种僵化限制了编解码器演进的节奏,也导致许多压缩优化无法落地。

无需配置即可选择正确的编码方式

ES95 从架构层面解决了时间序列索引中的这一问题。每个字段的编码方式不再固化在存储格式中,而是在写入时根据字段映射中已经声明的信息自动选择:字段名称、字段数据类型以及指标角色。时间戳使用不同于计数器的编码方式。计数器使用不同于仪表值的编码方式。ES95 会对所有这些数据进行编码,并为每一种数据选择正确的策略。

用户已经将编解码器需要了解的所有信息告诉了 Elasticsearch。映射描述数据;编解码器选择压缩策略。

压缩策略是编解码器需要考虑的问题,而不是用户需要考虑的问题。

另一种方案是将每个字段的编码方式选择作为配置参数公开,让用户可以针对特定字段选择更好的压缩方式。但这样会把判断哪种编码方式适合哪种数据类型的负担交给最不适合做出这种判断的人,并且会导致大多数部署永远无法享受到这种收益。ES95 将这一决策保留在编解码器内部,这才是它应该存在的地方。这一点在托管和无服务器部署中尤为重要,因为用户希望系统能够自动做出最佳的存储决策。

无人计划的时间戳结果

采用自适应架构后,团队开始着手发布原计划中的浮点数压缩算法。在该算法实现之前,这套架构已经通过显著改善时间戳的压缩效果证明了自身的价值。

时间序列索引首先按照其时间序列标识符(由指标维度构成的 _tsid)排序,然后在每个时间序列内部按照时间戳排序。磁盘上的时间戳并不是一条平滑的连续序列;而是由许多条首尾相接的平滑序列组成,每个时间序列对应一条序列,而每当一个时间序列结束、下一个时间序列开始时,就会出现一次较大的跳跃。

编解码器会以固定大小的数据块进行压缩,而不会考虑这些时间序列的边界。跨越时间序列边界的数据块会包含来自两个不同时间序列的时间戳。两者之间的跳跃会破坏单调性,从而降低跨越不同时间序列的数据块中的增量编码效果。一个原本每个值几乎可以压缩到零比特的数据块,最终却需要 9 个或更多比特,因为位打包会使用相同的固定比特数对数据块中的每个值进行编码,所以一个较大的跳跃会决定整个数据块的成本。

图 2:SplitDelta 通过在时间序列边界处分割数据块,保留单调序列。

在理想情况下,每个数据块都属于单个时间序列:时间戳以近乎恒定的间隔递增,增量编码能够捕捉这种规律性,而位打包则将结果压缩到每个值接近零比特。时间序列较少时,边界数据块很少,其开销几乎可以忽略。但在一个每秒写入数百万文档、涵盖数千个时间序列的可观测性集群中,情况就不同了。成本会沿着两个维度增长:时间序列数量和数据密度。时间序列越多,边界事件就越多;时间序列越稀疏,单个数据块中就越可能包含多个跳跃。在高频变化的环境中,这两种情况会叠加,使边界数据块不断累积,成为任何时间序列工作负载中读取次数最多的字段所承担的一项持续成本。这就是为什么 @timestamp 的存储增长速度超过了产生这些数据的数据量。

解决方法是检测时间序列边界,并将每个连续序列作为独立序列进行处理。与其尝试跨越两个时间序列之间的跳跃进行编码,让数据块中的每个值都承担这个巨大跳跃所带来的存储成本,不如分别按照各自的特征对每个连续序列进行压缩。边界只是变成了一条接缝:编码器在边界两侧都永远不会看到这个跳跃。

这种编码方式称为 SplitDelta。现在,@timestamp 和单调递增的 long 计数器默认使用它。不需要改变格式,也不需要迁移。现有的 segment 会继续保留旧版编码。

在高基数的 Elasticsearch Rally 基准测试中,这一项原本未计划的编码就将计数器的存储空间减少了 20%–30%,并将 @timestamp 的存储空间减少了 92.4%,从 1.03 GB 降至 79 MB。从 GB 级降到 MB 级,而且没错,这不是笔误。

可插拔的处理流水线已经证明了自身的价值。在 ALP 正式发布之前,SplitDelta 这个原本不在计划中的编码方式就已经能够在不改变格式或进行迁移的情况下直接接入。

ALP:恢复一直存在的十进制结构

大多数浮点指标都可以表示为不会损失精度的短小十进制数:例如 CPU 使用率为 23.47,平均负载为 1.15ALP(自适应无损浮点压缩)会从浮点表示中恢复这种十进制结构,将数值转换为整数,而现有的处理流水线已经能够很好地处理这些整数。ES95 会将 ALP 的输出送入与时间戳和计数器相同的成熟整数压缩流水线,而不是将 ALP 作为独立的编码方式,从而获得额外的存储节省。无法适配 ALP 模型的值(例如不规则的高精度浮点数或特殊值)会回退到直接位打包或原始表示,而不会降低数据块中其他值的压缩效果。

图 3:ALP 从浮点值中恢复十进制结构,使其能够作为整数进行压缩。

ALP 让 Elasticsearch 能够根据浮点指标实际包含的数据结构来处理它们,而不是根据它们所采用的二进制表示方式来处理。它会自动应用于 double 类型的仪表字段,并根据字段类型和指标角色进行选择,通过的正是这套架构为它构建的入口。

数据说明了什么

以下是 Elasticsearch Rally 基准测试在一个包含 22.6 亿个数据点的高基数工作负载上的表现。结果来自内部的 tsdb-metricsgen 基准测试。

字段或指标存储空间减少(%)
@timestamp−92.4%
cpu.load_average.5m−74.3%
system.cpu.utilization−63%
memory.utilization−19%
doc values−33.6%

整体减少 33.6% 的结果需要结合背景来看。

时间序列索引包含的不只是指标。每个数据点还带有用于标识时间序列的标签:主机名称、IP 地址、区域、容器 ID。这些维度字段以 keyword 的形式存储。ES95 并不针对维度字段。

在这项基准测试中,host.iphost.mac 两个维度字段在 ES95 运行后占据了 doc values 存储空间的 44%。33.6% 的总体减少幅度反映的正是这种数据构成。按字段分别来看才是更真实的情况。维度字段的压缩仍然是一个正在积极推进的工作方向。

不同字段之间的差异是最有说服力的结果。有些仪表值的存储空间减少了将近四分之三,而另一些减少幅度还不到五分之一。这种差异直接证明了 ES95 会根据每个字段实际存在的数据结构来匹配压缩方式。固定编码会以完全相同的方式处理每个字段,从而错过其中的大部分压缩收益。

无需额外配置即可获得更好的压缩

SplitDeltaALP 带来的存储空间减少,是 ES95 最直观的成果。而更重要的成果,是产生这些优化的架构。

ES95 之前,每一种新的压缩技术都需要进行存储格式演进。这一现实决定了哪些想法值得实际推进。如今,新的编码方式成为编解码器内部的实现决策,而不再是迁移项目。现有数据永远不需要迁移,用户只需升级 Elasticsearch,新写入的数据就能自动获得更好的压缩效果。SplitDeltaALP 是首批从这一架构中受益的编码方式,但不会是最后一批。

让用户选择压缩算法,只会重复 Elasticsearch 已经掌握的信息。不需要调整任何按字段设置的压缩参数,也不需要专业知识就能获得良好的存储效率。不同字段采用不同的策略,是因为 ES95 理解每个字段包含什么类型的数据,而不是因为用户进行了配置。随着编解码器不断演进,这些决策也会随之演进,而 API 无需改变。

在 Elasticsearch Serverless 中,良好的默认设置是产品的一部分。用户期望由系统而不是配置来做出存储决策。ES95 的设计正是为了满足这一期望:从一开始就选择正确的编码方式,并随着时间推移不断变得更好。

压缩一直就存在于数据之中

ES95 为时间序列编解码器的演进方式建立了新的标准。新的编码方式成为实现层面的决策,而不再是迁移项目。用户每次升级 Elasticsearch,都能让新写入的数据获得更好的压缩效果。

压缩本来就存在于数据之中。ES95 只是移除了隐藏它的东西。

原文:Time series database compression: 92% smaller timestamps | Elasticsearch Labs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值