分布式链路追踪的数据模型本质上是:
树状或有向无环图(DAG)结构,节点是 Span,根节点是 Root Span,所有 Span 通过 SpanID 和 ParentSpanID 串成调用链。
更正式的定义:
- Trace:一次分布式调用(全局事务)的整体结构。
- Span:Trace 中的一个「操作单元」。
- 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 表示:
- 一次 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 = 树结构(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。
架构面试中,面试官希望你能说出 设计原则:
- 唯一性
- 可扩展
- 随机性
- 高可用
- 安全性
- 分布式可生成
下面分开讲。
OpenTelemetry 标准:
TraceID = 16 字节(128 bit)随机数 → 十六进制字符串(32 字符)
例如:
4bf92f3577b34da6a3ce929d0e0e4736
为什么用 128 bit?
- 2^128 = 3.4e38,几乎不可能碰撞
- 分布式所有节点都能安全生成
- 足够长,可跨跨机房、跨平台
- 可用于全链路关联
TraceID 的生成策略一般为:
- CSPRNG(Secure RNG)安全随机
- UUID v4
- 128bit 的基于时间/混合算法
- Snowflake 方案(扩展版)
要求:
TraceID 必须全局唯一,不允许重复。
OpenTelemetry 标准:
SpanID = 8 字节(64 bit)随机数 → 十六进制字符串(16 字符)
例如:
00f067aa0ba902b7
为什么比 TraceID 短?
- Span 数量远多于 Trace(一个 Trace 下很多 Span)
- 64bit 足够唯一(2^64 = 1.8e19)
- 更节省带宽、日志存储、上下文传播成本
要求:
- 每个 Span 只能有一个唯一 SpanID
- SpanID 不可复用
- 通常使用 CSPRNG 生成
如果当前 Span 是根(入口请求):
parentSpanID = 0000000000000000(或 null)
否则:
parentSpanID = 父 span 的 spanID
面试官经常会问:
为什么不用递增 ID?为什么必须用随机数?
原因:
随机 128bit 碰撞概率几乎为 0 不需要中心节点 天然分布式
递增 ID 会泄漏系统内部状态(请求量、增长速度)
随机数生成是无锁的 Snowflake 等方案需要协调节点信息
Java / Go / Node / Python 必须都能生成 所以随机数最通用
每次跨进程调用(如 HTTP → RPC)时:
必须携带:
- TraceID
- SpanID
- ParentSpanID
- Sampling 标记
通过 header 传递,例如:
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)进行上下文传播。”
这样整个链路就可被观察、分析和可视化。”