题干与适用场景
一个多租户 API 的延迟直方图在高峰期出现 p99 回归。团队不能把 user_id、完整 URL 或请求参数加入指标标签, 否则时间序列数量和成本会失控;但只看聚合桶又无法定位某一次慢请求。请设计 metrics exemplar,把少量 Trace 上下文 附着到指标样本,并让排障人员从图表跳到 Trace 后继续追查日志和依赖。
题目考察候选人能否区分指标的聚合语义与 exemplar 的外部引用。OpenTelemetry 将 exemplar 定义为与一次指标事件关联的 记录值,可带 traceid、spanid、观测时间和过滤后的属性;OpenMetrics 也要求 exemplar 有值、标签和时间戳。方案必须 保持主指标低基数,不能把 exemplar 当成隐藏标签系统。
面试官考察点
- 是否知道 exemplar 不改变直方图的 bucket、count、sum,引用的是聚合之外的观测。
- 是否能设计 Trace-based 或概率采样,并解释采样率、尾延迟命中率与成本的取舍。
- 是否使用
traceid、spanid等稳定引用,避免把完整请求、令牌或个人信息写入指标。 - 是否处理时间戳、乱序、后端丢弃、保留期和跨系统权限,而不是只画一条跳转链接。
- 是否能用低基数指标定位时间窗口,再用 exemplar 验证代表性并追到日志、依赖和版本。
回答前需要澄清的问题
- 指标后端、Trace 后端和可视化工具分别是什么,是否支持 exemplar 查询和深链接?
- 需要覆盖哪类指标:Histogram、Counter 还是 Gauge?p99 是服务端测量还是网关测量?
- 采样目标是所有错误、尾延迟,还是按租户和版本分层?跨地域是否有统一采样策略?
- Trace 的保留期、访问控制、脱敏规则和跨租户隔离如何约束引用?
- 允许多少内存、网络和存储开销,exemplar 丢失时主指标是否仍必须可用?
30 秒回答框架
“我保留低基数的延迟直方图,把少量命中采样的 traceid、spanid、值和观测时间作为 exemplar。采样优先覆盖错误和尾延迟, 不把用户标识放进指标标签;指标后端只存短期引用,点击后由权限校验跳到 Trace。我要验证桶统计未被改变、时间窗口能查到 exemplar、丢弃不会影响主指标,并按命中率、跳转成功率、额外内存和敏感字段泄露做门禁。”
分步骤深入解答
第一步:定义指标与 exemplar 的边界
延迟直方图的标签只保留 service、routetemplate、region、statusclass 等有限集合。每个样本的值仍计入 bucketcounts、count 和 sum;exemplar 只保存一条可追溯的观测引用,不能用 traceid 扩大时间序列维度。
第二步:设计采样与尾延迟命中
在请求上下文中先决定是否采样 Trace,再把已采样且满足错误、超过延迟阈值或分层预算的观测附到对应 Histogram 样本。可设置 每服务和每指标的固定容量,采用 reservoir 或环形缓冲区;采样率变化要记录版本,避免用命中数量直接推断流量比例。
第三步:编码引用和时间语义
exemplar 至少包含数值、标签集合和观测时间;引用 Trace 时优先使用 traceid 与 spanid。时间应接近观测发生时刻, 并与指标样本的时间窗口对齐。接收端可能截断标签或丢弃 exemplar,因此查询链路要允许“有指标、无 exemplar”。
latency_seconds_bucket{route="/checkout",le="1"} 982
# {trace_id="4f8...",span_id="91a...",build="2026.07.31"} 1.42 1753938000000第四步:控制隐私、基数与成本
禁止把 URL 原文、请求体、邮箱、授权令牌或用户 ID 写入 exemplar。build、区域等可选属性必须经过白名单和长度限制; OpenTelemetry 还提醒,被 View 从指标流中过滤的属性仍可能出现在 exemplar 的 filtered attributes 中,因此要单独配置脱敏。 估算每秒样本数、单条字节数、内存环形缓冲、远端写入和 Trace 查询成本,超预算时先降低 exemplar 采样而不是污染指标标签。
第五步:实现跨后端跳转
仪表盘按 PromQL 或等价查询先取时间范围内的 exemplar,再用 trace_id 拼接 Trace 后端的受控链接。链接服务应校验租户、 地域和权限,并对不存在、过期或跨环境的 Trace 返回可解释状态。日志和 Span 使用同一 Trace Context,但不要求三种信号共用存储。
第六步:验证退化与发布门禁
用固定流量回放比较启用前后的 bucket、count、sum 和 p99,证明 exemplar 不改变聚合结果;再注入慢请求和错误请求,检查命中率、 时间戳、Trace 跳转和跨租户隔离。故意让 exemplar 后端延迟、截断或不可用,确认指标仍可查询。发布门禁至少包含敏感字段扫描、 内存上限、远端写入失败率、跳转成功率和按服务/版本的采样公平性。
高质量示范回答
“我先把指标标签限制为路由模板、状态类别和区域,直方图继续负责 p99 聚合。请求进入时沿用 Trace Context;当请求被 Trace 采样且命中错误或尾延迟策略时,向对应 Histogram 样本附加 traceid、spanid、值和时间戳。exemplar 使用白名单属性, 不携带用户标识或请求体,View 过滤和导出前再做一次脱敏。”
“Prometheus/OpenMetrics 查询只取指定时间窗口的 exemplar,仪表盘经权限校验后跳到 Trace;Trace 不存在时保留指标结果并提示引用 已过期。压测比较 bucket/count/sum/p99,故障演练覆盖后端丢弃、乱序、跨租户访问和预算耗尽。我们用命中率、排障到首个 Trace 的时间、额外内存、写入成本和敏感字段扫描结果决定采样预算,而不是为了提高命中率把高基数标签塞进指标。”
常见错误
- 把
trace_id放进指标标签 → 时间序列爆炸 → 把它放在 exemplar 引用中。 - 只按固定概率采样 → p99 或错误样本可能长期缺失 → 叠加尾延迟、错误和分层预算。
- 忽略 exemplar 时间戳 → 图表时间窗查不到对应 Trace → 记录观测时间并校验窗口。
- 把过滤后的敏感属性当作安全 → 属性仍可能随 exemplar 导出 → 单独白名单、脱敏和权限检查。
- 后端丢 exemplar 就报警为指标故障 → 聚合与引用耦合 → 允许指标可用、exemplar 缺失并监控两者。
- 无限保存引用 → 内存和成本不可控 → 固定容量、保留期和采样预算。
追问及应对
追问一:exemplar 会改变 p99 吗?
不会。exemplar 的值对应的观测已经参与 Histogram 的 bucket、count 和 sum;它额外提供引用和上下文,不应创建新的指标序列或重复计数。
追问二:为什么不把完整 URL 作为 exemplar 属性?
完整 URL 可能包含用户信息、令牌和无限基数。优先使用路由模板、版本等白名单字段;需要定位参数时在受控 Trace 或日志中查看,并按租户权限脱敏。
追问三:exemplar 全部丢失时系统如何工作?
指标查询和告警继续依赖聚合序列。排障人员只能失去从指标到 Trace 的快捷入口,因此应监控 exemplar 接收率和跳转成功率,不能让引用链路阻塞指标写入。
追问四:如何证明采样没有偏向某个租户?
按租户、区域、版本和结果类别统计请求量、采样量与命中量,设置最小保障和最大预算;比较错误与尾延迟命中率,发现偏差后调整分层采样,而不是提高全局比例。