3. 分布式追踪基础概念:Trace、Span、SpanContext、TraceId、SpanId、Parent-Child关系、采样率

在深入存储选型之前,必须统一团队对分布式追踪核心数据模型的理解。值班台作为故障响应的一线工具,其追踪数据的结构直接决定了查询效率、关联分析能力以及存储成本。本节将拆解构成一次完整追踪的原子单元。

3.1 核心概念全景

分布式追踪的本质是记录一次请求在多个服务、多个进程间的完整传播路径。以下六个概念构成了追踪数据模型的基石:

概念 定义 类比(快递物流) 关键属性
Trace 一次完整请求的生命周期,跨多个服务 一个包裹从发货到签收的全程 全局唯一 TraceId
Span Trace 中的一个工作单元,代表一次操作 包裹经过的每个 SpanId、操作名、开始/结束时间
SpanContext 跨进程传递的追踪上下文 包裹上的运单标签 TraceId、SpanId、Baggage
TraceId Trace 的全局唯一标识 运单号 16/32 字节,全局唯一
SpanId Span 的唯一标识 内部工单号 8 字节,Span 内唯一
Parent-Child Span 之间的因果关系 上一站到下一站的依赖 Child Span 记录 ParentSpanId
值班台视角: 在故障排查时,Trace 是“案件卷宗”,Span 是“笔录条目”,SpanContext 是“案件编号”。没有清晰的 Parent-Child 关系,就无法还原调用链的因果逻辑。

3.2 Trace 与 Span:追踪的骨架与血肉

Trace 是一个有向无环图(DAG),由多个 Span 通过 Parent-Child 关系连接而成。每个 Trace 有且仅有一个根 Span(Root Span),代表请求的入口点。

Span 是追踪的最小工作单元,包含以下核心字段:

  • 操作名(Operation Name):如 HTTP GET /api/orders、MySQL query
  • 开始时间戳 & 持续时间:精确到微秒
  • 标签(Tags):键值对,记录元数据(如 http.status_code=200)
  • 日志(Logs):时间戳 + 结构化消息,记录事件(如异常堆栈)
  • SpanContext:用于跨进程传播的上下文
// 典型 Span 结构(JSON 表示)
{
  "traceId": "abc123def456",
  "spanId": "span001",
  "parentSpanId": "root_span",
  "operationName": "HTTP GET /api/users",
  "startTime": 1710000000123456,
  "duration": 150000,  // 微秒
  "tags": {
    "http.method": "GET",
    "http.status_code": 200,
    "peer.service": "user-service"
  },
  "logs": [
    {
      "timestamp": 1710000000123500,
      "fields": {
        "event": "cache.miss",
        "key": "user:123"
      }
    }
  ]
}

3.3 TraceId 与 SpanId:唯一性保障

这两个 ID 是追踪数据的“主键”,决定了存储系统的索引效率和查询性能。

属性 TraceId SpanId
生成时机 请求入口(网关/负载均衡) 每个 Span 创建时
长度(W3C 标准) 16 字节(128 bit),hex 编码 32 字符 8 字节(64 bit),hex 编码 16 字符
生成算法 随机数 + 时间戳组合 随机数 / 自增序列
全局唯一性 必须全局唯一 Span 内唯一即可
存储索引 主索引,用于聚合 Trace 辅助索引,用于关联查询
选型陷阱: 部分存储系统(如 Elasticsearch)对 128 bit 的 TraceId 索引性能较差。如果 TraceId 采用 32 字符 hex 字符串,建议使用 keyword 类型并启用 eager_global_ordinals;如果采用二进制存储,需确认后端是否支持。

3.4 Parent-Child 关系:构建调用链拓扑

Parent-Child 关系通过 parentSpanId 字段实现。每个 Span(除根 Span 外)必须记录其父 Span 的 SpanId。这种关系形成了三种典型的调用模式:

  • 顺序调用(Sequential):服务 A 调用服务 B,B 返回后再调用 C。Span 呈链式结构。
  • 并行调用(Parallel):服务 A 同时调用服务 B 和 C。B 和 C 的 Span 共享同一个 ParentSpanId。
  • 嵌套调用(Nested):服务 A 调用服务 B,B 内部又调用 C。形成深度嵌套的树形结构。
Trace 与 Span 的 Parent-Child 关系拓扑 Root Span API Gateway Span B: Auth parentSpanId: Root Span C: Order parentSpanId: Root Span D: Payment parentSpanId: Root Span B1: JWT Verify parentSpanId: Span B Span C1: DB Query parentSpanId: Span C Span D1: 3rd API parentSpanId: Span D 说明: • 根 Span 无 parentSpanId,是 Trace 的入口点 • Span B/C/D 共享 parentSpanId=Root,表示并行调用;Span B1 的 parentSpanId=Span B,表示嵌套调用

3.5 SpanContext:跨进程的追踪信使

SpanContext 是分布式追踪的“粘合剂”,负责在服务之间传递追踪状态。它通常通过 HTTP 头、gRPC 元数据或消息队列属性进行传播。

W3C Trace-Context 标准 定义了两种传播头:

  • traceparent:携带 TraceId、SpanId 和 TraceFlags(采样标记)。格式:00-{traceId}-{spanId}-{flags}
  • tracestate:携带供应商特定的扩展数据,用于兼容不同追踪系统
// traceparent 示例
// 版本- TraceId(32 hex) - SpanId(16 hex) - Flags(2 hex)
traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01

// tracestate 示例
tracestate: congo=t61rcWkgMzE,rojo=00f067aa0ba902b7
值班台实践: 在微服务网关层统一注入 traceparent 头,确保所有下游服务都能继承同一个 TraceId。如果发现某个服务“断链”(Trace 不完整),优先检查该服务是否正确解析和传递了 SpanContext。

3.6 采样率:成本与完整性的平衡

采样率决定了“记录多少比例的请求”。在值班台场景中,采样策略直接影响故障复现能力和存储成本。

常见采样策略对比:

策略 原理 适用场景 值班台推荐度
固定比率采样 按固定百分比(如 10%)采样 低流量、稳定系统 ★★☆☆☆
基于速率采样 每秒固定采样 N 条 Trace 高流量、成本敏感 ★★★☆☆
基于错误采样 100% 采样错误 Trace + 随机采样正常 Trace 故障排查、SLO 监控 ★★★★★
尾部采样(Tail-Based) 请求完成后,根据结果(延迟、错误)决定是否保留 高精度故障分析 ★★★★☆
头采样(Head-Based) 请求入口处决定是否采样,后续继承 简单、低延迟 ★★★☆☆
采样策略决策流程 请求到达 是否错误? (4xx/5xx) 100% 采样 保留完整 Trace 固定比率采样 如 10% 随机保留 值班台推荐:基于错误采样 + 尾部采样混合策略 • 错误 Trace 100% 保留,确保故障可复现 • 正常 Trace 按 5%-10% 采样,用于性能基线分析

3.7 采样率对存储选型的影响

采样率直接决定了存储系统的写入吞吐和磁盘占用。在值班台场景中,需要根据流量预估存储规模:

  • 低采样(< 1%):适合高流量生产环境(如每秒 10 万请求),存储成本可控,但可能遗漏偶发故障。
  • 中采样(1% - 10%):适合大多数微服务系统,兼顾成本与可观测性。
  • 高采样(> 10%):仅用于低流量系统或核心链路(如支付、登录)。
  • 全采样(100%):仅用于开发/测试环境或合规审计场景。
存储选型公式: 每日 Span 写入量 = QPS × 平均 Span 数/请求 × 采样率 × 86400 秒。例如:QPS=5000,平均 15 Span/请求,采样率 5%,则每日 Span 量 ≈ 5000 × 15 × 0.05 × 86400 = 3.24 亿 Span。以此评估存储系统的写入性能和压缩比。