TraceId、SpanId 和 Span 分别是什么?
简化版
TraceId 标识一次完整请求链路,Span 表示链路中的一个操作单元,比如一次 HTTP 调用、一次 SQL 查询或一个消息消费过程,SpanId 是 Span 的唯一标识。多个 Span 通过父子关系组成一棵调用树,共同描述一次请求从入口到结束的完整路径。
面试里可以说:TraceId 用来串起整次请求,Span 用来描述请求中的每一段工作,SpanId 和 ParentSpanId 用来还原调用关系。
详细版
一次请求进入网关时,追踪系统通常生成 TraceId。这个 TraceId 会随着请求在服务之间传播,后续服务创建自己的 Span,并记录操作名、开始时间、结束时间、耗时、状态、标签和事件。每个 Span 都有 SpanId,并通过 ParentSpanId 指向上游 Span。
例如一次下单请求的 Trace 可能包含:网关接入 Span、订单服务处理 Span、库存服务扣减 Span、数据库更新 Span、消息发送 Span。查看 Trace 时,工程师可以看到每段耗时和错误位置,从而定位瓶颈。
TraceId 也常写入日志 MDC,这样日志和链路追踪可以互相跳转。排查问题时,先从日志里找到 TraceId,再打开追踪系统看完整调用链,是非常常见的流程。
完整版教学
一、Trace 是一次完整请求
Trace 可以理解为一次请求在分布式系统中的完整足迹。用户发起一次下单,请求可能经过多个服务,但它们都属于同一个 Trace。
TraceId 是这次足迹的唯一编号。只要日志、调用链和错误事件都带上同一个 TraceId,工程师就能把散落在多个服务里的信息串起来。
TraceId 的价值不在于它本身有多复杂,而在于它能跨服务传播并保持一致。
二、Span 是一次局部操作
Span 是 Trace 中的一个片段。它表示某个服务或组件完成的一段工作。常见 Span 包括:
- 网关处理请求。
- 服务方法执行。
- HTTP 或 RPC 调用下游。
- SQL 查询或更新。
- Redis 命令。
- MQ 发送或消费。
每个 Span 通常会记录开始时间、结束时间、耗时、状态、操作名、服务名、实例信息和标签。
三、SpanId 和 ParentSpanId 还原调用树
只有 TraceId 还不够,因为它只能说明这些操作属于同一次请求,不能说明谁调用了谁。SpanId 标识当前 Span,ParentSpanId 标识它的父 Span。
例如:
TraceId = T1
Span A: 网关处理请求
Span B: 订单服务处理
Span C: 调用库存服务
Span D: 写订单数据库
通过父子关系,追踪系统可以画出调用树和时间线,让人看到请求的结构。
四、Span 里常见字段
一个 Span 通常包含:
- traceId:所属 Trace。
- spanId:当前 Span 标识。
- parentSpanId:父 Span 标识。
- name:操作名,如
HTTP GET /orders。 - startTime / endTime:开始结束时间。
- duration:耗时。
- status:成功、错误、超时等。
- attributes/tags:服务名、URL、SQL 表名、错误码等。
- events/logs:某些关键时间点事件。
这些字段越规范,追踪系统越容易聚合分析。
五、为什么 Span 不能随便打
如果每一行代码都创建 Span,链路会极其庞大,性能和存储成本都难以接受。Span 应该围绕有排障价值的边界创建,比如服务入口、远程调用、数据库访问、消息发送和关键业务步骤。
Span 太少,定位不到细节;Span 太多,噪音和成本会爆炸。生产系统要根据链路重要性和成本选择合理粒度。
六、TraceId 和日志的关系
日志通常会把 TraceId 放入 MDC 或类似上下文中,打印到每一行日志。这样从错误日志可以找到 TraceId,再进入追踪系统查看完整链路;也可以从追踪系统中的某个 Span 跳到对应服务日志。
这就是可观测性的关联能力。TraceId 不是只服务追踪系统,它也是日志、指标和告警之间的连接线。
七、面试回答建议
回答时先说 TraceId 标识一次请求,Span 表示一次操作,SpanId 标识 Span,ParentSpanId 表示父子关系。然后举一个下单请求跨网关、订单、库存、数据库的例子。
再补充 Span 记录耗时、状态、标签,TraceId 写入日志用于关联。这样答案既有概念,也有落地。
八、常见追问和落地边界
常见追问是 Span 粒度如何选择。服务入口、远程调用、数据库访问、消息发送这类边界通常值得建 Span;普通 getter、循环内部小函数不适合都建 Span。粒度过粗定位不到问题,粒度过细会造成噪音和成本。
另一个边界是 Span 标签不要乱加。URL、状态码、服务名、错误码有价值;订单号、用户 ID、TraceId 这类高基数字段不适合大量作为指标化标签。需要查询具体请求时,可以放日志或事件中,并注意脱敏。
九、常见误区与追问
这道题要紧扣「TraceId 与 Span」本身回答,不能把它混成泛泛的可观测性套话。面试官通常会沿着“为什么需要、流程怎么走、失败怎么兜、代价是什么”继续追问,所以回答要覆盖日志、指标、追踪、上下文传播、采样、告警和排障闭环。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | TraceId 标识一次完整请求,Span 标识其中一个调用或操作片段,父子关系组成链路树 | 不要停在名词解释 |
| 流程机制 | 入口生成 TraceId -> 创建根 Span -> 调用下游创建子 Span -> 传播 parent 信息 -> 记录耗时状态 -> 后端重建链路树 | 要说清触发点、状态变化、确认点和失败兜底 |
| 工程取舍 | 一次请求 TraceId 不变,网关、订单、库存各自创建 Span,SpanId 不同但 parent 指向上游 | 可观测性不能替代稳定性设计,它用额外采集、存储和分析成本换更快定位问题 |
TraceId 与 Span 面试拆解:
1. 入口生成 TraceId
2. 创建根 Span
3. 调用下游创建子 Span
4. 传播 parent 信息
5. 记录耗时状态
6. 后端重建链路树
记忆钩子:先给结论,再拆流程,再讲数字例子和失败边界;回答「TraceId 与 Span」时要围绕题目问法收束,不要把相邻概念堆成一段没有重点的名词清单。
- 误区:TraceId 和 SpanId 是同一个东西。 TraceId 串整条链路,SpanId 标识单个操作。
- 误区:Span 越多越好。 过细会增加采集成本和噪声,要围绕关键边界埋点。
- 误区:TraceId 只能用于链路系统。 它也应进入日志和错误事件,用于跨系统检索。
- 追问:根 Span 是什么? 一次请求入口创建的第一个 Span,表示整条请求的起点。
- 追问:父子 Span 有什么用? 还原调用层级和耗时归属,定位等待发生在哪一段。
- 追问:异步任务怎么建 Span? 从消息或任务上下文恢复 Trace,再创建新的 consumer/internal Span。
十、加强记忆
TraceId 像一次旅行的行程号,Span 像行程中的每一段路,SpanId 是每段路的编号,ParentSpanId 说明这段路是从哪一段接下来的。
链路追踪就是把这些路段拼起来,看一次请求到底走过哪里、哪里慢、哪里错。