Skip to content

Latest commit

 

History

History
256 lines (166 loc) · 5.86 KB

File metadata and controls

256 lines (166 loc) · 5.86 KB

讲一下 Trace 的数据模型,什么是 Span,Trace ID 和 Span ID 是怎么设计的?

一、Trace 的数据模型是什么?

分布式链路追踪的数据模型本质上是:

树状或有向无环图(DAG)结构,节点是 Span,根节点是 Root Span,所有 Span 通过 SpanID 和 ParentSpanID 串成调用链。

更正式的定义:

  1. Trace:一次分布式调用(全局事务)的整体结构。
  2. Span:Trace 中的一个「操作单元」。
  3. Span 之间关系:Parent-Child 层级关系,通过 parentSpanID 表示。

一个 Trace = 若干 Span 构成的因果链路(Causal Chain)

例如一次请求:

Client → API Gateway → Service A → Service B → DB

会产生:

Trace
 ├── Span A(API)
 │    └── Span A1(内部逻辑)
 └── Span B(访问数据库)

二、什么是 Span?(核心概念)

Span 是分布式链路追踪系统中最小的可观测单元。

一个 Span 表示:

  • 一次 RPC 调用
  • 一次函数执行
  • 一条 SQL/Redis 调用
  • 一次 HTTP 请求
  • 甚至一次 CPU 密集计算

每个 Span 包含:

字段 含义
trace_id 所属 Trace 的全局 ID
span_id 当前 Span 的唯一 ID
parent_span_id 父节点 Span 的 ID
name 操作名称(如 GET /order)
kind SERVER/CLIENT/PRODUCER/CONSUMER
timestamp 开始时间
duration 耗时
attributes 标签:HTTP 状态码、DB 语句、错误原因
events 事件,如异常
status OK / ERROR

一句话定义:

Span = 一次“操作”的开始时间 + 结束时间 + 上下文信息。


三、Span 之间是怎么组织成 Trace 的?

Span 的组织结构是:

Trace = 树结构(Root Span → Child Span → Sub Child...)

关系靠两个字段:

  • traceID:所有 Span 相同
  • parentSpanID:指向父 Span

示意:

TraceID = abc123

Span A(SpanID = 1, Parent = null)        ← Root Span
 ├── Span B(SpanID = 2, Parent = 1)
 └── Span C(SpanID = 3, Parent = 1)
       └── Span D(SpanID = 4, Parent = 3)

这是一个标准的层级 Trace。


四、Trace ID 和 Span ID 是怎么设计的?

架构面试中,面试官希望你能说出 设计原则

  • 唯一性
  • 可扩展
  • 随机性
  • 高可用
  • 安全性
  • 分布式可生成

下面分开讲。


1. Trace ID 设计(16 字节=128bit)

OpenTelemetry 标准:

TraceID = 16 字节(128 bit)随机数 → 十六进制字符串(32 字符)

例如:

4bf92f3577b34da6a3ce929d0e0e4736

为什么用 128 bit?

  • 2^128 = 3.4e38,几乎不可能碰撞
  • 分布式所有节点都能安全生成
  • 足够长,可跨跨机房、跨平台
  • 可用于全链路关联

TraceID 的生成策略一般为:

  • CSPRNG(Secure RNG)安全随机
  • UUID v4
  • 128bit 的基于时间/混合算法
  • Snowflake 方案(扩展版)

要求:

TraceID 必须全局唯一,不允许重复。


2. Span ID 设计(8 字节=64bit)

OpenTelemetry 标准:

SpanID = 8 字节(64 bit)随机数 → 十六进制字符串(16 字符)

例如:

00f067aa0ba902b7

为什么比 TraceID 短?

  • Span 数量远多于 Trace(一个 Trace 下很多 Span)
  • 64bit 足够唯一(2^64 = 1.8e19)
  • 更节省带宽、日志存储、上下文传播成本

要求:

  • 每个 Span 只能有一个唯一 SpanID
  • SpanID 不可复用
  • 通常使用 CSPRNG 生成

3. ParentSpanID

如果当前 Span 是根(入口请求):

parentSpanID = 0000000000000000(或 null)

否则:

parentSpanID = 父 span 的 spanID

五、为什么 Span ID 和 Trace ID 都是随机数?(设计哲学)

面试官经常会问:

为什么不用递增 ID?为什么必须用随机数?

原因:

1. 防止分布式系统 ID 冲突

随机 128bit 碰撞概率几乎为 0 不需要中心节点 天然分布式

2. 安全性(避免可枚举、避免攻击者推测 Trace 数量)

递增 ID 会泄漏系统内部状态(请求量、增长速度)

3. 性能更高

随机数生成是无锁的 Snowflake 等方案需要协调节点信息

4. 全链路跨语言、跨进程传播

Java / Go / Node / Python 必须都能生成 所以随机数最通用


六、链路追踪上下文(Context Propagation)

每次跨进程调用(如 HTTP → RPC)时:

必须携带:

  • TraceID
  • SpanID
  • ParentSpanID
  • Sampling 标记

通过 header 传递,例如:

W3C Trace Context 标准
traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
tracestate: vendor_specific

这让整个链路保持一致。


七、面试可背总结

你可以这样回答:

“Trace 的数据模型是一个由多个 Span 构成的有向无环图(通常是树结构)。

Span 是链路追踪中的最小可观测执行单元,包含操作名称、开始和结束时间、属性、事件、状态等信息。

整条调用链通过 TraceID + ParentSpanID 串联:

  • TraceID:一次分布式调用的全局 ID,通常 128bit 随机数,全局唯一。
  • SpanID:单个 Span 的唯一 ID,通常 64bit 随机数。
  • ParentSpanID:指向父 Span 的 SpanID,用于构建调用树。

TraceID 保证整条链路的关联性,SpanID 保证单节点操作的唯一性。跨服务调用通过 HTTP Header(如 W3C traceparent)进行上下文传播。”

这样整个链路就可被观察、分析和可视化。”