首页
/ Prometheus 3.x 特性开关(Feature Flags)深度指南:从 --enable-feature 到源码级验证

Prometheus 3.x 特性开关(Feature Flags)深度指南:从 --enable-feature 到源码级验证

2026-09-06 18:43:56作者:温玫谨Lighthearted

本文基于当前仓库的 docs/feature_flags.md 编写,系统讲解 Prometheus 中所有默认关闭的实验性或破坏性特性开关:如何通过 --enable-feature 启用它们、每个开关改变了哪些底层行为、以及仓库源码中的实际实现位置与验证方式。读完本文,你可以安全地评估并开启 exemplar 存储、Start Timestamp 全链路、PromQL 扩展语法、OTLP delta 摄入、Search API 等功能,并理解其废弃与迁移路径。

特性开关的工作机制

Prometheus 将默认禁用的特性(因为它们是破坏性变更或仍被视为实验性)统一收纳在 docs/feature_flags.md 中。行为变化会通过发布说明通告,且这些特性在未来版本中可能被默认启用。

启用方式是命令行参数 --enable-feature,接受逗号分隔的多个特性名:

prometheus --enable-feature=exemplar-storage,metadata-wal-records

从源码看,该参数的解析集中在 cmd/prometheus/main.gosetFeatureListOptions 函数中:函数遍历 featureList 并用逗号拆分,每个特性名对应一个 case 分支,向 flagConfig 中设置具体的布尔开关(例如 c.tsdb.EnableExemplarStoragec.scrape.ParseSTc.web.EnableSearch 等)。几个值得注意的实现细节:

  • 未知选项只告警不报错default 分支记录 Unknown option for --enable-feature 的 Warn 日志(cmd/prometheus/main.go),即拼写错误的特性名不会导致启动失败,需留意启动日志。
  • 互斥校验otlp-deltatocumulativeotlp-native-delta-ingestion 不能同时启用,冲突时启动直接返回错误(cmd/prometheus/main.go)。
  • 运行特性注册表:部分特性还会写入 util/features/features.go 中的全局特性注册表(按 apitsdbpromqlscrape 等类别组织),供 HTTP API 上报。启动后可通过 GET /api/v1/features 端点查询当前构建支持的全部特性及各特性是否启用,cmd/prometheus/features_test.go 中的 TestFeaturesAPI 正是通过启动真实进程、请求该端点并与 testdata/features.json 黄金文件比对来验证的。
  • 有效的选项全集(来自 cmd/prometheus/main.go 中该参数的帮助文本):concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, histograms-st-encoding, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, openmetrics2, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, search-api, st-storage, st-synthesis, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding, zstd-scrape

此外,--enable-feature 的历史包袱同样由这段 switch 处理:auto-reload-config 已废弃(提示改用 --config.auto-reload),promql-duration-exprooo-native-histograms 已永久启用(no-op),native-histograms 提示改用抓取配置项 scrape_native_histograms。这些处理逻辑同样位于 cmd/prometheus/main.go

存储层特性

Exemplars 存储(exemplar-storage

OpenMetrics 规范允许抓取目标为某些指标附带 exemplar——指向 MetricSet 之外数据的引用,最常见的用途是程序 trace ID。

实现上,exemplar 存储是一个固定大小的环形缓冲(circular buffer),在内存中为所有序列保存 exemplar。启用该特性后,Prometheus 抓取到的 exemplar 才会被保存。可通过配置文件中 storage/exemplars 区块按“exemplar 数量”控制环形缓冲大小。单个只带 trace_id=<jaeger-trace-id> 的 exemplar 大约占用 100 字节内存。启用后,exemplar 还会被追加写入 WAL 做本地持久化(保存时长取决于 WAL 保留期)。

源码侧,该开关直接映射为 c.tsdb.EnableExemplarStoragecmd/prometheus/main.go),并在配置热重载时被透传给 reloadConfig 的加载器(cmd/prometheus/main.go)。

关闭时的内存快照(memory-snapshot-on-shutdown

关闭进程时对内存中的 chunk 连同序列信息做快照并落盘。这样启动时可以直接用该快照恢复内存状态并 m-map 磁盘上的 chunk,WAL 回放只需要处理快照之外的 WAL 片段,从而显著降低启动时间。

延迟 Head 压缩启动(delayed-compaction

为 Head 压缩的启动时间加上一个不超过 chunk range 10% 的随机偏移,帮助同一台宿主机上的多个 Prometheus 实例错开压缩时机、减轻共享资源(磁盘、CPU)的瞬时压力。约束包括:

  • 只有自动触发的 Head 压缩及其直接派生的操作会受此延迟影响;
  • 若连续多次 Head 压缩都可能发生,只有第一次会经历延迟;
  • 延迟期间 Head 照常工作(继续提供查询与样本追加);
  • 延迟只改变压缩开始的时间点,产出的 block 时间对齐方式与不延迟时完全一致。

绕过页缓存的 IO(use-uncached-io

实验性特性,仅在 Linux 上可用。启用后 chunk 写入绕过页缓存(当前实现为 direct I/O),主要目标是消除页缓存行为带来的困惑,防止因缓存“虚高”增长导致的内存过量分配。注意该特性在启用时会通过 fileutil.UncachedIOSupported() 检测平台支持,不支持则直接返回错误(cmd/prometheus/main.go)。

元数据 WAL 记录(metadata-wal-records

启用后,Prometheus 将元数据保存在内存中,并按序列粒度把元数据变化作为 WAL 记录跟踪。如果你希望通过新版 Remote Write 2.0 发送元数据,必须启用此特性。

XOR2 chunk 编码(xor2-encoding

注意:此特性开关已废弃。 XOR2 float chunk 编码已经稳定,应改用配置文件中 storage.tsdb 段的 chunk_encoding.floats 字段(见 配置文档)来显式选择。该开关目前仅把 float chunk 编码的默认值设为 xor2,在未来大版本中将变为 no-op(对应源码中的告警逻辑见 cmd/prometheus/main.go)。

另外,st-storage 特性也会自动选择 XOR2 作为默认 float chunk 编码,因为 XOR chunk 无法保存 Start Timestamp。

Histogram ST chunk 编码(histograms-st-encoding

警告:这是高度实验性且有风险的设置:

  • histogramSTfloathistogramST 编码的 chunk 无法被不支持该编码的旧版 Prometheus 读取。一旦启用并写入数据,若回退版本需要手动从磁盘删除这些 block,否则所有查询都会报错。
  • 编码方案仍在实验中,任意版本都可能变化,跨版本持久化 block 数据会丢失。
  • 编码很新,下游工具与 LTS 系统(例如 Thanos sidecar 上传的 block)可能尚不支持。

该设置启用针对原生直方图与 float 直方图样本的新 histogramSTfloathistogramST chunk 编码:它们在对应直方图 chunk 格式上扩展了 Start Timestamp(ST)头与逐样本 ST 编码,作用相当于 XOR2 编码 对 float chunk 做的事。该开关不影响 float chunk。

st-storage 特性会自动启用这些直方图编码;若单独启用本开关而未启用 st-storage,则只使用支持 ST 的直方图 chunk 编码,但不会保存摄入时收到的 Start Timestamp。

Start Timestamp(ST)全家桶

Prometheus 围绕“指标样本携带开始时间”提供了一组渐进式特性,理解它们的关系是安全启用的前提。

ST 零值注入(created-timestamp-zero-ingestion

说明:CreatedTimestamp 特性为保持一致性已更名为 StartTimestamp,此特性开关仍沿用旧名以保持稳定性。

启用 Start Timestamp 的摄入:在合适时 ST 会被注入为值为 0 的样本。目前 Prometheus 支持在 PrometheusProtoOpenMetrics1.0.0 两种格式上承载 ST,其中推荐 PrometheusProto——OpenMetrics 1.0 的 ST 信息通过 <metric>_created 指标传递,解析这类指标既易出错又昂贵(增加开销),还要小心不要让额外的 _created 指标污染你的 Prometheus。

因此,启用 created-timestamp-zero-ingestion 后,Prometheus 会把全局 scrape_protocols 的默认值改为 [PrometheusProto, OpenMetricsText1.0.0, OpenMetricsText0.0.1, PrometheusText0.0.4],即优先协商 Prometheus Protobuf 协议(除非显式设置了其他 scrape_protocols)。从源码看,正是把全局默认值替换为 config.DefaultProtoFirstScrapeProtocols 实现的(cmd/prometheus/main.go),该协议列表定义在 config/config.go

除了 Prometheus 侧启用,被抓取的应用也必须暴露 ST 才能生效。

ST 原生存储(st-storage

启用逐样本的 Start Timestamp 存储,贯穿 WAL、TSDB/Agent 与 Remote Write 2.0,能够完整保留抓取与接收协议呈现的精确 ST 值。未来该特性将取代通过注入合成 0 样本的 created-timestamp-zero-ingestion

目前支持 ST 的格式同样为 PrometheusProtoOpenMetrics1.0.0,推荐 PrometheusProto(ST 传递更高效)。同样要求被抓取应用暴露 ST。

已知限制(实验性特性):

  • 引入新的 WAL 记录类型(SamplesV2),只能被 Prometheus 3.11 或更高版本回放;
  • 为了持久化(TSDB block),该特性会自动为 float 启用 XOR2 chunk 格式、为原生直方图启用 ST chunk 格式(与 chunk_encoding.floats: xor2histograms-st-encoding 开关独立启用时相同)。若在配置文件中显式写 chunk_encoding.floats: xorst-storage 处于激活状态,配置重载时会被拒绝,因为 XOR chunk 不保存 Start Timestamp。这些约束在实验阶段结束后可能调整;
  • 原生直方图与 NHCB 在其他方面的 ST 支持仍在推进中;
  • PromQL 层面对 ST 的使用不在本特性范围内(见下条 use-start-timestamps)。

源码中该开关同时设置了 scrape.ParseSTtsdb.EnableSTStorageFloatChunkEncoding = EncXOR2EnableHistogramSTEncoding 与 agent 端开关,并同样切换 proto 优先的抓取协议默认值(cmd/prometheus/main.go)。

ST 在 PromQL 函数中的使用(use-start-timestamps

启用 rate()irate()increase()start_timestamp() 等 PromQL 函数对 Start Timestamp 的使用。注意该特性目前不支持扩展范围选择器(promql-extended-range-selectors)。

ST 合成(st-synthesis

当源端不提供 ST 时,对累积型指标(Counter、经典直方图、原生直方图)合成 Start Timestamp。其思路类似于 OpenTelemetry Collector 社区贡献的 metricstarttimeprocessor 的“减去初始点”策略:跟踪前值以检测重置,并从第一个样本起减去初始参考点,合成一条从零开始的时间线。

实验性特性的注意事项:

  • 特性开启时第一个样本会被丢弃,用于建立 ST 参考点。因此若某序列只上报过一个点,开启后可能导致该序列没有任何样本入库;
  • 合成能给出准确的 Start Timestamp 且保持计数器速率准确,但原始计数器值将与抓取值不同——第一个点被丢弃、其时间戳被用作后续所有点的起始时间戳,后续所有点都会相对该被丢弃的点做归一化(减去它)。相当于用原始数据创建了一条已知起始时间的新计数器流;
  • 合成仅对抓取数据生效(Remote Write 与 OTLP 接收器尚未实现);
  • 合成要求样本有序,因此没有 ST 的累积样本即使设置了 tsdb.out_of_order_time_window 也会因乱序被拒绝;
  • 若某序列的追加失败(例如因乱序样本被拒),该序列的合成状态会被清除,失败后的下一个样本会被当作“第一个样本”再次丢弃以建立新参考点。

PromQL 相关特性

逐步统计(promql-per-step-stats

启用后,在查询请求中传 stats=all 会返回逐步骤(per-step)统计,包含:

  • totalQueryableSamples / totalQueryableSamplesPerStep:查询期间加载的样本总数。对 ratesum_over_time 这类 range-vector 函数在多步求值时,每一步都会计入完整窗口(同一点可能在多个步骤被重复计数)。
  • samplesRead / samplesReadPerStep:样本读取(I/O)总数。range 查询中的 range-vector 函数只计每步的新增点(第 0 步计完整窗口,后续步骤只计上一步未出现的点);其他查询类型下该值等于 totalQueryableSamples。
  • peakSamples:求值期间内存中的峰值样本数(用于 query.max-samples 限制)。

服务端另有两个可观测计数器:prometheus_engine_query_samples_total(按步计完整窗口的加载样本数)与 prometheus_engine_query_samples_read_total(range-vector 按步计增量读取数)。若引擎或查询层面任一未启用该特性,逐步骤统计将完全不计算。

实验性 PromQL 函数(promql-experimental-functions

启用被视为实验性的 PromQL 函数。这些函数的名称、语法或语义可能改变,甚至可能整体被移除。

延迟 __name__ 标签移除(promql-delayed-name-removal

启用后,Prometheus 改变 PromQL 查询结果中移除 __name__ 标签的方式(对于需要移除的函数与表达式):把移除延迟到查询求值的最后一步,而不是每求值一次会派生指标的表达式/函数就移除一次。

好处:

  • 允许通过 label_replacelabel_join 可选地保留 __name__
  • 避免对 __name__ 标签应用正则匹配器时出现 "vector cannot contain metrics with the same labelset" 错误。

限制与风险:

  • 分开求值查询的一部分仍会触发 labelset 冲突——手动或用 PromLens 类工具分析中间结果时常见;
  • 若查询引用了已被移除的 __name__ 标签,行为可能改变(例如 sum by (__name__) (rate({foo="bar"}[5m])))。这类查询很少见且容易修复——上例中去掉 by (__name__) 在无特性时结果不变,在启用特性时则能修复潜在问题;
  • 理论上可构造聚合到 __name__、把“延迟移除”与“未移除”的样本放进同一分组,此时该分组的名称会被移除——这种情形在实用查询中几乎不会出现。

扩展范围选择器(promql-extended-range-selectors

为 PromQL 的 range 与 instant 选择器启用实验性 anchoredsmoothed 修饰符,让你在 rateincrease 等函数中更精细地控制 range 边界处理,尤其在缺失或不规则数据下。注意:原生直方图尚不受扩展范围选择器支持,且不支持子查询

anchored:在 range 起点使用 lookback delta 内最近的样本(若 lookback delta 内无样本,则使用 range 内第一个样本);range 终点同样使用 range 内最后一个样本。不做外推或插值,适合直接获取样本值之间的差。适用于:resetschangesrateincreasedelta。示例:

increase(http_requests_total[5m] anchored)

注意:increase 配合 anchored 修饰符时,返回结果为整数。

smoothed:range 选择器在 range 边界处线性插值,利用边界前后两侧的样本值做更稳健的估计,可抗不规则抓取与缺失样本;但它需要求值区间之后的样本才能正确工作(见下方规则告警提示)。instant 选择器则在求值时间戳处用紧邻前后的两个样本线性插值。适用于:rateincreasedelta。示例:

rate(http_requests_total[step()] smoothed)

告警与录制规则注意smoothed 需要求值区间之后的样本,直接在规则中使用通常会低估结果(求值时刻拿不到未来样本)。要安全地在规则里用 smoothed必须给规则组设置 query_offset,确保计算窗口完全落在过去、所需样本都已可用。关键告警建议至少偏移一个抓取间隔;非关键或希望更强容错(容忍漏抓)的场景可考虑更大偏移(多个抓取间隔)。

二元运算 fill 修饰符(promql-binop-fill-modifiers

为 PromQL 二元运算符启用实验性的 fill()fill_left()fill_right() 修饰符,允许为二元运算一侧缺失的匹配项填入指定的默认样本值。示例:

  rate(successful_requests[5m])
+ fill(0)
  rate(failed_requests[5m])

更多细节与示例参见 fill 修饰符文档

抓取与协议相关特性

额外抓取指标(extra-scrape-metrics

注意:此特性开关已废弃,请改用 extra_scrape_metrics 配置项(可在全局与抓取配置两级设置),该开关将在未来大版本移除,详见配置文档

启用后,每次实例抓取会在以下额外时间序列中存储一个样本:

  • scrape_timeout_seconds:该目标的 scrape_timeout 配置值,配合 scrape_duration_seconds / scrape_timeout_seconds 可观察各目标距超时的余量;
  • scrape_sample_limit:该目标的 sample_limit 配置值,配合 scrape_samples_post_metric_relabeling / scrape_sample_limit 观察距限制的余量。注意未配置限制时该值为 0,上述查询会出现除以 0 得到 +Inf;若只想查“配置了限制”的目标,用 scrape_samples_post_metric_relabeling / (scrape_sample_limit > 0)
  • scrape_body_size_bytes:最近一次成功抓取响应的解压后大小。因超出 body_size_limit 而失败的抓取报 -1,其他抓取失败报 0

Zstandard 抓取压缩(zstd-scrape

启用后,Prometheus 除了 gzip 之外还会在抓取请求中宣告支持 Zstandard 压缩的响应。解压后的响应仍受配置的 body_size_limit 约束。

OpenMetrics 2.0(openmetrics2

启用对暴露 OpenMetrics 2.0 文本格式的抓取目标的支持,内容类型为 application/openmetrics-text; version=2.0.0

OpenMetrics 2.0 支持仍是实验性的:解析器尚未稳定,今天 Prometheus 接受的暴露格式未来版本可能拒绝,请勿在生产中依赖当前行为。

关闭该开关时,OpenMetrics 2.0 内容类型会被当作不支持的内容类型处理:若目标配置了 fallback_scrape_protocol 则回退使用它,否则抓取失败。

如果你正在实现 OpenMetrics 2.0 exporter 或客户端库,请注意:被 Prometheus 成功抓取不等于你的输出符合规范,请以官方 OpenMetrics 2.0 迁移指南为准。

类型与单位标签(type-and-unit-labels

启用后,Prometheus 会按 PROM-39 提案的设计开始注入额外的保留标签 __type____unit__。这些标签来源于既有抓取与摄入格式(OpenMetrics Text、Prometheus Text、Prometheus Proto、Remote Write 2、OTLP)的元数据结构;用户提供的同名 __type____unit__ 标签会被覆盖。

PromQL 层会以与 __name__ 相同的方式处理这些标签:例如在 -+ 等运算中被丢弃,并受 promql-delayed-name-removal 特性影响。该特性让重要元数据信息可以直接随样本与 PromQL 层使用,尤其适合:

  • 想按类型或单位选择指标的用户;
  • 想处理同名不同类型/单位序列的场景,例如原生直方图迁移、或来自 OTLP 端点(未经翻译)的 OpenTelemetry 指标。

后续还有依赖此特性的规划工作,例如在类型误用时提供帮助的 PromQL 体验改进、自动重命名、delta 类型等。

与元数据记录的行为:启用本特性且存在元数据 WAL 记录时,若两者给出的 type 或 unit 不一致(小概率情况),Prometheus 输出倾向于 __type__/__unit__ 标签的值。例如 Remote Write 2.0 场景下,即使元数据记录(可能因 bug)说是 "counter",只要 __type__="gauge",远端时间序列会被设为 gauge。

OTLP 相关特性

OTLP delta 转累积(otlp-deltatocumulative

启用后,Prometheus 不再丢弃 delta 时序(temporality)的 OTLP 指标,而是把它们转换为等价的累积形式。不能与 otlp-native-delta-ingestion 同时启用(启动时会报错,见前文互斥校验)。

该转换复用 OTel Collector 的 deltatocumulative 处理器(默认设置)。delta 转换需要在内存中保持按序列聚合 delta 变化的状态:Prometheus 重启后该状态丢失,累积序列会从零重新聚合,表现为一次计数器重置。该状态会周期性清除不活跃的序列(按 max_stale 设置)。

启用后可能对性能产生负面影响,因为内存状态由互斥锁保护;纯累积的 OTLP 请求不受影响。

OTLP 原生 delta 支持(otlp-native-delta-ingestion

启用后允许原生摄入 delta 时序的 OTLP 指标:原样存储原始样本值,不做转换。不能与 otlp-deltatocumulative 同时启用。

当前 StartTimeUnixNano 字段被忽略,delta 指标被赋予“未知”的指标元数据类型。delta 支持处于非常早期阶段,摄入与查询流程未来可能变化(参见 prometheus/proposals 中第 48 号提案)。

查询建议

  • 标准 PromQL 计数器函数(rate()increase())面向累积指标设计,用于 delta 指标会给出错误结果。目前要获得类似效果,请用 sum_over_time()
    • sum_over_time(delta_metric[<range>]):在指定时间范围内对 delta 值求和;
    • sum_over_time(delta_metric[<range>]) / <range>:计算 delta 指标按秒速率。
  • <range> 不是指标采集间隔的整数倍,上述写法可能不理想。例如指标采集间隔为 10m,而你执行 sum_over_time(delta_metric[1m]) / 1m(1m step)的 range 查询,图表会每 10 分钟出现一个高速率单点,而不是 10 个点上的较低恒定值。

当前已知坑

  • delta 指标若通过federation暴露,当摄入间隔与联邦端点的抓取间隔不一致时,数据可能被错误采集;
  • 难以判断某指标是 delta 还是累积时序——指标名与标签中没有时序提示。目前若同时摄入 delta 与累积指标,建议显式添加自定义标签加以区分。未来计划引入类型标签来一致地区分指标类型,并可能让 PromQL 函数类型感知(例如对 delta 指标使用仅适用于累积的函数时给出警告);
  • 同一时间戳摄入多个样本时,只保留其中一个点,样本不会求和(这是 Prometheus 的通用行为——相同时间戳的重复样本会被拒绝)。任何聚合都必须在发送样本到 Prometheus 之前完成。

规则引擎与 API/UI 特性

独立规则并发求值(concurrent-rule-eval

默认情况下,规则组之间并发执行,但组内规则串行执行——因为规则可能把前一条规则的输出作为自己的输入。若规则间没有可检测的依赖关系,就没有必要串行运行。启用 concurrent-rule-eval 后,规则组内不依赖其他规则的规则会并发求值,可能改善规则组求值延迟与资源利用率,代价是增加并发查询负载。

并发规则求值数量由 --rules.max-concurrent-evals 配置,默认值为 4(见 cmd/prometheus/main.go 的默认值注册;设置该值时可能需要同步调整 query.max-concurrency)。

Search API(search-api

启用实验性的搜索 API 端点,支持模糊匹配与过滤地发现指标名、标签名与标签值,详见搜索 API 文档

配套的 --web.search.max-limit 标志(默认 10000)为搜索端点接受的 limit 查询参数设置硬性上限:超限请求返回 HTTP 400;默认响应限制(100)会被静默钳制到该上限,因此运维调小上限不会破坏不带 limit 的请求;设为 0 表示完全禁用上限——不建议在可信网络之外暴露端点时这样做,否则单个客户端可一次性请求整个索引。

旧版 Web UI(old-ui

回退到提供旧版(Prometheus 2.x)Web UI,而不是新 UI。随 Prometheus 3.0 发布的新 UI 是一次完全重写,目标是更干净、少干扰、内部实现更现代,但功能尚不完全、也未充分经生产检验,部分用户仍可能偏好旧 UI。

启用与验证的实操清单

  1. 启用:启动命令追加 --enable-feature=<name1>,<name2>;注意 st-storage 会连带启用 XOR2 与直方图 ST 编码,并与 created-timestamp-zero-ingestion 一样把默认 scrape_protocols 切换为 proto 优先列表(config/config.go)。
  2. 核对日志:启动时每个开关都会输出对应的 Info/Warn 日志(如 "Experimental in-memory exemplar storage enabled"),废弃选项(extra-scrape-metricsxor2-encoding 等)会输出明确的迁移告警;未知名称只有 Warn。
  3. 验证运行态:访问 GET /api/v1/features,按类别查看构建支持的特性与启用状态;该端点的回归行为由 cmd/prometheus/features_test.goTestFeaturesAPI 测试保障。
  4. 迁移提示extra-scrape-metrics → 配置项 extra_scrape_metricsxor2-encoding → 配置项 storage.tsdb.chunk_encoding.floats: xor2auto-reload-config → 命令行 --config.auto-reload。迁移后应移除这些开关,避免未来大版本中行为变化。
登录后查看全文
热门项目推荐
相关项目推荐