3. OpenTelemetry 采样基础:Head Sampling 与 Tail Sampling 的核心区别

在 OpenTelemetry 的采样体系中,决策时机是区分不同采样策略的核心维度。根据采样决策是在 Span 创建之初(头部)还是在 Span 完成并收集到所有属性之后(尾部),采样被划分为 Head Sampling(头部采样) 与 Tail Sampling(尾部采样)。这两种策略在延迟、准确性、资源消耗和适用场景上存在本质差异,直接影响 SLI 的统计可靠性。

3.1 Head Sampling:快速决策,低延迟

Head Sampling 在 Span 被创建时立即做出采样决策,无需等待 Span 结束或收集后续属性。决策依据通常仅基于 Span 的固定属性(如 Span Name、Service Name、预定义的采样率)。

核心特征: 决策点位于 Trace 生命周期的起点,一旦决定,整个 Trace 的所有 Span 要么全部保留,要么全部丢弃(基于 TraceId 的一致性哈希)。

3.1.1 工作原理

  1. Span 创建:应用代码开始一个 Span,生成 TraceId 和 SpanId。
  2. 采样器介入:OpenTelemetry SDK 中的 Sampler 接口被调用,传入 TraceId、Span 名称、Span 属性等。
  3. 决策返回:采样器返回 Decision(DROP 或 RECORD_AND_SAMPLE)。
  4. 传播:决策结果通过 W3C TraceContext 传播到下游服务,确保同一 Trace 的所有 Span 采样决策一致。

3.1.2 典型实现:TraceIdRatioBased Sampler

// OpenTelemetry Java SDK 配置示例
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
    .setSampler(Sampler.traceIdRatioBased(0.1)) // 10% 采样率
    .build();

3.2 Tail Sampling:精准决策,高保真

Tail Sampling 在 Span 完成并收集到所有属性、状态码、事件和异常信息之后,由独立的采样处理器(通常运行在 Collector 端)做出决策。决策依据可以包含 Span 的完整上下文,如错误状态、延迟、特定属性值等。

关键优势: Tail Sampling 可以基于“结果”做决策,例如只保留包含错误的 Trace,或只保留延迟超过 P99 阈值的 Trace,从而大幅提升 SLI 相关数据的准确性。

3.2.1 工作原理

  1. Span 完成:所有 Span 数据(包括属性、事件、状态)被完整导出到 Collector。
  2. Trace 组装:Collector 中的 TailSamplingProcessor 根据 TraceId 将属于同一 Trace 的所有 Span 缓存并组装成完整 Trace。
  3. 策略评估:处理器依次评估用户配置的采样策略(如 ErrorSampler、LatencySampler、AndCompositeSampler)。
  4. 决策执行:满足策略条件的 Trace 被保留并发送到后端;不满足的 Trace 被丢弃。

3.3 核心区别对比

对比维度 Head Sampling Tail Sampling
决策时机 Span 创建时(Trace 起点) Span 完成后(Trace 终点)
决策依据 TraceId、Span 名称、固定属性 完整 Span 属性、状态码、延迟、错误信息
延迟影响 无额外延迟(同步决策) 引入缓存和等待延迟(异步决策)
内存开销 低(无状态) 高(需要在 Collector 中缓存 Trace)
SLI 准确性 低(无法区分错误/慢 Trace) 高(可精准保留关键 Trace)
配置复杂度 简单(仅需采样率) 复杂(需定义策略、超时、缓存大小)
适用场景 高吞吐、低延迟、通用监控 错误分析、延迟分析、SLI 保真

3.4 架构对比图

下图展示了 Head Sampling 与 Tail Sampling 在数据流路径上的核心差异。Head Sampling 在应用端快速决策,而 Tail Sampling 在 Collector 端进行二次筛选。

Head Sampling vs Tail Sampling 架构对比 Head Sampling(应用端) 应用服务(SDK) Sampler(TraceIdRatio) 决策:DROP / SAMPLE 直接发送到后端 Tail Sampling(Collector端) 应用服务(SDK) 全部发送到 Collector Collector 缓存 & 组装 Trace TailSamplingProcessor 策略评估(错误/延迟/属性) 保留关键 Trace → 后端

3.5 对 SLI 准确性的影响分析

采样策略的选择直接决定了 SLI 计算中使用的数据样本是否具有代表性。以下是两种策略对常见 SLI 指标的影响:

  • 错误率 SLI:Head Sampling 可能因随机丢弃而遗漏关键错误 Trace,导致错误率被低估;Tail Sampling 可通过 ErrorSampler 确保所有错误 Trace 被保留,错误率计算更准确。
  • 延迟 SLI(P99/P95):Head Sampling 的随机性可能使高延迟 Trace 被丢弃,导致延迟指标被低估;Tail Sampling 的 LatencySampler 可专门保留慢 Trace,使延迟分布更真实。
  • 可用性 SLI:Head Sampling 无法区分正常请求和故障请求,可用性计算偏差较大;Tail Sampling 可基于状态码精准筛选,可用性数据更可靠。
重要提醒: 如果 SLI 目标要求高精度(如 99.9% 可用性监控),必须使用 Tail Sampling 或结合 Head Sampling 与后端二次采样。仅依赖 Head Sampling 可能导致 SLI 数据严重失真,尤其在错误率较低的场景下。

3.6 混合策略:Head + Tail 联合采样

在实际生产环境中,最佳实践通常是 Head Sampling 做第一级粗筛(降低数据量),Tail Sampling 做第二级精筛(保证 SLI 准确性)。这种混合策略兼顾了低延迟和高保真:

# OpenTelemetry Collector 配置示例:Head + Tail 联合采样
receivers:
  otlp:
    protocols:
      grpc:

processors:
  # 第一级:Head Sampling(Collector 端模拟,基于 TraceId)
  probabilistic_sampler:
    hash_seed: 42
    sampling_percentage: 20  # 先降低 80% 数据量

  # 第二级:Tail Sampling(基于完整 Trace 信息)
  tail_sampling:
    decision_wait: 10s
    num_traces: 10000
    expected_new_traces_per_sec: 500
    policies:
      - name: error-policy
        type: status_code
        status_code: ERROR
      - name: latency-policy
        type: latency
        threshold_ms: 500

exporters:
  otlp:
    endpoint: "backend:4317"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [probabilistic_sampler, tail_sampling]
      exporters: [otlp]
实践建议: 在混合策略中,Head Sampling 的采样率建议设置在 10%-30%,以控制 Collector 的内存压力;Tail Sampling 的策略应优先保留对 SLI 影响最大的 Trace(如错误和慢请求),确保关键数据不丢失。