比 Prometheus 快 30x:我们如何将 Elasticsearch 重建为领先的列式 metrics 存储

11 阅读24分钟

作者:来自 Elastic Kostas KrikellasMartijn Van GroningenNhat Nguyen 及 Felix Barnsteiner

Elasticsearch 现在以每个数据点 3.75 字节的成本存储 OTel 指标,并且查询速度比 Prometheus 快最多 30 倍。以下是我们如何重建 TSDS 和 ES|QL 的过程。

Elasticsearch 现在以每个数据点 3.75 字节的成本存储 OTel 指标 —— 相比一年前的 25 字节大幅下降 —— 并且在查询上相比 PrometheusMimirClickHouse 最多快 30 倍,同时存储效率最高提升 2.5 倍。这些提升来自于我们将 TSDS 存储和 ES|QL 计算 引擎重建为完全列式的 metrics 引擎,并在此过程中加入原生 OTel ingestion —— 同时仍然保持 Elasticsearch 存储和查询日志、trace 以及其他任意数据的能力。

8.7 版本起,Elasticsearch 已支持在 time -series data streams(TSDS)中存储 metrics。该能力主要聚焦于存储优化,如早期博客所述。但在 indexing throughput 、存储效率和查询延迟方面,它仍未达到专门用于 metrics 存储与查询的系统水平。

在过去一年中,我们重新审视了存储层,优化了 OTel metrics 的 ingestion,并为 ES|QL 计算引擎增加了针对 time series 数据的向量化处理能力。这些改进带来了显著性能提升,相比早期 TSDS 版本:

  • 在 OTel metrics 上,存储效率最高提升 6.6x,达到每个数据点 3.75 bytes
  • OTel 数据 indexing throughput 提升最高 50%
  • 查询延迟最高提升 160x,包括快速的 counter rate 计算和 time series aggregation 的 window 支持

因此,Elasticsearch 已成为领先的列式 metrics 引擎,在索引 吞吐量 上与 PrometheusMimirClickHouse 等竞品持平甚至更优,在存储效率上最高领先 2.5x,在查询性能上最高领先 30x。同时,它仍然保留对 logs 和其他数据的统一存储能力,并可以完整使用 ES|QL 的丰富查询能力(例如 inline statslookup join)—— 而这些是基于 PromQL 的系统所不具备的。因此,Elasticsearch 可以作为一个统一的存储与查询引擎覆盖所有用户数据,在 metrics 和可观测性场景中实现零妥协。

TSDS 如何组织

TSDS 具有以下特性,这些特性有助于提升 time-series 编解码器的性能,并在按 time series 聚合数据点时得到正确结果:

  • metric name 以及 dimension 的名称和值用于计算 _tsid,这是每条 time series 的唯一标识符。
  • TSDS 按 [_tsid 升序,timestamp 降序] 进行排序。因此,每条 time series 都会在磁盘上连续存储,且较新的数据点会优先出现。由于 _tsid 是基于维度值计算的,这些维度在磁盘上也会被聚集在一起。
  • shard routing 基于 _tsid,因此每个 _tsid 的值只会出现在一个 shard 中。
  • backing indices 是按时间划分的,并且在时间上彼此不重叠。

本文接下来将说明我们如何利用这些特性来提升存储、索引以及查询性能。

存储优化

TSDS 已经实现了非常有竞争力的存储占用,在能够将多个 metrics 合并到同一个 document、并共享相同维度值的情况下,可以达到每个数据点 0.9 bytes。然而,当大多数数据点具有独立的维度集合(这在 OTel 或 Prometheus metrics 中是典型情况)时,document 往往只包含一个数据点。在这种模式下,存储开销会上升到每个数据点 25 bytes,而专用 metrics 存储系统则可以做到低于 10 bytes per data point。

为了进一步降低存储占用,在过去一年中我们应用了一系列优化:

用 doc value skippers 替代 inverted indices 和 BKD trees

Elasticsearch 默认会为所有非 metric 字段创建 inverted indices(用于文本值)或 BKD trees(用于数值字段),例如 @timestamp 和 dimensions。这些索引可以提升基于这些字段过滤的查询性能,但会显著增加存储开销——几乎会让每个字段的存储翻倍。此外,它们还会在 segment merging 过程中被处理,从而增加 CPU、内存和存储开销,并降低系统吞吐能力——在高写入吞吐的 metrics 场景中尤其明显。

Lucene 引入了 doc value skippers,这是一种层级化的稀疏索引结构,它只存储一组文档块的最小值和最大值。范围查询可以利用这些 min/max 信息跳过不符合条件的文档块。skippers 在字段已排序的情况下效果尤其好。由于 TSDS 按 [_tsid, timestamp desc] 排序,维度值在磁盘上也会被聚集存储。因此可以用 doc value skippers 替代 @timestamp 和 dimension 字段上的索引,从而强化列式布局 —— 每个字段独立存储在自己的文件中,并且不需要为索引目的重复追踪每个文档。

doc value skippers 几乎没有存储开销 —— 用它们替代传统索引,在 OTel 场景中将每个数据点的存储减少了约 10 bytes(从 25 bytes 中扣除)。此外,在包含时间范围过滤或维度过滤(包括 prefix 和 regex)的查询中,它们表现良好——在我们的基准测试中,用 skippers 替代独立索引后,查询性能没有明显下降。自 9.3 版本起,TSDS 默认启用 doc value skippers。

启用合成 ID(synthetic ids)

_metadata 字段 _id 也是导致存储开销较大的一个因素。TSDS 已经扩展为在复制不再需要这些 doc values 时对其进行裁剪,但用于支持基于 ID 的 API(Get、Delete、Update)的 inverted index 仍然保留。

TSDS 中的 ID 值是通过组合 _tsid 和 @timestamp 生成的,这两个字段共同唯一标识每个数据点。由于这些字段已经配置了 doc value skippers,因此可以用一种方式替代 _id 上的 inverted index:

  • (a) 从 _id 中解析出 _tsid 和 @timestamp 值;
  • (b) 分别使用 doc value skippers 来进行匹配检查。

同时需要注意避免在 metrics ingestion 过程中进行昂贵的重复 ID 检查,因此通过 segment-level bloom-filters 来控制开销。

在 Elasticsearch 中为 metrics 支持 synthetic ids 是首次实现。这一优化在 OTel metrics 场景中从原始的 25 bytes per data point 中减少了 5 bytes,并且没有任何功能损失。synthetic ids 在 9.4 版本的 TSDS 中默认启用。我们计划在进一步评估后,将其扩展到 logs 和其他应用场景。

裁剪 sequence numbers

sequence numbers 被用于 replication,同时也通过 Optimistic Concurrency Control(OCC)为文档修改操作提供强一致性语义。虽然这些语义适用于某些场景,但并不适合 metrics,因为在 metrics 中并发更新非常少,也没有实际需求去防止对相同 id 数据点的并发操作。因此我们决定在 TSDS 中,在 9.4 版本里禁用所有 API 中 sequence numbers 的使用,并同时移除 OCC 支持。这带来了显著的存储减少,在 OTel 数据中从原始的 25 bytes per data point 中减少了 4 bytes,因为不再需要 inverted index,并且 sequence numbers 在不再用于 replication 后也会被裁剪。Updatedelete-by-query 操作仍然支持,但一致性语义会更弱。

如果某个 metrics 应用仍然认为 OCC 很重要,可以通过在 TSDS 所在的 index template 中设置 index.disable_sequence_numbers: false 来恢复旧行为。

使用大 numeric codec blocks

TSDS 已经使用了一种高级 codec(如之前文章所述)。该 codec 在大多数情况下表现良好,但在重复的 keyword 和 number 序列上性能较差,尤其是当 dimension 包含 IP 和 MAC address 时,会导致存储膨胀。我们发现现有的重复序列识别逻辑在更大的 codec block 下效果更好,尤其是随着序列长度增加。因此在 9.3 版本中,我们将 numeric block size 从 128 提升到 512,从而在包含 IP 和 MAC address 作为 dimension 的 OTel 数据集中减少了 2 bytes per data point。我们也在开发更可配置的 codec layout,使 block size 以及其他参数可以根据 field type 和 cardinality 更灵活地调整。

索引吞吐量

Elasticsearch 支持批量(bulk)文档写入。这个入口长期以来被优化为“尽可能宽松”,以确保所有文档都能被接收。然而,这种灵活性会在索引过程中带来额外的处理成本。对于 metrics 应用来说,这一类负担是可以通过不同方式优化的,如下所述。

引入 OTLP protobuf 入口

OTel metrics 和 Prometheus 已经建立了基于 protocol buffers 的 metrics ingestion 协议。过去需要一个转换步骤,将收集到的 protobuf 消息转换为 Elasticsearch 可以消费的 bulk 请求。

Elasticsearch 最近扩展了新的入口,支持直接接收来自 OTel metrics collectors 以及 Prometheus remote write 的消息。相比 JSON 解析,解析和处理这些(二进制)消息的成本更低;同时用于 _tsid 计算的 dimension hash 操作可以在单个 protobuf 消息内跨更多数据点复用并摊销。此外,_tsid 在 coordinator 节点中每个 doc 只计算一次,然后再传递到 data nodes 进行索引,从而避免在每个索引文档中重复执行这一昂贵步骤。这些改进使 OTel metrics 的索引吞吐量提升最高可达 20%。OTLP 入口在 9.2 版本作为技术预览引入,并在 9.3 成为 GA;我们也在 9.4 为 Prometheus remote write 增加了类似入口(技术预览),并正在推进 OTel Logs 和 Traces 的覆盖。

通过 doc value skippers 降低索引 CPU

除了显著的存储占用外,inverted indices 在构建以及 segment merge 时也需要大量 CPU。用 doc value skippers 替代它们可以降低 ingestion 阶段的 CPU 负载,从而提升索引吞吐量约 10%,这是在前述存储优化之外的额外收益。

synthetic recovery source

文档的原始 source(索引时提供的内容)在 metrics 中不会被永久存储,但 Elasticsearch 仍然需要在 replication 过程中临时保存它。这一情况在 9.1 版本发生变化:source 被改为按需生成用于 replication,这被称为 synthetic recovery source。该优化减少了 50% 的磁盘 I/O,并对 metrics indexing performance 带来了显著提升。更多细节可以参考相关文章

查询执行

用 doc value skippers 替代 inverted indices 后,TSDS 实现了纯列式存储布局,其中 metric 和 dimension 字段都以 Lucene doc values 的形式存储,每个字段都在各自独立的文件中进行编码和压缩。结合采用向量化执行(vectorized execution)的 ES|QL compute engine,Elasticsearch 现在能够提供一个完全列式的 metrics 存储与查询处理引擎。我们将这一思路推进到极致,实现了一个在查询性能上明显优于专用 metrics 引擎以及其他列式存储的列式 metrics processing engine。

计算引擎中的 time series 集成

time series 处理主要基于按 time series(或 _tsid)应用聚合函数,例如 gauge average 或 counter rate。随后再通过第二层聚合函数对这些中间结果进行归约,从而生成针对 grouping dimensions 的结果,例如按 host 和 process 聚合。可观测性仪表盘正是建立在这种执行模型之上,它可以提供 metrics 随时间变化的概览视图,并允许通过对 dimension 值和时间范围进行过滤来快速深入分析。

为了支持这一执行模型,我们引入了 TS source command,为这种查询提供了一种简单但强大的语法。它可以将针对每条 time series 的内部聚合函数,与对这些中间结果进行的外部聚合结合起来。例如,下面这个查询会计算过去一天中,每个 host 每小时 search requests rate 的总和:

`

1.  TS metrics
2.    | WHERE TRANGE(1d)
3.    | STATS SUM(RATE(search_requests)) BY TBUCKET(1h), host

`AI写代码

为了执行该查询,计算引擎会感知数据的存储方式,并按 _tsid 值应用内部聚合函数。由于数据按 _tsid 排序,time series aggregation functions 会在获取 metric values 的同时进行处理,直到 _tsid 发生变化,或者 timestamp 属于下一个时间 bucket。这使得这些函数能够对获取到的 metric value 列进行向量化执行,而 dimension values 只有在 _tsid 变化时才会被(一次性)获取。第二层聚合函数的计算同样高效,其中间聚合结果会存储在 primitive value 数组中,并在 _tsid 变化时进行填充。

计算引擎天然支持并行查询执行,能够充分利用可用的处理核心。time series aggregation 完全利用了这一能力,并在适用情况下并行处理数据点,通过提高 CPU 利用率来降低响应时间。

ES|QL 中的 time series processing 在 9.2 版本作为技术预览引入,并在 9.4 达到 GA。我们预计所有 metrics 应用都会采用它,并从显著提升的查询性能中获益。

零拷贝数据解码与加载

相比通过 /_search API 执行聚合,time series 数据的向量化处理立即带来了明显的性能提升(某些查询达到 8x),但与竞争性的 metrics store 相比,性能仍然不足。基准测试与性能分析显示,在数据解码与聚合函数计算之间,计算引擎内部存在过多数组拷贝。为了解决这一问题,我们引入了以下优化:

  • TSDS 的 codec 被扩展为可以直接将磁盘上的数据解码到 primitive arrays 中,这些数组位于计算引擎用于执行 time series aggregation 的 block 内部。不再需要额外拷贝,因为计算引擎可以批量读取这些 block,并按列逐个处理其数组。
  • 包含单一值重复 N 次的 block,会被表示为只包含这两个值的 constant block,而不是长度为 N 的数组,这是一种内存中的 run-length encoding。过滤和聚合操作也被扩展为能够高效消费这些 block。这降低了 _tsid 和 dimension 字段的内存压力与 CPU 开销,因为它们的值由于索引排序而被聚集在一起。
  • 对于聚合 metric 字段中包含 null 值的文档,会在 Lucene 层提前过滤掉,在它们被解码并复制进 block 之前完成处理。
  • 所有针对 timestamp 和 dimension 字段的过滤与正则表达式,都会下推到 Lucene,由其利用 doc value skippers 高效过滤不匹配的文档。

综合来看,这些优化带来了超过 10x 的查询执行加速(再结合向量化执行本身的 8x 提升,总体达到 80x)。这些优化自 9.2 版本引入 TS source command 时便已包含,并持续进行调优。

优化的 counter rate 计算

虽然大多数 time series aggregation 都可以轻松并行化执行,但 cumulative counter 的 rate 计算则更加复杂,因为它需要按顺序处理所有数据点,以检测 counter reset(例如 host 重启)。为了解决这一问题,计算引擎使用 _tsid 前缀将 time series 分配到不同线程。我们特别确保每个线程获得的是按顺序排列的 _tsid 范围,而不是通过 hash-partitioning 的方式分配 _tsid,这样每个线程都能够按顺序扫描磁盘数据,并继续利用高效解码与零拷贝 block 加载能力。其性能提升非常显著,rate 计算性能已经明显超过专用 metrics store,我们将在下一部分中看到这一点。

对于 cumulative counter,还有另一个有趣的问题:当 time bucket 边界时间戳上没有数据点时,如何正确计算整个 bucket 的 counter increase。metrics 系统通常会使用外推(extrapolation),即将每个 time bucket 的第一个和最后一个数据点延伸到边界,或者通过相邻 bucket 的最后数据点之间的 delta 来计算。

我们认为还可以做得更好:通过在每个 bucket 的最后一个数据点与下一个 bucket 的第一个数据点之间进行插值(interpolation),来估算每个边界上的值。然后再基于每个 time bucket 上下边界的插值结果来计算 delta。

滑动窗口支持

Elasticsearch 长期以来一直支持按时间分 bucket 的聚合,但此前无法将处理数据的窗口扩展到单个时间 bucket 之外。使用比时间 bucket 更大的窗口——例如对每分钟 bucket 使用 5 分钟窗口——可以平滑尖峰,更清晰地观察每条 time series 的底层趋势,并减少噪声:

所有 time-series aggregation functions 现在都扩展支持了 window 作为可选参数。当 window 是 time bucket 的整数倍时(例如 [TBUCKET](https://www.elastic.co/docs/reference/query-languages/esql/functions-operators/grouping-functions/tbucket "TBUCKET")(5m) 配合 1h window),计算引擎会先按照 time bucket 的跨度对数据点进行聚合,然后再按照 window 范围组合这些中间结果。这种两阶段方法避免了重复扫描数据点,并最大化复用中间结果,从而提升响应速度。window 支持在 9.3 版本作为技术预览引入,并在 9.4 达到 GA。

高效 datetime rounding

time-series 数据查询通常包含时间 bucket 划分。虽然数据点可以很容易地分配到小时以下的时间 bucket,但更大的 bucket 会受到时区、夏令时、每月天数变化等问题影响。Elasticsearch 拥有完善的 datetime rounding 逻辑来处理这些复杂情况,但在处理 time series 数据时会带来较高的 CPU 开销。

为了解决这一问题,计算引擎被扩展为能够识别哪些场景可以采用更简单的逻辑来将数据点分配到时间 bucket。例如,它能够识别 bucket 是否小于一小时,或者时区与夏令时是否不会影响某个查询,并切换为简单的 modulo 运算进行 datetime rounding。这一优化使某些查询的响应时间进一步提升了 30%。该改动在 9.4 版本中引入。

性能评估

为了评估我们方案的性能,并跟踪其随时间演进和优化的效果,我们重点关注了 OTel metrics,因为:(a)OpenTelemetry 是业界收集 metrics 的标准,被所有云厂商广泛采用;(b)它会产生每个 document 只包含一个 metric 的存储布局,而这种模式传统上对 Elasticsearch 的性能并不友好。

我们使用 Metricsgenreceiver 来生成数据集。该工具受到 TSBS 启发,用于生成模拟由 OTel hostmetricreceiver 收集的数据点。我们使用了两个数据集:

  1. 低基数(low-cardinality)场景:100 个 host,每 1 秒发送一次 metrics,总计包含 1.4 万条 time series
  2. 高基数(high-cardinality)场景:1 万个 host,每 10 秒发送一次 metrics,总计包含 140 万条 time series

我们在 EC2 单节点部署环境中进行了基准测试,其中低基数和高基数数据集分别使用 c6i.4xlargec8g.8xlarge 机器。

在竞品对比方面,我们使用了 Prometheus(v3.11.1)、Mimir(v3.0.6)以及 ClickHouse(v26.3.9.8-lts)。Prometheus 和 Mimir 具备完整的 time series processing 能力,例如 counter rate 计算;而 ClickHouse 缺少此类支持,因此最多只能提供近似结果(例如它无法一致地追踪 counter reset)。尽管如此,我们仍然报告了 ClickHouse 的响应时间,以展示:一旦 Elasticsearch 针对列式查询处理完成优化,即使面对那些并未真正按 time series 语义处理数据的列式引擎,它依然能够超越它们。

我们尽量为每个系统(包括 Elasticsearch)使用默认配置,而不针对特定 workload 进行调优。这有助于理解缺乏经验或没有足够时间调优的新用户,在接收 metrics 流量和配置 dashboard 时的真实使用体验。我们专注于单节点测试,以降低噪声,并兼容所有系统(Prometheus 默认并不支持多节点部署)。Elasticsearch 的性能已被证明能够随节点数量良好扩展,我们计划在后续文章中分享相关扩展性结果。

存储效率与索引吞吐量

我们在提升存储效率方面的努力获得了巨大回报。OTel metrics 的存储成本在一年内从每个数据点 25 bytes 降低到 3.75 bytes。对于一个已经针对 time series 做过优化的系统来说,如此大幅的提升是非常惊人的,在行业中也极为罕见:

当前的竞争格局看起来对 Elasticsearch 非常有利:

  • 在存储效率和索引吞吐量方面,Elasticsearch 略优于 Mimir
  • 在存储效率上比 Prometheus 高约 2.5x,在索引吞吐量上也略有优势
  • 在存储效率上比 ClickHouse 高约 2x,在索引吞吐量上高约 40%

查询性能

用于 metrics 处理的新型列式引擎在实践中表现非常高效。我们使用了一组混合查询,包括 gauge average 和 counter rate,这是最常见但优化方式不同的两类操作。查询时间范围为 4 小时数据,覆盖每个 metric 的所有 time series。

ClickHouse 不支持 time series aggregations,因此其结果具有一定局限性,无法与原生支持 time series processing 的 Prometheus 或 Mimir 直接对比。我们根据公开指南对每个查询进行了尽可能等价的调整。这里的重点是展示我们的列式引擎与通用列式存储之间的对比情况。

结果摘要如下:

查询类型相比 Mimir相比 Prometheus相比 ClickHouse †
Gauge average最多快 30x最多快 30x最多快 8x
Counter rate最多快 30x最多快 30x最多快 3.5x
基于 host name 的 prefix filter最多快 5x最多快 5x最多快 3x
带 window 的 gauge average最多快 25x最多快 25x最多快 4x

†ClickHouse 缺乏对 time series aggregations 和 counter reset detection 的原生支持。

Gauge average

我们比较了按 host 计算每小时平均 memory utilization(基于每条 time series 的 average)的性能,使用以下查询:

`

1.  # PromQL
2.  avg by (host.name) (avg_over_time(system.memory.utilization[1h]))

`AI写代码
`

1.  # ES|QL
2.  TS metrics-hostmetricsreceiver.otel-default
3.  | STATS AVG(AVG_OVER_TIME(system.memory.utilization)) BY host.name, TBUCKET(1h)

`AI写代码

在低基数和高基数数据集中,Elasticsearch 都能以最高 30x 的优势明显优于其他系统,表现非常轻松地领先:

Counter rate

接下来我们比较了按 host 计算每小时 cpu rate 平均值的性能,使用以下查询:

`

1.  # PromQL
2.  avg by (host.name) (rate(system.cpu.time[1h]))

`AI写代码
`

1.  # ES|QL
2.  TS metrics-hostmetricsreceiver.otel-default
3.  | STATS AVG(RATE(system.cpu.time)) BY host.name, TBUCKET(1h)

`AI写代码

尽管数据点仍然按 time series 顺序处理,但 counter rate 的性能与 gauge average 的计算表现一致(而该查询涉及的 time series 比前一个查询多 6.6x 的 document)。Elasticsearch 在与其他系统的对比中仍然保持显著优势,在低基数数据集上比 Mimir 和 Prometheus 快 30x,在高基数数据集上快 16x:

非常令人印象深刻的是,在高基数数据集中,Elasticsearch 能在不到 2 秒内处理 50 万条 time series 的 4 小时数据,而其他系统需要超过 30 秒,从而导致这些查询下的 dashboard 无响应。即使 ClickHouse 也更慢,尽管它没有用于检测 counter reset 以及在 bucket 之间进行 delta 外推/插值的逻辑。

基于 host name 前缀的过滤

接下来我们比较了基于 host name 前缀进行过滤的性能,使用以下查询:

`

1.  # PromQL
2.  avg by (host_name)
3.    (avg_over_time(system_cpu_load_average_1m{host_name=~"host-.*"}[5m]))

`AI写代码
`

1.  # ES|QL
2.  TS metrics-hostmetricsreceiver.otel-default
3.  | WHERE host.name LIKE "host-*"
4.  | STATS AVG(AVG_OVER_TIME(system.cpu.load_average.1m)) BY host.name, TBUCKET(5m)

`AI写代码

尽管将 host.name 上的 inverted index 替换为 doc value skipper,Elasticsearch 仍然能够保持最多 5x 的性能优势,相比其他系统:

Gauge average with window

我们比较了带有 90 分钟 window 的 time series aggregations 性能,其中 time bucket 为 30 分钟,使用以下查询:

`

1.  # PromQL
2.  avg by (host.name) (avg_over_time(system.memory.utilization[90m]))&step=30m

`AI写代码
`

1.  # ES|QL
2.  TS metrics-hostmetricsreceiver.otel-default
3.  | STATS AVG(AVG_OVER_TIME(system.memory.utilization, 90m))
4.      BY host.name, TBUCKET(30m)

`AI写代码

在低基数和高基数数据集中,Elasticsearch 都能轻松优于其他系统:

Elasticsearch 在低基数和高基数数据集中都保持显著优势

在低基数数据集中最高可达 25x 的性能领先,在高基数数据集中约为 8x。相比 ClickHouse 的表现也更好,接近 4x 的优势,这表明我们的方案在 windowed query 操作上的高效性。

Elasticsearch metrics 的下一步发展

Elasticsearch 已扩展出 metrics 存储与处理能力,在性能上优于 Prometheus、Mimir 和 ClickHouse。我们正在快速推进对 PromQL 和 Prometheus remote write 的支持,这些功能在 9.4 版本中也以技术预览形式提供。这些扩展使熟悉 Prometheus 及相关系统的用户可以将应用迁移到 Elasticsearch——无需迁移已有 dashboards。由于 Prometheus 集成复用了本文介绍的同一存储与查询引擎,因此 Prometheus 场景同样可以获得这些性能提升。此外,metrics 既可以用 PromQL 查询,也可以用 ES|QL 查询,甚至可以在 ES|QL pipeline 中与其他数据结合使用,从而显著扩展分析能力,远超传统 Prometheus 体系。

在存储效率、索引吞吐量和查询性能方面的提升已经非常可观,但我们仍在持续优化。后续将进一步改进 time series codec,继续降低每个 data point 的字节数;同时增强 metrics ingestion 的 batch processing,减少同步开销与不必要的处理层;还将更广泛使用 doc value skippers,在可能的情况下存储预计算的 sum 和 count 等聚合结果,以减少数据点加载与计算开销,并引入更偏向 CPU 友好的 partitioning 和 grouping 方式。

常见问题(Frequently asked questions)

什么是 columnar metrics engine,它为什么重要?

columnar metrics engine 是一种列式存储的 metrics 引擎,它将每个字段分别存储在独立文件中,而不是按行存储,然后只读取查询所需的列来执行计算。对于 time series 数据,这意味着 Elasticsearch 可以分别解码 metric values、dimension fields 和 timestamps,并在每一列上进行向量化处理,从而相比行式存储获得更快的聚合速度和更低的存储开销。

Elasticsearch 与 Prometheus 在 time series metrics 存储与查询方面相比如何?

在 9.4 版本中,Elasticsearch 的 OTel metrics 存储达到每个 data point 3.75 bytes,比 Prometheus 低约 2.5x。在查询方面,Elasticsearch 在 gauge average 和 counter rate benchmark 中比 Prometheus 和 Mimir 快最多 30x。在高基数数据集(140 万 time series)中,Elasticsearch 能在 2 秒内处理 4 小时数据,而 Prometheus 需要超过 30 秒。

什么是 Elasticsearch TSDS,什么时候应该使用?

TSDS(time-series data streams)是 Elasticsearch 用于 metrics 和 time series 数据的存储格式。它按 time series 标识符(_tsid)和 timestamp 排序,使用列式 doc values 存储字段,并采用专用 codec 进行压缩。适用于所有 metrics 工作负载,尤其是 OpenTelemetry 或 Prometheus 数据,在这些场景下存储效率和查询速度都很重要。

什么是 ES|QL 中的 TS source command?

TS 是 ES|QL 的 source command,在 9.4 中达到 GA。它使用两层模型执行 time series 查询:先在每条 time series 内部进行聚合(例如 RATE() 或 AVG_OVER_TIME()),再对结果进行外层聚合。计算引擎按 time series 排序顺序处理数据,从而实现向量化和并行执行。例如:TS metrics | STATS AVG(RATE(cpu.time)) BY host.name, TBUCKET(1h)。

Elasticsearch 如何从 25 bytes 降到 3.75 bytes per OTel data point?

主要由 4 个优化组成(9.1 到 9.4):用 doc value skippers 替代 inverted indices(-10 bytes),启用 synthetic ids(-5 bytes),裁剪 sequence numbers(-4 bytes),以及将 codec block size 从 128 提升到 512(-2 bytes)。最终在一年内实现约 6.7x 的存储压缩。

Elasticsearch 能否在不迁移 dashboards 的情况下替代 Prometheus?

Elasticsearch 支持 Prometheus remote write(9.4 技术预览)以及 PromQL 查询(9.4 技术预览)。现有基于 PromQL 的 Grafana dashboards 可以通过少量修改接入 Elasticsearch。由于 TSDS 和 ES|QL 共享同一底层引擎,这些性能优化同样适用于 PromQL 查询。

什么是 doc value skippers,它为什么重要?

doc value skippers 是 Lucene 的一种结构,用于记录文档块的 min/max 值。在 TSDS 中,它替代了 dimension 字段和 @timestamp 上的 inverted index。它将每个 data point 的存储减少最多约 10 bytes,并降低约 10% 的索引 CPU 开销,同时在时间范围和维度过滤查询中不会带来明显性能退化。

原文:www.elastic.co/observabili…