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

作者:来自 Elastic Mary Gouseti

Elasticsearch 会计算出时间边界,并随着文档写入创建过去的后备索引,因此历史数据迁移可以通过你正常的数据摄取路径运行。

查看将数据摄取到 Elasticsearch 中的不同方式,并深入了解实际示例,尝试一些新内容。

Elasticsearch 提供了大量新功能,帮助你针对自己的使用场景构建最佳搜索解决方案。现在就开始免费云试用,或者立即在你的本地机器上试用 Elastic。

现在,你可以直接将带有过去时间戳的文档写入 Elasticsearch 时间序列数据流(TSDB)。通过批量 API(bulk API)、OpenTelemetry 协议(OTLP)端点Prometheus 远程写入端点发送数月的历史指标数据。Elasticsearch 会在文档到达时创建过去的后备索引,计算每个索引的时间边界,并将其附加到数据流(data stream)。回填的文档与实时文档完全相同地存储,使用列式存储和写入时去重,同时最多可以节省 70% 的存储空间。时间序列数据回填功能随 Elasticsearch 9.5 一起发布,默认处于禁用状态,只需设置一个集群设置即可启用。你可以回填到多早的时间取决于你的生命周期配置,因为对于由于降采样或可搜索快照而已经变为只读的索引,回填并不适用。

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

即使加载历史指标并不是非常常见的使用场景,但当团队开始采用 TSDB 时,这是一个重要步骤。其中最突出的两个场景是:为新的时间序列数据流引导初始数据,以及将其他系统或其他数据流中的数据迁移到时间序列数据流。

为新的时间序列数据流引导初始数据

你希望启动一个新的时间序列数据流,并预先加入一周的历史数据,这样从一开始就有有意义的数据可供查询。使用现有工具时,你必须在索引模板中将 index.look_back_time 设置为最长的七天,所有历史数据都会写入单个后备索引。对于超过七天的数据,则需要手动创建过去的后备索引。

从其他系统迁移指标

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

我们希望让这两个场景都尽可能接近正常的批量索引过程。

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

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

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

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

在 9.5 中,Elasticsearch 可以通过创建后备索引,将可写入窗口向过去扩展。这会将可写入窗口转换为一个滑动窗口,从当前时间一直延伸到首次执行只读或破坏性生命周期操作的位置。这些操作的常见示例通常定义在你的生命周期配置中,包括降采样可搜索快照。此外,还包括保留配置

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

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

为什么将历史数据加载到 TSDB 中很困难

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

随着时间推移,滚动更新会创建新的后备索引,以覆盖即将到来的时间范围。在此版本之前,并不存在与之对应的处理过去时间范围的机制。创建过去时间范围的索引比较棘手,因为历史数据可能跨越很长的时间段,并且可能以乱序方式到达 Elasticsearch。因此,Elasticsearch 无法确定其后备索引应该覆盖的写入时间范围。我们对此的解决方案是使用预先配置的时间间隔,并以延迟方式创建过去的后备索引。

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

当检测到某个文档的时间戳没有被任何现有后备索引覆盖时,Elasticsearch 会确定缺失索引的时间边界并创建这些索引。然后,它会通过一次原子操作将这些索引添加到数据流中。

以延迟方式创建索引可以确保过去的单个请求不会因为一次要求创建 300 个索引而使集群不堪重负。同时,在没有文档需要写入之前,它也不会创建索引。

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

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

第一种是主动式。先检查每个传入文档的时间戳,然后再进行路由,并预先创建所有缺失的过去后备索引。这样可以让写入路径保持简洁。文档进行路由时,它所需要的索引已经存在。这确实要求数据流已经存在,并且至少包含一个时间序列后备索引,因为我们需要检查它,以确定可写入窗口以及新索引的时间边界。缺点是,每个针对时间序列数据流的批量请求都会增加额外的处理工作,即使请求中没有过去的时间戳,完全不需要进行回填也是如此。

第二种是反应式。让文档按照正常的索引流程失败,拦截该失败,创建缺失的索引,然后重试。这样可以避免常见场景中的任何额外开销,因为只有实际发生不匹配时才会执行额外工作。代价是失败处理路径更加复杂,并且每个回填文档都需要进行一次重试。

我们针对主动式方案进行了性能测试,测试对象是没有过去时间戳的批量请求,结果没有发现可测量的性能下降。事实证明,检查时间戳所产生的开销微乎其微。于是我们确定了方案。主动创建更加简单,并且与 Elasticsearch 已有的索引自动创建机制保持一致。此外,对于不使用回填的工作负载,它不会产生可测量的成本。

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

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

属性 如何设置 约束
持续时间 默认为一天,可以通过集群设置 data_streams.past_tsdb_index_interval 进行配置 最短为一小时。如果触发时间戳落在不超过配置持续时间 1.3 倍的间隔中,Elasticsearch 会将其合并到一个桥接索引中,而不是创建许多很小的索引。
开始时间 以现有下一个后备索引的开始时间为锚点,以配置的持续时间为倍数向过去推算 如果与前一个相邻索引发生重叠,则增加开始时间,使其与前一个相邻索引的结束时间一致。
结束时间 开始时间加上配置的持续时间 如果与下一个索引发生重叠,则减少结束时间,使其与下一个索引的开始时间一致。

处理并发写入

在分布式环境中,多个节点可能同时收到包含重叠过去时间戳的批量请求。每个节点会收集与现有索引均不匹配的时间戳,并向主节点发送请求。

主节点执行一次集群更新,对这些时间戳进行排序,然后逐个检查时间戳是否已被现有索引或新创建的索引覆盖。如果没有覆盖,它就会根据上述方式计算时间边界,并发出新的创建索引请求。集群更新始终按顺序执行,并保证生成有效的集群状态,因此可以保证新索引不会与现有索引发生重叠。

回填索引的生命周期时间如何工作

过去的后备索引保存的是旧数据,但它们本身是新创建的索引。如果不进行调整,生命周期功能会根据索引的创建时间,而不是数据所属的时间来执行降采样和保留策略。为了解决这个问题,我们使用 index.time_series.end_time 作为 index.lifecycle.origination_date。因此,无论是数据流生命周期还是索引生命周期管理(ILM)所感知到的索引年龄,都是基于其中数据的年龄,而不是索引的创建时间。

如何使用时间序列数据回填

如何启用时间序列数据回填

回填支持默认处于禁用状态。请在集群级别启用它

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

使用历史指标进行初始数据加载

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

  1. 创建你的索引模板。

  2. 初始化你的数据流。(这是一个重要步骤,因为已有的数据流是创建过去后备索引的前提条件。)

  3. 开始建立索引。

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

将数据迁移到现有数据流

在可写入窗口内迁移数据

对于落在数据流可写入窗口内的数据,将你的迁移管道指向数据流,然后让 Elasticsearch 处理其余工作。

迁移超出只读操作范围的数据

对于早于可写入窗口的数据(例如,你正在迁移 18 个月的指标数据,但降采样会在 7 天后开始),你需要使用一个不包含只读生命周期操作的独立数据流。保留策略不是问题,因为这些数据最终本来就会被删除。具体模式如下:

  1. 为历史数据流创建索引模板,使用与原始数据流相同的映射,但不包含生命周期配置:

    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" }
    }
    }
    }
    }

  2. 创建历史数据流。如果不执行此步骤,第一个索引请求可能会失败。在第一个索引请求期间,Elasticsearch 可以创建数据流,但此时还无法创建任何过去的后备索引,因此对历史文档建立索引可能会失败。显式创建数据流可以确保所有索引请求都会被接受。

    PUT _data_stream/metrics-historical-2024

  3. 将历史数据写入历史数据流,同时当前数据继续流入原始数据流。

  4. 加载完成后,添加生命周期配置。由于此功能作用于数据流级别,因此目前仅支持数据流生命周期:

    PUT _data_stream/metrics-historical-2024/_lifecycle
    {
    "enabled": true,
    "downsampling": [{ "after": "7d", "fixed_interval": "10m" }]
    }

  5. 使用通配符模式(my-metrics*)或数据流别名查询两个数据流中的数据。

  6. 如果配置了保留策略,请在历史数据流中的数据过期后将其删除。数据流生命周期会删除数据,但不会清理数据流本身。

如你所见,历史数据需要作为一个整体适配目标层,因为生命周期会在数据加载完成后才启用。如果你要进行大规模的历史数据导入,可以考虑将其拆分成多个批次。请确保每个批次在建立索引时作为一个整体都能适配目标层,以避免集群耗尽磁盘空间。生命周期一旦启用,就会立即开始处理该批次的索引,但处理完整的积压数据需要一定时间。

在大规模迁移期间保护集群:降采样闸门

当数据流生命周期针对一个包含大量索引的数据流运行,并且这些索引都符合降采样条件时,它会同时将这些索引加入队列。降采样需要大量 CPU 和 I/O;它会读取并重写索引中的所有数据。一次性将数十个操作加入队列,可能会让主节点在协调这些操作的过程中,被持久化任务更新压垮。

在支持回填之前,就可能出现降采样闸门场景(例如,为一个已经积累了数月数据的现有数据流添加生命周期策略时)。而回填从设计上来说会让这种情况更加容易发生。

在 9.5 和无服务器环境中,我们为数据流生命周期增加了洪水保护机制。现在,它会跟踪每个数据流中正在进行降采样的索引数量。如果该数量达到阈值,数据流生命周期就会暂停为该数据流继续加入新的操作队列,直到数量下降。该阈值可以通过集群设置 data_streams.lifecycle.downsampling.max_indices_in_progress 进行配置。其他数据流不会受到影响。

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

  • 回填不适用于只读索引。如果某个时间段已经执行了降采样或可搜索快照转换,那么该时间段的文档仍然会被拒绝。

  • 此功能要求预先存在一个时间序列数据流,并且至少包含一个时间序列后备索引。

  • 系统数据流不支持此功能。

  • 复制数据流依赖领导者数据流,因此无法直接进行回填。

  • 扩展集群规模仍然需要由你负责。加载数月的数据可能会同时触发大量存储使用、强制合并操作和生命周期活动。在开始之前,请确认你的集群有足够的余量来处理这些负载。

总结

在 Elasticsearch 9.5 正式发布之前,将历史数据加载到 TSDB 中是一个手动过程。通过自动化生成和管理过去的后备索引,我们希望将历史数据迁移转变为标准数据摄取管道的一项原生能力。管理有时间边界的索引本身仍然具有复杂性,但这项工作已经从用户需要负责的事情转变为 Elasticsearch 内部负责的功能。无论你是在为新的数据流加载初始数据,还是迁移大量历史数据集,现在平台都可以处理这些繁重的工作,让你能够专注于分析指标。我们期待看到这些改进如何简化你采用 TSDB 的过程。

原文:Backfill time series data in Elasticsearch with the bulk API | Elasticsearch Labs

相关推荐
金立基包装胶水1 小时前
纸袋热封胶常见都有哪些问题?
大数据·笔记·其他
LlmCraft|大模型工程实践1 小时前
08 预训练语言模型:BERT 与 GPT
人工智能·深度学习·nlp
Horn Still Sounds1 小时前
Linux网络并发服务器模型与SQLite数据库学习笔记
linux·服务器·数据库
feasibility.1 小时前
ABot-World-0:当一块 RTX 5090 能编织无限世界——交互式世界模型的高德解法
人工智能·aigc·视频·地图·具身智能·英伟达·世界模型
leoZ2311 小时前
第 6 篇:SchemaForm 渲染器核心实现
前端·javascript·vue.js·人工智能·神经网络·机器学习·自然语言处理
故七月1 小时前
优胜劣汰·动态赋能——锦邻创享OPC社区的考核管理与退出机制
大数据·人工智能
10mAh1 小时前
【Linux】CPU 100% 怎么排查?——top、pidstat、jstack 到线程定位实战
linux·运维·服务器
枫叶林FYL1 小时前
【群体智能集群控制工程实践】第10章 无人舰队核心功能实现
大数据·人工智能·算法
!chen2 小时前
客户环境 Nginx 配置流式报表与超时排查
运维·nginx