采样率设成 1% 那次,最慢的那批请求一条都没采到:可观测性三支柱的 4 个反直觉细节
title: 采样率设成 1% 那次最慢的那批请求一条都没采到可观测性三支柱的 4 个反直觉细节tags: 可观测性,链路追踪,OpenTelemetry,日志,指标category: 后端一个查了三个小时也没查出来的慢接口去年 6 月运营反馈履约中心的订单详情接口偶尔要转好几秒。我们看了监控大盘P99 是 340msP999 是 2.1 秒QPS 大约 600。数字不算漂亮但也不至于让人卡到抱怨。真正难受的是查不出来。我们有链路追踪Jaeger 里翻了半小时找到的 trace 全是 50-80ms 的正常请求一条超过 1 秒的都没有。团队里有人说是不是采样没采到我当时的反应是采样率 1%600 QPS一分钟也有 360 条 trace怎么可能一条慢的都没有后来才想明白这是个概率问题也不完全是概率问题。P999 意味着 1000 个请求里有 1 个慢1% 的采样意味着 100 个请求里采 1 个。两个事件独立的话采到一条慢请求的期望是每 10 万个请求一次600 QPS 下差不多每 3 分钟一条。理论上翻半小时应该能看到十条。但我们的采样是头部采样head-based sampling在链路入口就用 traceId 的哈希决定采不采。而慢请求集中在一个特定的场景用户订单里包含跨境商品时会额外调一次报关信息服务。这类订单占全量的 0.8%。1% 的均匀采样 × 0.8% 的场景占比采到的概率是万分之八——半小时 108 万个请求期望 8.6 条而这 8.6 条散落在几十万条 trace 里肉眼根本翻不到。采样率不是采到多少是采到什么。这是第一个反直觉的地方。从头部采样切到尾部采样我们的方案是引入 OpenTelemetry Collector 的tail_sampling处理器把决策从入口挪到链路结束之后。配置大概是这样processors: tail_sampling: decision_wait: 10s num_traces: 100000 policies: - name: slow-traces type: latency latency: { threshold_ms: 800 } - name: error-traces type: status_code status_code: { status_codes: [ERROR] } - name: baseline type: probabilistic probabilistic: { sampling_percentage: 1 }三条策略是或的关系慢的全采、错的全采、剩下的按 1% 采基线。切过去当天Jaeger 里立刻出现了几百条 2 秒以上的 trace一眼看到报关信息服务那一段占了 1.8 秒。Java 侧要配合改一件事确保 span 上带了足够的属性否则采到了也定位不了。我们在 Feign 拦截器里补了业务维度public class TracingFeignInterceptor implements RequestInterceptor { Override public void apply(RequestTemplate template) { Span span Span.current(); if (!span.getSpanContext().isValid()) { return; // 没有活跃 span直接返回 } // 业务维度跨境订单、店铺 ID、渠道用于事后按维度筛 trace OrderContext ctx OrderContextHolder.get(); if (ctx ! null) { span.setAttribute(order.cross_border, ctx.isCrossBorder()); span.setAttribute(order.shop_id, ctx.getShopId()); span.setAttribute(order.channel, ctx.getChannel()); } // 把 traceId 透传到下游的自定义头方便日志侧关联 template.header(X-Trace-Id, span.getSpanContext().getTraceId()); } }逐行说下考虑第 6-8 行必须判空。在异步线程、定时任务里Span.current()拿到的是Span.getInvalid()直接setAttribute不报错但数据全丢白写。第 12 行的cross_border就是那次事故留下的教训。事后我们才能在 Jaeger 里用order.cross_bordertrue一键筛出问题链路。第 17 行透传 traceId 到 header是为了让下游服务在日志里也能打出同一个 traceId。OpenTelemetry 的 W3Ctraceparent头本身能透传但有些老服务不认加一个明文头成本很低。有一个坑要提醒setAttribute写入的属性数量在 OpenTelemetry Java SDK 里默认上限是 128 个超出会被静默丢弃。我们曾经在循环里给 span 打过订单里每个 SKU 的 ID30 个 SKU 就写 30 个属性跟其他属性一叠加就顶到上限导致后面真正重要的属性被丢。改成拼成一个逗号分隔的字符串就好了。日志traceId 不落盘等于白追Trace 能告诉你哪一段慢但告诉不了你为什么慢。那次报关服务的 1.8 秒最终是靠日志定位到它在做一次没走索引的模糊查询。前提是日志里得有 traceId。我们用的是 Logback MDC接入方式是 OpenTelemetry 的 logback appenderpublic class TraceIdMdcFilter extends OncePerRequestFilter { private static final String MDC_TRACE_ID traceId; private static final String MDC_SPAN_ID spanId; Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) throws ServletException, IOException { SpanContext sc Span.current().getSpanContext(); boolean valid sc.isValid(); if (valid) { MDC.put(MDC_TRACE_ID, sc.getTraceId()); MDC.put(MDC_SPAN_ID, sc.getSpanId()); resp.setHeader(X-Trace-Id, sc.getTraceId()); // 回吐给前端方便客服报障 } try { chain.doFilter(req, resp); } finally { if (valid) { MDC.remove(MDC_TRACE_ID); // 必须清理线程会被复用 MDC.remove(MDC_SPAN_ID); } } } }第 21-22 行的清理是重点。MDC 底层是ThreadLocalTomcat 的线程池会复用线程。不清理的话下一个请求如果因为某些原因没进这个 filter比如静态资源、错误页就会打出上一个请求的 traceId。我们出过一次这个问题排查时按 traceId 搜出来的日志横跨了三个不相干的订单非常迷惑。第 14 行把 traceId 写回响应头是我强烈推荐的做法。客服接到用户报障时让用户提供一下页面上的错误码我们把 traceId 后 8 位显示在错误页比让用户描述我大概几点钟点的高效一百倍。异步场景要额外处理。我们用TaskDecorator把 MDC 传到线程池Bean public TaskDecorator mdcTaskDecorator() { return runnable - { MapString, String parent MDC.getCopyOfContextMap(); // 提交时的父线程上下文 Context otelContext Context.current(); // OTel 上下文 return () - { MapString, String previous MDC.getCopyOfContextMap(); if (parent ! null) { MDC.setContextMap(parent); } try (Scope ignored otelContext.makeCurrent()) { // 让子线程 span 挂到父链路 runnable.run(); } finally { if (previous ! null) { MDC.setContextMap(previous); } else { MDC.clear(); } } }; }; }第 4 行在提交任务的线程里拿快照第 6 行返回的 lambda 在执行任务的线程里跑这个先后关系搞反了就完全无效。我见过有人把getCopyOfContextMap()写在 lambda 里面等于拿了执行线程自己的上下文等于没做。第 11 行的makeCurrent()配合 try-with-resources保证子线程结束后作用域正确关闭。少了这一步异步任务里创建的 span 会变成孤儿根 span在 Jaeger 里显示成一条独立的链路。指标为什么 P99 会骗人三支柱里最容易被误用的是指标。我们踩过的坑是分位数聚合。假设有 10 个 Pod每个 Pod 上报自己的 P99。Prometheus 里做avg(http_request_p99)得到的数字不是集群的 P99——分位数不可平均这是数学事实。10 个 Pod 各自的 P99 平均值可能显著低于整体 P99。正确做法是上报直方图histogram在查询时聚合Bean public MeterFilter histogramFilter() { return new MeterFilter() { Override public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) { if (id.getName().startsWith(http.server.requests)) { return DistributionStatisticConfig.builder() .percentilesHistogram(true) // 输出 _bucket 序列 .serviceLevelObjectives( Duration.ofMillis(50).toNanos(), Duration.ofMillis(100).toNanos(), Duration.ofMillis(200).toNanos(), Duration.ofMillis(500).toNanos(), Duration.ofSeconds(1).toNanos(), Duration.ofSeconds(3).toNanos()) .build().merge(config); } return config; } }; }第 9 行percentilesHistogram(true)让 Micrometer 输出 Prometheus 的_bucket时间序列之后就能用histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le))算出真正的集群 P99。第 10-16 行手动指定了 SLO 边界。默认的percentilesHistogram会生成 60 多个桶每个桶是一条时间序列乘上 uri、method、status 几个标签序列数很容易爆炸。我们上线第一版没限制桶Prometheus 的内存从 6GB 涨到 21GB被 OOMKill 了两次。指定 6 个业务真正关心的边界之后序列数降到原来的十分之一。这是第三个反直觉点指标的成本不在于打点而在于标签的笛卡尔积。一个 uri 标签如果把路径参数也带进去比如/order/12345几万个订单号就是几万条时间序列。我们现在的规矩是任何新增标签必须能说清它的基数上限。三支柱各自的定位维度TraceLogMetric回答的问题哪一段慢/错为什么慢/错有没有问题、多严重数据粒度单请求单事件聚合存储成本高可采样降低最高低查询延迟秒级秒到分钟级毫秒级适合告警不适合部分适合最适合保留周期我们的配置7 天14 天热 90 天冷15 个月我们的排障动线固定是Metric 发现异常 → Trace 定位到具体服务和 span → Log 看那个 span 期间的详细上下文。三步走缺一步都会卡住。有人主张只留日志日志里什么都有。我不认同。600 QPS 的服务一天的日志量是几百 GB没有 trace 做索引你根本不知道该搜哪一段。反过来只有 trace 没有日志也不行span 里塞不下堆栈和 SQL 语句。复盘数据那次改造前后的对比指标改造前改造后慢请求 trace 捕获率约 1%约 100%800ms 全采平均排障耗时P503.2 小时25 分钟Trace 存储量日均 40GB日均 52GBPrometheus 内存21GBOOM 过7.4GBCollector CPU尾采样—常驻 2.3 核尾部采样不是免费的。Collector 要把一条链路的所有 span 缓存decision_wait时长我们配 10 秒等链路结束才决策。num_traces: 100000这个上限对应的内存我们实测约 3.5GB。链路 QPS 再翻一倍的话Collector 就得横向扩而横向扩又带来新问题同一条 trace 的 span 必须路由到同一个 Collector 实例否则尾采样看到的是残缺链路。我们是在 Collector 前面加了一层按 traceId 做一致性哈希的loadbalancingexporter 解决的。我的取舍建议如果团队刚开始建可观测性我的建议顺序是先指标再日志最后 trace。理由很实际——指标的投入产出比最高一个 Prometheus 加几个 Grafana 面板一天就能让你知道服务有没有问题。日志次之大部分团队本来就有加个 traceId 就能用。Trace 的接入成本最高还要改代码、改配置、扩基础设施在服务数少于 10 个的时候收益有限。反过来服务数超过 30 个、调用层级超过 4 层之后trace 从锦上添花变成没有就没法干活。我们是在服务数到 60 多个的时候才彻底重视 trace 的回头看晚了大概一年。对采样策略我的判断是头部采样适合成本敏感、故障率低的成熟系统尾部采样适合排障压力大、慢请求分布不均匀的系统。混合用也可以——入口做 10% 的头部采样保底Collector 再做尾部筛选能省掉 90% 的 span 传输带宽。最后留个问题你们的 trace 保留多久我们定的是 7 天理由是超过一周的链路数据几乎没人查。但每次做季度容量复盘时又会有人抱怨想看看三个月前的链路对比。你们是怎么平衡存储成本和回溯需求的评论区聊聊。

相关新闻

蝉鸣拉开暑假的序幕

蝉鸣拉开暑假的序幕

燥热的蝉鸣拉开暑假的序幕,长达两月的假期,褪去了校园里紧凑的课业压力,生活慢慢放缓了脚步。不用早起追赶早读,不用整日埋在习题里,我们拥有充足的时间按照自己的节奏生活。有人奔赴山川大海,和家人结伴出…

2026/8/6 19:49:30 阅读更多 →
为什么选择Inkling-Small-mlx-3bit?解密Apple Silicon专属AI模型的独特优势

为什么选择Inkling-Small-mlx-3bit?解密Apple Silicon专属AI模型的独特优势

为什么选择Inkling-Small-mlx-3bit?解密Apple Silicon专属AI模型的独特优势 【免费下载链接】Inkling-Small-mlx-3bit 项目地址: https://ai.gitcode.com/hf_mirrors/mlx-community/Inkling-Small-mlx-3bit Inkling-Small-mlx-3bit是专为Apple Silicon优化的…

2026/8/6 19:49:30 阅读更多 →
UE5.5与MQTT实现JSON通信的实时交互方案

UE5.5与MQTT实现JSON通信的实时交互方案

1. 项目概述:UE 5.5与MQTT的JSON通信方案在实时交互应用开发中,Unreal Engine 5.5与MQTT协议的结合正在成为物联网、数字孪生等领域的标配方案。这个技术栈的核心价值在于:通过C实现的高性能MQTT客户端,能够以JSON格式在虚幻引擎中…

2026/8/6 19:48:30 阅读更多 →

最新新闻

终极iOS开发效率工具:HYBMasonryAutoCellHeight让动态Cell高度计算从未如此简单

终极iOS开发效率工具:HYBMasonryAutoCellHeight让动态Cell高度计算从未如此简单

终极iOS开发效率工具:HYBMasonryAutoCellHeight让动态Cell高度计算从未如此简单 【免费下载链接】HYBMasonryAutoCellHeight A very helpful category for calculating the height of cell automatically. 项目地址: https://gitcode.com/gh_mirrors/hy/HYBMasonr…

2026/8/6 20:33:46 阅读更多 →
发现植物大战僵尸的隐藏玩法:PVZTools修改器完全指南 [特殊字符][特殊字符]‍♂️

发现植物大战僵尸的隐藏玩法:PVZTools修改器完全指南 [特殊字符][特殊字符]‍♂️

发现植物大战僵尸的隐藏玩法:PVZTools修改器完全指南 🌱🧟‍♂️ 【免费下载链接】pvztools 植物大战僵尸原版 1.0.0.1051 修改器 项目地址: https://gitcode.com/gh_mirrors/pv/pvztools 你是否想过在植物大战僵尸中创造属于自己的游…

2026/8/6 20:33:46 阅读更多 →
SuperRDP终极指南:三步解锁Windows远程桌面完整功能

SuperRDP终极指南:三步解锁Windows远程桌面完整功能

SuperRDP终极指南:三步解锁Windows远程桌面完整功能 【免费下载链接】SuperRDP Super RDPWrap 项目地址: https://gitcode.com/gh_mirrors/su/SuperRDP SuperRDP是一款专为Windows系统设计的强大工具,能够一键解除微软对远程桌面功能的限制。无论…

2026/8/6 20:33:46 阅读更多 →
数据库性能优化实战:从表结构到SQL调优

数据库性能优化实战:从表结构到SQL调优

1. 数据库性能优化概述数据库性能优化是每个DBA和开发者的必修课。我见过太多项目初期运行流畅,随着数据量增长逐渐变得卡顿,最终不得不重构的案例。性能优化不是等到系统崩溃时才该考虑的事情,而应该贯穿整个项目生命周期。优化的核心在于平…

2026/8/6 20:33:46 阅读更多 →
【AI产品经理】第三章 电商实战

【AI产品经理】第三章 电商实战

第三章 电商实战一、名称解释 1. 电子商务 (E-commerce) 通过互联网等电子手段进行的商品或服务的交易活动。按交易主体分为 B2C(企业到消费者)、B2B(企业到企业)、C2C(消费者到消费者)、O2O(线…

2026/8/6 20:32:46 阅读更多 →
MySQL索引失效原理与优化实践

MySQL索引失效原理与优化实践

1. 索引失效的本质:当优化器决定放弃索引 MySQL索引失效的根本原因在于查询优化器的成本计算机制。优化器会根据统计信息估算全表扫描和索引扫描的成本,当它认为全表扫描更高效时,就会放弃使用索引。这种"失效"实际上是优化器的主动…

2026/8/6 20:32:46 阅读更多 →

日新闻

深入解析LimboAI C++内核:架构设计与性能优化实战

深入解析LimboAI C++内核:架构设计与性能优化实战

1. 项目概述:为什么我们需要深入LimboAI的C内核?如果你是一名使用Godot引擎的游戏开发者,尤其是对AI行为逻辑有较高要求的项目,那么LimboAI这个名字你大概率不会陌生。它作为Godot 4生态中一个备受瞩目的行为树与状态机插件&#…

2026/8/6 0:00:06 阅读更多 →
Unity 2D游戏敌人AI系统:基于PlayMaker状态机与2D Toolkit的实战开发

Unity 2D游戏敌人AI系统:基于PlayMaker状态机与2D Toolkit的实战开发

1. 项目概述与核心思路大家好,我是老张,一个在游戏开发一线摸爬滚打了十多年的老码农。今天咱们接着聊《空洞骑士》风格2D动作游戏的Demo制作。上一期我们搭好了基础框架,处理了角色移动和碰撞,这一期,我们要让游戏世界…

2026/8/6 0:00:06 阅读更多 →
被动防火门市场前景发展趋势

被动防火门市场前景发展趋势

被动防火门依靠材质结构、密闭构造阻隔烟火蔓延,无需电控启动,是建筑被动消防系统核心构件,行业依托新规管控、城市更新、工业安全升级迎来稳定扩容,整体朝着合规化、专项化、低碳化、智能化方向发展。现阶段 GB12955‑2024 新版国…

2026/8/6 0:00:06 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/5 15:00:43 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/5 13:13:56 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/5 10:20:36 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/5 23:28:39 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/5 21:00:14 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/5 23:46:51 阅读更多 →