在这篇文章中,我们将介绍Elasticsearch在时间序列数据存储方面的重大改进,并提供关于存储效率的性能预期。
背景
Elasticsearch最近在存储和查询时间序列数据方面进行了大量投资,重点是提高存储效率。通过多个项目的努力,相比标准索引,存储节省可高达60-80%。在某些场景下,我们的系统每个数据点的存储效率甚至可以低于一个字节,与最先进的专用时间序列数据库(TSDB)系统竞争。让我们来看看最近在时间序列数据存储效率方面的改进。
Elasticsearch 时间序列数据的存储改进
合成源(synthetic _source)
默认情况下,Elasticsearch将原始JSON文档主体存储在_source字段中。这种重复存储对于度量指标来说效果不佳,因为度量指标通常通过聚合查询进行检查,而不会使用这个字段。为了解决这个问题,我们引入了synthetic _source,它在需要时根据存储在文档字段中的数据重建原始_source的简化版本。虽然支持的字段类型有限,并且合成_source的速度较慢,但这些限制对主要依赖关键字、数值、布尔值和IP字段以及使用聚合查询的度量数据集影响不大。我们正在努力消除这些限制,使合成源适用于任何映射。
启用合成源后,时间序列数据流(TSDS)索引的大小减少了40-60%。因此,自从TSDS发布(v.8.7)以来,合成源默认启用。
专用编解码器
TSDB系统广泛使用专用编解码器,通过利用记录度量指标的时间顺序来减少每个数据点的字节数。我们的系统扩展了标准的Lucene编解码器,支持运行长度编码、delta-of-deltas(二阶导数)、最大公约数和XOR编码。编解码器在Lucene段级别指定,因此旧索引可以在索引新数据时利用最新的编解码器。
为了提高这些压缩技术的效率,索引按所有维度字段(升序)计算的标识符排序,然后按时间戳(降序)排序。这种方式使得维度字段(主要是关键字)可以通过运行长度编码有效压缩,而度量指标的数值按时间序列聚类并按时间排序。由于大多数时间序列随时间变化缓慢,只有偶尔出现峰值,Elasticsearch依靠Lucene的垂直分区存储引擎,这种方法最大限度地减少了连续存储数据之间的差异,提高了存储效率。
元数据修剪
_id
字段是用于唯一标识每个文档的元数据字段,对度量应用的价值有限,因为时间序列分析依赖于聚合查询而不是检查单个度量值。为此,TSDS修剪存储的值但保留倒排索引以支持文档检索查询。这导致了10-20%的存储减少,没有功能损失。
生命周期集成
TSDS可以与数据生命周期管理机制集成,如ILM和数据流生命周期。这些工具自动删除旧索引,而ILM还支持将索引移动到更便宜的存储层(例如使用机械硬盘或归档云存储)以降低存储成本,同时不影响常用度量指标的查询性能,并且用户参与最少。
降采样
在许多度量应用中,短期内保持细粒度数据(例如过去一周的每分钟数据)是可取的,而对于旧数据则可以增加粒度以节省存储(例如过去一个月的每小时数据,过去两年的每日数据)。降采样用预聚合度量指标的统计表示替换原始度量数据。这不仅提高了存储效率,因为降采样索引的大小仅为原始度量索引的一小部分,还提高了查询性能,因为聚合查询扫描的是预聚合结果而不是即时计算原始数据。
降采样与ILM和DSL集成,自动应用降采样并允许随着数据老化使用不同分辨率的降采样数据。
TSDS存储效率的测试结果
TSDS存储收益
我们通过夜间基准测试跟踪TSDS的性能,包括存储使用和效率。TSDB轨迹(请参阅磁盘使用可视化)显示了我们存储改进的影响。接下来,我们将展示TSDS发布前的存储使用情况、TSDS发布时的改进情况以及当前状态。
TSDB轨迹的数据集(k8s度量)有九个维度字段,每个文档平均包含33个字段(度量和维度)。索引包含一天的度量,共116,633,696个文档。
在ES 8.7版本之前,索引TSDB轨迹的数据集需要56.9GB的存储。按元数据字段、时间戳字段、维度字段和度量字段细分存储使用情况如下:
字段名称 | 百分比 |
---|---|
_id | 5.1% |
_seq_no | 1.4% |
_source | 78.0% |
@timestamp | 1.31% |
维度字段 | 2.4% |
度量字段 | 5.1% |
其他字段 | 9.8% |
_source元数据字段是存储占用的最大贡献者。正如前面提到的,合成源是我们度量工作中为了提高存储效率而引入的改进之一。这在使用合成源作为TSDS默认配置的ES 8.7版本中得到了体现。在这种情况下,存储占用下降到6.5GB,实现了8.75倍的存储效率提升。按字段类型细分如下:
字段名称 | 百分比 |
---|---|
_id | 18.7% |
_seq_no | 14.1% |
@timestamp | 12.6% |
维度字段 | 3.6% |
度量字段 | 12.0% |
其他字段 | 50.4% |
这种改进得益于不再存储_source字段,并且通过索引排序将同一时间序列的度量顺序存储,从而提高了标准Lucene编解码器的效率。
使用ES 8.13.4版本索引TSDB轨迹的数据集占用4.5GB存储,进一步提高了44%。按字段类型细分如下:
字段名称 | 百分比 |
---|---|
_id | 12.2% |
_seq_no | 20.6% |
@timestamp | 14.0% |
维度字段 | 1.6% |
度量字段 | 6.7% |
其他字段 | 58.6% |
这是一个显著的改进,与8.7.0版本相比,主要贡献因素是_id字段占用的存储空间减少(其存储值被修剪),而维度字段和其他数值字段通过最新的时间序列编解码器更高效地压缩。
大多数存储现在归因于“其他字段”,即提供类似于维度的上下文字段,但不用于计算用于索引排序的标识符,因此其压缩效率不如维度字段。
降采样存储收益
降采样通过牺牲查询分辨率来换取存储收益,具体取决于降采样间隔。对TSDB轨迹的数据集(每10秒收集一次度量)进行1分钟间隔的降采样,生成的索引大小为748MB,提升了6倍。缺点是度量按分钟粒度预聚合,因此无法检查单个度量记录或在小于分钟的时间间隔(例如每5秒)进行聚合。然而,预计算统计数据(最小值、最大值、总和、计数、平均值)的聚合结果与原始数据计算的结果相同,因此降采样不会影响准确性。
如果可以接受更低的分辨率,并且度量按小时间隔进行降采样,生成的降采样索引将仅占用56MB存储。需要注意的是,这种改进是13.3倍,即低于预期的60倍。这是因为所有索引都需要存储每个段的额外元数据,这是一个常数开销,随着索引大小减少而变得更加显著。
总结
下图显示了各个版本存储效率的演变,以及降采样提供的额外节省。请注意,纵轴是对数刻度。
总体来说,过去的版本使我们的度量服务的存储效率提高了12.5倍。如果通过降采样以降低存储占用,这一数值可以达到1000倍或更高。
TSDS的配置建议
在本节中,我们探讨了为了提高存储效率配置TSDS的最佳实践。
每个文档包含多个度量
虽然Elasticsearch使用垂直分区分别存储每个字段,但字段仍逻辑上分组在文档中。由于度量共享维度,因此在每个索引文档中包含尽可能多的度量可以更好地摊销维度和元数据的存储开销。相反,每个文档只存储一个度量以及其相关维度,会最大化维度和元数据的开销,从而膨胀存储。
具体来说,我们使用合成数据集量化了每个文档包含度量数量的影响。当我们在每个索引文档中包含所有度量(20个)时,TSDS的存储使用量仅为每个数据点0.9字节,接近最先进的专用度量系统(每个数据点0.7字节)的性能。相反,每个索引文档只有一个度量时,TSDS需要每个数据点20字节,存储占用显著增加。因此,将尽可能多的度量组合在每个索引文档中并共享相同的维度值是值得的。
修剪不必要的维度
Elasticsearch架构使我们的度量服务在每个度量的时间序列数量(即维度基数的乘积)高达数百万或更多时,具有可管理的性能成本,远远超过竞争系统。然而,维度确实占用相当大的空间,高基数会降低我们的TSDS压缩技术的效率。因此,重要的是仔细考虑在度量索引文档中包含哪些字段,并积极修剪到仪表盘和故障排除所需的最少维度集。
一个有趣的例子是一个包含IP字段的可观察性映射,发现该字段包含最多16个IP(IPv4、IPv6)地址,严重影响了存储占用和索引吞吐量,而几乎不使用。用机器标签替换它后,存储改进显著,没有丧失调试性。
使用生命周期管理
ILM可以将旧的、不常访问的数据移动到更便宜的存储选项,并且ILM和数据流生命周期可以处理随着数据老化而删除度量数据。这种全自动方法减少了存储成本,无需更改索引映射或配置,因此强烈推荐。
此外,值得考虑通过降采样以换取存储来处理数据老化。这种技术既能显著节省存储空间,又能使仪表盘更响应,只要对于旧数据的分辨率降低是可以接受的——在实际应用中很常见,因为很少有人会以每分钟的粒度检查几个月前的数据。
下一步
过去几年中,我们在度量存储占用方面取得了显著改进。我们打算将这些优化应用于度量以外的数据类型,特别是日志数据。虽然某些功能是度量特有的,如降采样,但我们仍希望通过日志特定的索引配置实现2-4倍的存储减少。
尽管减少了所有Elasticsearch索引所需的元数据字段的存储开销,我们计划更加积极地修剪这些字段。好的候选字段有_id和_seq_no。此外,还有机会对时间戳和支持范围查询的其他字段应用更高级的索引技术,如稀疏索引。
降采样机制在提高查询性能方面具有巨大潜力,只要可以接受小的存储代价。一个想法是支持多个降采样分辨率(例如原始数据、每小时和每日)在重叠的时间段上,查询引擎自动选择每个查询最适合的分辨率。这将允许用户根据仪表盘时间缩放来指定降采样,使其更响应,并且在索引后几分钟内启动降采样。它还可以解锁保留原始数据和降采样数据,可能使用更慢/更便宜的存储层。