尾部采样不是免费的 — 三笔成本
一句话总结
头部采样在一切尚未发生时就作决定,因此会按照稀有事件本身的稀有程度,精准地将其丢弃。尾部采样根据结果决定,却要付出三种成本:不会减少网络流量、消耗大量内存,并要求基于 trace ID 路由。头部已经丢弃的内容,尾部无法复原。
为什么需要它
对多数组织而言,全量存储成本不可接受。问题在于丢弃什么。假设使用 1% 头部采样,调查每小时 5 个错误。
보존되는 오류 트레이스 = 5 × 0.01 = 시간당 0.05건
1건을 보려면 평균 20시간 대기
这就是头部采样的实际结局:需要调查时,那条 trace 并不存在;剩下的又全是正常请求,没有查看价值。
工作原理
尾部采样在 trace 完成前将 span 缓存在内存,看到结果后再决定。存在错误就保留,速度慢就保留,其余只保留少量。每条策略都是“保留条件”,多条策略中任一匹配就会保留。因此,要排除健康检查,应使用反转匹配的 invert_match。
| 项目 | 头部采样 | 尾部采样 |
|---|---|---|
| 决策时刻 | 创建 root span 时 | 经过 decision_wait 后 |
| 保留错误、慢请求 | 只按概率 | 通过规则 100% 保留 |
| agent、网络成本 | 按比例降低 | 不降低 |
| 后端存储成本 | 降低 | 降低 |
| Collector 内存 | 可忽略 | 按每秒 trace 数 × 等待时间缓存 |
| 运维要求 | 无 | 必须基于 trace ID 路由 |
成本 1——网络不会减少。 要做决定,所有 span 必须先到达 Collector,因此节省的只有后端存储和索引。Collector 自身 CPU 与网络消耗反而增加。
成本 2——内存。 decision_wait 期间必须保留全部 span,计算式很简单。
초당 트레이스 수 × decision_wait(초) × 트레이스당 스팬 수 × 스팬 크기 = 버퍼 메모리
예: 10,000 trace/s × 30s × 12 스팬 × 1.2KB
= 4,320,000 KB = 약 4,219 MB (여유 2배면 약 8,438 MB)
num_traces 是同时驻留内存的 trace 数上限,超限时最旧的 trace 会被强制决定。所需值也来自同一乘法:每秒 trace 数 × decision_wait。10,000 trace/s、等待 30 秒,需要 300,000。
成本 3——路由。 同一 trace 的所有 span 必须到达同一个 Collector 实例。横向扩容后若在前面放普通负载均衡器,一个 trace 的 span 会散到多个实例,各实例只看碎片便作决定,结果就是随机断裂的 trace。解决办法是在前层部署使用 routing_key: traceID 的 loadbalancing exporter。
同理,DaemonSet 中不能进行尾部采样。 每节点一个 Collector 时,一条 trace 的 span 会分散到不同节点,各 agent 只能看到一部分。尾部采样只能放在 Deployment(gateway)层。
现场会遇到的情况
代价最高的一次事故,是流量增长后仍把 num_traces 设为 50,000。峰值为 10,000 trace/s,decision_wait 为 30 秒,需要 300,000;上限只有六分之一,旧 trace 持续被强制决定。叠加内存压力后 Collector 反复 OOM 重启,丢失 5 分钟数据。指标几乎没有异常——数据仍在“正常”发送,只是后端缺少特定 trace。
教训有两条。第一,启用 tail_sampling 前先做乘法。第二,数据消失而指标安静时,暂时从 pipeline 移除 tail_sampling 再复现,这是最快区分方法。
实际组合通常是:超高流量服务先在头部降到 10~50%,再在其上使用尾部采样捞出错误和慢请求。头部已丢弃的内容尾部无法恢复,因此头部比例原则上应在可承受范围内尽量高。
下一实验要做什么
在 /root/otca-sampling/ 下编写五条尾部采样策略:100% 保留错误和延迟的结果策略;用 invert_match 排除健康检查;用 and 组合 VIP 租户与延迟;以及保留其余请求的概率策略。随后配置前层 loadbalancing exporter,亲自计算缓冲内存并写入文件,最后编写包含该数值的 OpenTelemetryCollector CR。