bigywater.tech
微服务 · 64

采样:头部采样与尾部采样的差别,采样率对存储量和排障能力的影响

头部采样和尾部采样差在哪?采样率降到 1% 会丢掉多少出错的 trace?多个服务的采样决定不一致会怎样?尾部采样要付出什么代价?出错的 span 来晚了会怎么样?这篇用 OpenTelemetry SDK 和 Collector 的 tail_sampling 逐项量出来。

0 / 49
头部采样比例 1% 时,保留下来的出错的 trace 数(10% 时 11 / 49)
308 / 3000
三个服务各自用不同比例(10%、50%、100%)独立采样时,完整的 trace 数
5 / 100
出错的 span 比 decision_wait 晚到 4 s 时,尾部采样保留下来的出错 trace 数(晚 0.5 s 是 100 / 100)

头部采样

实测 · 本机执行输出
  采样比例 | 保留的 trace 数 | 保留的 span 数 | 出错的 trace 保留 | 慢 trace 保留 | 正常 trace 保留
  -----+-------------+------------+--------------+------------+------------
  100% | 3000        | 12000      | 49 / 49      | 95 / 95    | 2856 / 2856
  10%  | 271         | 1084       | 11 / 49      | 6 / 95     | 254 / 2856 
  1%   | 31          | 124        | 0 / 49       | 0 / 95     | 31 / 2856  
  头部采样在 trace 开始时按 trace id 做决定,那时还不知道这个 trace 之后会不会出错、会不会变慢,所以出错和慢的 trace 和正常的 trace 以同样的比例被丢弃

在 trace 开始时按 trace id 决定,不知道之后的错误和耗时,出错和慢的 trace 同样被丢掉。

采样决定的一致性

实测 · 本机执行输出
  配置                         | 后端收到至少 1 个 span 的 trace 数 | 收到 3 个 span 的(完整) | 收到 2 个 span 的 | 只收到 1 个 span 的
  ---------------------------+---------------------------+-------------------+---------------+---------------
  (a)A 按 10% 决定,B、C 跟随父 span | 291                       | 291               | 0             | 0             
  (b)各自独立:A 10%、B 50%、C 100% | 3000                      | 308               | 1173          | 1519          
  (c)各自独立,同一个比例 10%          | 308                       | 308               | 0             | 0             
  跟随父 span 的采样标志(或者所有服务使用同一个确定性比例)时,一个 trace 要么三个 span 都在,要么都不在;各服务用不同的比例独立决定,就会出现只有一部分 span 的残缺 trace

跟随父 span 的采样标志或者所有服务使用同一个确定性比例,trace 要么完整要么没有;不同比例各自独立会产生大量残缺 trace。

尾部采样

实测 · 本机执行输出
  指标                          | 尾部采样后      | 备注            
  ----------------------------+------------+---------------
  保留的 trace 数                 | 288        | 全部 3000       
  出错的 trace                   | 49 / 49    |               
  慢 trace                     | 95 / 95    |               
  正常 trace                    | 144 / 2856 | 目标 5%(约 142 个)
  保留下来的 trace 中 span 完整(4 个)的 | 288 / 288  |               
  file exporter 文件大小(字节)      | 399,817    | 全量 2,761,015  
  应用生成 3000 个 trace 花了 0.2 s;从应用 flush 完到 Collector 的采样输出文件第一次有内容用了 3.2 s(decision_wait 3 s);Collector 内存(RSS)在应用发完之后 185 MB

等 trace 到齐再决定,出错和慢的全留,存储量只有全量的一小部分,代价是延迟和 Collector 的内存。

迟到的 span

实测 · 本机执行输出
  出错的 span 比其他 span 晚发出 | 保留的 trace 数(共 100 个出错的) | 其中含出错 span 的 | 其中 4 个 span 都在的
  ----------------------+-------------------------+--------------+----------------
  0.5 s                 | 100                     | 100          | 100            
  4.0 s                 | 5                       | 5            | 5              
  tail_sampling 在 decision_wait 到期时按当时收到的 span 做决定;出错 span 比 decision_wait 还晚到,决定时这个 trace 里还没有错误,出错的 trace 就按 5% 的概率保留
ERRORS {}

span 晚于 decision_wait 到达时,决定已经做出,出错的 trace 可能被丢掉。

分步演示

采样
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。OpenTelemetry Python SDK 1.45.1,Collector contrib 0.163.1(tail_sampling 处理器,file exporter);3000 个 trace 是固定种子生成的(出错 49、慢 95、正常 2856),span 的时间戳是按设定值写入的(慢 trace 总耗时 800 ms);Collector 的概率采样用 trace id 的哈希,所以正常 trace 的保留数每次运行不同(开发中的另一次运行是 130);每种配置只跑了 1 次
    • 没有验证:Collector 多实例部署时 tail_sampling 需要按 trace id 路由到同一个实例(load-balancing exporter);num_traces 与 decision_wait 对内存的影响;大流量下 Collector 的吞吐上限;自适应采样、动态调整比例;采样对指标(RED)准确性的影响;Jaeger、Tempo 自己的采样配置
    京ICP备2026067502号-1京公网安备11010502063487号