3. 采样策略基础:Head-based Sampling 与 Tail-based Sampling 原理对比
在分布式系统中,采样是控制可观测性数据量与成本的核心手段。OpenTelemetry 支持两种截然不同的采样策略:Head-based Sampling(头部采样) 和 Tail-based Sampling(尾部采样)。理解它们的原理、适用场景与权衡,是设计高效采样方案的前提。
3.1 什么是 Head-based Sampling?
Head-based Sampling 在 Span 创建之初(即请求进入服务时)就决定是否采样。决策点位于 Trace 的根部,一旦做出决定,整个 Trace 的所有 Span 都会遵循该决定(采样或丢弃)。
- 决策时机:Span 创建时(请求入口)
- 决策依据:采样率、请求属性(如 HTTP 方法、路径)、固定概率
- 优点:实现简单、性能开销低、无额外延迟
- 缺点:无法基于完整 Trace 信息(如错误、延迟)做决策
典型场景:高吞吐量、低延迟要求的微服务,只需固定比例样本用于容量规划或基础监控。
3.2 什么是 Tail-based Sampling?
Tail-based Sampling 在 Trace 完成之后(所有 Span 已到达后端)再做决策。采样器可以查看完整的 Trace 信息,包括错误码、延迟分布、Span 数量等,从而智能地保留有价值的 Trace。
- 决策时机:Trace 完成后(后端或 Collector 中)
- 决策依据:完整 Trace 属性(错误、延迟、路径、业务标签)
- 优点:能精准保留高价值 Trace(如错误、慢请求)
- 缺点:需要缓存 Span 数据,增加内存与延迟开销
关键区别:Head-based 是“先决定,再采集”;Tail-based 是“先采集,再决定”。
3.3 核心原理对比
| 对比维度 | Head-based Sampling | Tail-based Sampling |
|---|---|---|
| 决策时机 | Span 创建时(请求入口) | Trace 完成后(后端) |
| 决策信息 | 仅请求头、固定规则 | 完整 Trace 属性、错误、延迟 |
| 内存开销 | 低(无缓存) | 高(需缓存 Span 直到 Trace 完成) |
| 延迟影响 | 无额外延迟 | 增加 Trace 完成后的处理延迟 |
| 采样精度 | 粗粒度(固定概率) | 细粒度(基于内容) |
| 典型实现 | OpenTelemetry SDK 内置 Sampler | OpenTelemetry Collector 处理器 |
3.4 架构流程对比
下图展示了两种采样策略在分布式 Trace 采集链路中的位置与决策流程。
3.5 决策流程对比
以下流程图展示了两种策略在 Trace 生命周期中的决策路径差异。
3.6 适用场景与权衡
| 场景 | 推荐策略 | 原因 |
|---|---|---|
| 高吞吐量、低延迟要求 | Head-based | 无额外延迟,内存开销低 |
| 错误/慢请求诊断 | Tail-based | 能精准保留异常 Trace |
| 容量规划、基础监控 | Head-based | 固定比例样本足够 |
| 全链路业务分析 | Tail-based | 需要完整 Trace 信息 |
| 资源受限环境 | Head-based | 无需缓存,节省内存 |
| SLO 告警与根因分析 | Tail-based | 保留关键错误 Trace |
实践建议:生产环境中常采用混合策略——Head-based 控制整体成本,Tail-based 针对错误/慢请求做二次采样。OpenTelemetry Collector 支持通过
tail_sampling 处理器实现这种组合。
3.7 代码示例:Head-based 采样配置
// OpenTelemetry SDK 配置 Head-based 采样(固定概率 10%)
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased
# 10% 采样率
sampler = TraceIdRatioBased(rate=0.1)
provider = TracerProvider(sampler=sampler)
# 或者基于规则采样(保留所有错误 Trace)
from opentelemetry.sdk.trace.sampling import ParentBased, ALWAYS_ON
# 父 Span 采样则子 Span 跟随,错误 Span 始终采样
sampler = ParentBased(root=TraceIdRatioBased(rate=0.1),
remote_parent_sampled=ALWAYS_ON)
3.8 代码示例:Tail-based 采样配置
# OpenTelemetry Collector 配置 Tail-based 采样
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
processors:
tail_sampling:
decision_wait: 30s # 等待 Trace 完成的最大时间
num_traces: 10000 # 缓存的最大 Trace 数
expected_new_traces_per_sec: 1000
policies:
# 策略1:保留所有错误 Trace
- name: error-policy
type: status_code
status_code:
status_codes:
- ERROR
# 策略2:保留延迟超过 500ms 的 Trace
- name: latency-policy
type: latency
latency:
threshold_ms: 500
# 策略3:固定概率采样(兜底)
- name: probabilistic-policy
type: probabilistic
probabilistic:
sampling_percentage: 10
exporters:
otlp:
endpoint: your-backend:4317
service:
pipelines:
traces:
receivers: [otlp]
processors: [tail_sampling]
exporters: [otlp]
关键参数说明:
decision_wait 控制 Trace 缓存时间,过长会增加内存压力,过短可能导致慢 Trace 未完成就被丢弃。num_traces 限制最大缓存数,防止 OOM。
3.9 本章小结
- Head-based Sampling:在 Span 创建时决策,低开销、无延迟,但无法基于完整 Trace 信息做精细选择。
- Tail-based Sampling:在 Trace 完成后决策,能精准保留高价值 Trace,但需要缓存和额外处理延迟。
- 选择依据:根据业务需求、资源限制和诊断目标权衡。高吞吐场景用 Head-based,错误诊断场景用 Tail-based。
- 混合策略:生产环境推荐组合使用,用 Head-based 控制成本,用 Tail-based 兜底关键 Trace。