首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >在 Elasticsearch 中回填时序数据:通过 bulk API 加载数月的历史指标

在 Elasticsearch 中回填时序数据:通过 bulk API 加载数月的历史指标

原创
作者头像
点火三周
发布2026-09-08 14:55:15
发布2026-09-08 14:55:15
260
举报
文章被收录于专栏:Elastic Stack专栏Elastic Stack专栏

现在,您可以直接将包含过去时间戳的文档写入 Elasticsearch 时序数据流 (TSDB)。通过 bulk APIOpenTelemetry 协议 (OTLP) 端点Prometheus 远程写入端点,可以发送数月的历史指标。Elasticsearch 会在文档到达时自动创建历史后备索引,计算每个索引的时间边界并将其附加到数据流上。回填的文档与实时文档完全一样存储,支持列式存储和写入时去重,并且可节省高达 70% 的存储空间。时序数据回填功能随 Elasticsearch 9.5 发布,默认禁用,可通过一个集群设置开启。您可以写入多久以前的数据取决于生命周期配置,因为回填不适用于已因降采样或可搜索快照而变为只读的索引。

回填之前如何加载历史指标

尽管加载历史指标不是一个非常常见的用例,但它是团队采用 TSDB 时的重要步骤。以下两种场景最为突出:引导新的时序数据流,以及将数据从其他系统或数据流迁移到时序数据流。

引导新的时序数据流

您希望从一周的历史数据开始一个新的时序数据流,以便从一开始就能查询到有意义的内容。使用现有工具,您必须在索引模板中将 index.look_back_time 设置为最大值七天,但所有历史数据都会落入同一个后备索引中。对于超过七天的数据,您需要手动创建过去的后备索引。

从其他系统迁移指标

您在另一个系统上存储了数月的历史指标,并希望将完整数据集迁移到 TSDB。您需要在实时摄入的同时加载数月的历史指标。原先的解决方法是手动创建所有必要的过去后备索引,并设置正确的 time_series.start_timetime_series.end_time,然后直接使用索引名称进行索引。最后通过修改数据流 API 将其附加到数据流上。这种方法虽然可行,但需要理解索引时间语义,并且要为每个时间窗口重复执行步骤,同时还要与正在进行的写入操作协调。

我们希望这两种场景都能尽可能接近常规的 bulk 索引体验。

时序数据回填带来了哪些变化

在 9.5 版本中,Elasticsearch 可以创建覆盖过去时间范围的后备索引,从而将可写入窗口向后扩展。

可写入窗口是指时序数据流接受新文档的 @timestamp 值范围。

过去,可写入窗口仅由 Elasticsearch 收到请求时已有的可写后备索引决定。

在 9.5 版本中,Elasticsearch 可以通过创建后备索引来扩展过去的可写入窗口。这使可写入窗口变成了一个从当前时间向后延伸到第一个只读或破坏性生命周期动作的滑动窗口。这些动作的常见示例(通常在生命周期配置中定义)包括降采样可搜索快照。示例还包括保留策略配置

因此,假设集群已启用加载历史数据,那么对于具有以下生命周期配置的数据流,其可写入窗口由降采样动作决定,因为这是使后备索引变为只读的第一个动作。因此,对于该数据流,Elasticsearch 接受 @timestamp 不早于三个月前的文档。

代码语言:json
复制
GET _data_stream/metrics/_lifecycle
{
  "enabled": true,
  "downsampling": [
    {
      "after": "90d",
      "fixed_interval": "10m"
    }
  ],
  "data_retention": "365d"
}

为什么向 TSDB 加载历史数据很困难

TSDB 由针对时间戳测量优化过的数据流组成。它使用列式存储布局,并强制维度不可变。它还将数据组织成按时间划分的后备索引;每个索引覆盖一个特定的时间范围,并且只接受 @timestamp 落在该范围内的文档。

随着时间的推移,滚动会创建新的后备索引来覆盖未来的时间范围。在此之前,没有对应的机制来处理过去的时间范围。创建过去的索引很棘手,因为历史数据可能跨越很长时间,并且可能以乱序到达 Elasticsearch。因此,Elasticsearch 无法确定其后备索引应覆盖的写入时间范围。我们的解决方案是使用预配置的间隔,并惰性创建过去的后备索引。

Elasticsearch 如何创建过去的后备索引

当检测到一个文档的时间戳不在任何现有后备索引的覆盖范围内时,Elasticsearch 会确定缺失索引的时间边界并创建它们。然后,它会在一个原子操作中将它们添加到数据流中。

惰性创建索引可以确保单个请求不会因为需要同时创建 300 个索引而使集群不堪重负。同时,也不会在还没有文档写入之前就创建索引。

时序数据回填时间线:过去的后备索引接受保留限制内的文档,拒绝更早的文档
时序数据回填时间线:过去的后备索引接受保留限制内的文档,拒绝更早的文档

主动与被动:我们如何选择索引创建方式

我们探索了两种检测何时需要创建过去后备索引的方法。

第一种是主动方式。在路由之前检查每个传入文档的时间戳,并预先创建任何缺失的过去后备索引。这保持了写入路径的简洁。当文档被路由时,其所需的索引已经存在。它要求数据流已经存在且至少有一个时序后备索引,因为我们通过检查该索引来确定可写入窗口和新索引的时间边界。缺点是它会给每个针对时序数据流的 bulk 请求增加额外工作,即使请求中不包含任何过去的时间戳,也不需要任何回填。

第二种是被动方式。让文档在常规索引中失败,拦截该失败,创建缺失的索引,然后重试。这避免了在常见情况下的任何开销,因为额外工作只在实际发生不匹配时才会发生。代价是失败处理路径更加复杂,并且每个回填文档都需要重试。

我们对主动方式针对不包含过去时间戳的 bulk 请求进行了性能测试,没有发现明显的性能下降。检查时间戳的开销可以忽略不计。这最终确定了方案。主动创建更简单,并且与 Elasticsearch 中索引自动创建的工作方式一致。此外,它不会给不使用回填的工作负载带来可测量的成本。

Elasticsearch 如何确定过去索引的边界

每个新的过去后备索引需要计算三个属性:持续时间、开始时间和结束时间。

属性

设置方式

约束

持续时间

默认为一天,可通过集群设置 data_streams.past_tsdb_index_interval 配置

最小一小时。如果触发时间戳落在不超过配置持续时间 1.3 倍的间隙内,Elasticsearch 会将其合并为一个桥接索引,而不是创建许多小索引。

开始时间

以现有下一个后备索引的起始时间为锚点,按配置持续时间的倍数向后推算

如果与上一个相邻索引的结束时间重叠,则调整为与上一个相邻索引的结束时间一致。

结束时间

开始时间加上配置的持续时间

如果与下一个索引的开始时间重叠,则调整为与下一个索引的开始时间一致。

处理并发写入

在分布式环境中,多个节点可能同时收到包含重叠过去时间戳的 bulk 请求。每个节点收集不在任何现有索引范围内的时间戳,并向主节点发送请求。

主节点执行一个集群更新,对这些时间戳进行排序,然后逐一检查该时间戳是否已被现有或新创建的索引覆盖。如果未被覆盖,则使用上述计算的时间边界发出新的创建索引请求。集群更新总是顺序执行,并且保证生成有效的集群状态,因此新索引保证不会与现有索引重叠。

生命周期年龄如何作用于回填索引

过去的后备索引虽然包含旧数据,但本身是新建的索引。如果不加调整,生命周期特性会根据索引的创建时间(而非数据的时间)来应用降采样和保留。我们通过使用 index.time_series.end_time 作为 index.lifecycle.origination_date 来解决这个问题。这样,数据流生命周期索引生命周期管理 (ILM) 所感知到的索引年龄将基于其数据的年龄,而不是创建时间。

如何使用时序数据回填

如何启用时序数据回填

回填支持默认禁用。在集群级别启用它

代码语言:json
复制
PUT _cluster/settings
{
    "persistent": 
    {
        "data_streams.time_series.create_past_indices_enabled": true  
    }
}

使用历史指标进行引导

要将历史数据加载到新的时序数据流中:

  1. 创建索引模板。
  2. 初始化数据流。 (这是一个重要步骤,因为存在现有数据流是创建过去后备索引的前提条件。)
  3. 开始索引。

当包含历史时间戳的文档到达时,过去的后备索引会自动创建,默认每个索引覆盖一天的数据。无需额外配置。

将数据迁移到现有数据流

迁移可写入窗口内的数据

对于数据流可写入窗口内的数据,将迁移管道指向该数据流,然后让 Elasticsearch 处理其余部分。

迁移超出只读动作的数据

对于比可写入窗口更旧的数据(例如,您要迁移 18 个月的指标,但降采样在 7 天后就生效),您需要一个没有只读生命周期动作的单独数据流。保留不是问题,因为数据最终会被删除。模式如下:

  1. 为历史数据流创建索引模板,使用与原始数据流相同的映射,但不设置生命周期:
代码语言:json
复制
PUT _index_template/my-metrics-historical
{
  "index_patterns": [
    "metrics-historical-*"
  ],
  "data_stream": {},
  "template": {
    "settings": {
      "index.mode": "time_series"
    },
    "mappings": {
      "properties": {
        "sensor_id": {
          "type": "keyword",
          "time_series_dimension": true
        },
        "temperature": {
          "type": "half_float",
          "time_series_metric": "gauge"
        },
        "@timestamp": {
          "type": "date"
        }
      }
    }
  }
}
  1. 创建历史数据流。如果不执行此步骤,第一次索引请求可能会失败。在第一次索引请求期间,Elasticsearch 可以创建数据流,但还不能创建任何过去的后备索引,因此索引历史文档可能会失败。显式创建数据流可以确保所有索引请求都会被接受:
代码语言:json
复制
PUT _data_stream/metrics-historical-2024
  1. 将历史数据索引到历史数据流中,同时当前数据继续流入原始数据流。
  2. 加载完成后,添加生命周期。此功能仅在数据流级别支持,因此只能使用数据流生命周期:
代码语言:javascript
复制
PUT _data_stream/metrics-historical-2024/_lifecycle
{
  "enabled": true,
  "downsampling": [
    {
      "after": "7d",
      "fixed_interval": "10m"
    }
  ]
}
  1. 使用通配符模式(my-metrics*)或数据流别名跨两个数据流进行查询。
  2. 如果配置了保留策略,则在数据过期后删除历史数据流。数据流生命周期会删除数据,但不会清理数据流本身。

如您所见,历史数据需要整体适应目标层,因为生命周期会在数据加载完成后启用。如果历史数据量很大,您可以选择将其分成批次。确保每个批次在索引时能整体适应目标层,以避免集群磁盘空间耗尽。生命周期会在启用后立即开始处理该批次的索引,但需要时间处理所有积压。

在大型迁移中保护集群:降采样防洪闸

当数据流生命周期针对一个包含大量符合降采样条件的索引的数据流运行时,它会同时将它们排队。降采样是 CPU 和 I/O 密集型操作;它会读取并重写索引中的所有数据。同时排队数十个操作可能会使主节点因持久任务更新而超负荷,而主节点需要协调这些操作。

降采样防洪闸场景在回填支持之前就可能发生(例如,向一个积累了数月数据的数据流添加生命周期策略)。回填功能的设计使其更有可能发生。

在 9.5 版本和无服务器中,我们为数据流生命周期添加了防洪保护。现在它会跟踪每个数据流中正在被降采样的索引数量。如果该数量达到阈值,数据流生命周期会暂停对该数据流进一步的操作排队,直到数量下降。该阈值可通过集群设置 data_streams.lifecycle.downsampling.max_indices_in_progress 配置。其他数据流不受影响。

时序数据回填的限制和前提条件

  • 回填不适用于只读索引。如果降采样或可搜索快照转换已经在某个时间段上运行过,则该时间段的文档仍会被拒绝。
  • 该功能需要一个预先存在的时序数据流,且至少有一个时序后备索引。
  • 系统数据流被排除在外。
  • 复制数据流依赖于主数据流,因此无法直接回填。
  • 扩展仍然是您的责任。加载数月的数据可能会同时触发显著的存储使用、强制合并操作和生命周期活动。在开始之前,请检查您的集群是否有足够的余量来管理这些操作。

结论

在 Elasticsearch 9.5 版本之前,将历史数据加载到 TSDB 是一个手动过程。通过自动化生成和管理过去的后备索引,我们旨在将历史数据迁移转变为标准摄入管道的原生能力。管理时间边界索引的固有复杂性仍然存在,但它已从用户责任转变为 Elasticsearch 的内部功能。无论是引导新的数据流还是迁移庞大的历史数据集,平台现在都会处理繁重的工作,让您专注于分析指标。我们期待看到这些改进如何简化您对 TSDB 的采用。

常见问题解答

什么是 Elasticsearch 中的 TSDB 回填?

TSDB 回填是 Elasticsearch 9.5 中提供的一项功能,允许您将包含过去时间戳的文档写入时序数据流。Elasticsearch 会自动创建所需的过去后备索引来存储它们。您使用与实时数据相同的端点;无需单独的工具或手动索引管理。

如何启用 TSDB 回填?

回填功能默认禁用。使用单个集群设置 PUT _cluster/settings 并将其中的 data_streams.time_series.create_past_indices_enabled 设置为 true 即可启用。对于大多数用例,无需其他配置。在无服务器环境中,启用此功能的方法将很快推出。

TSDB 回填的可写入窗口是什么?

可写入窗口是 TSDB 接受的过去时间戳范围。它从当前时间延伸到第一个使后备索引变为只读的生命周期动作(如降采样或可搜索快照转换),或者延伸到配置的保留期限,以先到者为准。如果两者都未配置,则窗口可以无限延伸。

我可以使用 TSDB 回填加载比可写入窗口更旧的数据吗?

不可以。您无法直接将其加载到同一个数据流中。比可写入窗口更旧的数据(例如,降采样在 7 天后生效,而数据来自 18 个月前)必须加载到没有生命周期策略的单独时序数据流中。然后,您可以使用通配符模式或数据流别名一起查询两个数据流。

TSDB 回填会保留写入时去重吗?

是的。创建的过去后备索引是时序索引,提供相同的保证。如果到达重复文档,Elasticsearch 会立即拒绝它,并返回 409 Conflict

Elasticsearch 如何防止多个节点回填同一时间范围时发生冲突?

边界是在集群状态更新中确定的,该更新由主节点顺序执行。如果两个节点提交请求以覆盖两个非常接近的时间戳,第一次集群更新会创建索引,第二次更新要么检测到该时间戳已被覆盖并跳过创建索引,要么创建一个与上一个索引相邻的索引。

TSDB 回填有哪些限制?

回填不适用于只读索引。如果降采样或可搜索快照已经在某个时间段上运行过,则该时间段的文档仍会被拒绝。该功能需要一个预先存在的时序数据流,且至少有一个后备索引,并且不适用于系统数据流或复制数据流。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 回填之前如何加载历史指标
    • 引导新的时序数据流
    • 从其他系统迁移指标
  • 时序数据回填带来了哪些变化
    • 为什么向 TSDB 加载历史数据很困难
  • Elasticsearch 如何创建过去的后备索引
    • 主动与被动:我们如何选择索引创建方式
    • Elasticsearch 如何确定过去索引的边界
    • 处理并发写入
    • 生命周期年龄如何作用于回填索引
  • 如何使用时序数据回填
    • 如何启用时序数据回填
    • 使用历史指标进行引导
    • 将数据迁移到现有数据流
      • 迁移可写入窗口内的数据
      • 迁移超出只读动作的数据
  • 在大型迁移中保护集群:降采样防洪闸
  • 时序数据回填的限制和前提条件
  • 结论
    • 常见问题解答
      • 什么是 Elasticsearch 中的 TSDB 回填?
      • 如何启用 TSDB 回填?
      • TSDB 回填的可写入窗口是什么?
      • 我可以使用 TSDB 回填加载比可写入窗口更旧的数据吗?
      • TSDB 回填会保留写入时去重吗?
      • Elasticsearch 如何防止多个节点回填同一时间范围时发生冲突?
      • TSDB 回填有哪些限制?
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档