想象你是一家大型电商公司的技术负责人,名叫老周。你们的系统早已不是“一台服务器跑所有业务”的单体时代,而是拆成了上百个微服务:用户登录、商品浏览、下单、支付、库存扣减……每一次你在 App 上点“立即购买”,背后就是一场几十个服务接力传递数据的“接力赛”。过去系统出问题时,你像在黑夜里找一根断掉的保险丝,无从下手。 好在有一个叫“分布式追踪”的工具,它给每一次用户请求贴上一张“运单号”(Trace ID),每个服务处理请求时都记录下“快递员到过哪里、做了什么、花了多久”(Span)。这些记录汇总起来,就成了一张完整的“物流轨迹图”。而 Jaeger、Zipkin、Tempo,就是三家处理这些运单信息的“快递分拣中心”——它们都能帮你还原接力赛的全程,但每家公司的仓库设计、查件方式、运营成本却天差地别。选对了,海量链路数据轻松驾驭;选错了,你会在存储账单和慢查询里苦不堪言。这篇文章,就是带你逛完这三家“分拣中心”,听一听三位主管各自怎么说。
对 AI 从业者而言,可观测性是排查分布式 AI 系统(如模型推理链路、特征管道)故障的基石。掌握三大追踪后端的选型逻辑,让你在搭建 AI 服务监控体系时少走弯路,快速定位性能瓶颈。
可视化演示
展示Jaeger Agent Collector Query Storage组件链路、索引型与非索引型存储架构对查询的影响、以及Jaeger Zipkin Tempo三大后端在存储与查询能力的对比。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Jaeger四组件链路 Agent接收Span批量转发Collector Collector写入Storage Query查询 Agt→Coll→Stor→Qry 组件分层支撑全链路追踪
三大后端初识
在天工市的物流行业,有三家风格迥异的快递分拣中心。第一家是“城市邮局”Zipkin,2012 年由 Twitter 开源,用 Java 写成,是追踪界的元老——像一位资深的邮政局长,规矩多、流程稳,很多老牌企业(如阿里的鹰眼早期)都受过它的启发。第二家是“极速快运”Jaeger,由 Uber 在 2017 年捐给 CNCF,用 Go 语言打造,主打高性能和云原生,像一支装备精良的机动分队。第三家是“云仓一体”Tempo,由 Grafana Labs 在 2021 年推出,背靠 Grafana 全家桶,像一座直接连通云厂商大仓库的智能物流中心。
这三家的共同目标完全一致:接收来自各微服务的 Span(追踪片段),按照 Trace ID 把它们拼成完整的调用链,并提供查询界面。但它们面对同一个问题时——“如何在海量运单中快速找到想要的那张?”——给出了截然不同的答案。
要理解答案的差异,先得明白一个核心概念:Span 是链路追踪的最小单元,记录了一次跨服务调用的名称、耗时、父级关系等元数据;Trace 则是若干 Span 按树形结构组成的完整请求轨迹。如果把一次用户请求比作一趟“接力赛”,Trace 就是整场比赛的录像,Span 则是每个运动员单独佩戴的 GPS 记录仪。
理解了 Span 的数据结构,就好比看懂了快递面单上的每一个格子。接下来,我们走进三家分拣中心的“仓库重地”,看看它们各自的存放哲学。
- Trace 的组成Trace 是所有 Span 的并集,表示一次完整请求的全部追踪数据。
- Span 的字段结构每个 Span 包含 traceId(运单号)、spanId(本环节编号)、parentSpanId(上一环节编号)等字段——正是这个 parentSpanId,把散落的 GPS 记录串成了一棵树。
存储架构对比
老周跟着 Zipkin 的邮政局长走进仓库,看到一排排整齐的货架——这是 Elasticsearch 集群。每一个 Span 被当作一份 JSON 文档存入 ES,靠倒排索引撑起查询。这种方式像在传统仓库里给每件包裹建立“索引卡片”,查询灵活,但索引的建立和维护要吃掉大量磁盘和内存。“老牌邮局”的这套玩法虽然成熟,运维 ES 集群却像伺候一位挑剔的老爷。
Jaeger 的仓库则提供了两种货架可选:支持 Cassandra 和 Elasticsearch。Cassandra 是列式存储,擅长海量写入,适合超大规模场景;ES 则与 Zipkin 类似,便于多维检索。Jaeger 让客户按需选择,就像快运公司让你选“走陆运还是空运”。但请注意,Cassandra 运维门槛高,ES 又在高基数标签下容易臃肿——两种选择各有隐形成本。
Tempo 的“仓库”最另类——它几乎没有自己的索引,直接把 Span 数据压缩后丢进对象存储(S3、GCS 或阿里云 OSS)。这就像一家不建货架的仓库,把所有包裹按“运单号”直接堆进成本极低的云仓。它不做复杂的索引,想要检索 Trace 时,用 Trace ID 算出对象存储的路径,直接取出对应文件。
存储架构的分野,直接决定了查询时的体验。接下来我们看看,三家分拣中心的“查件柜台”分别怎么工作。
- Tempo 的成本公式Tempo 的账单主要是对象存储费用(便宜)+ 查询时按需拉起计算资源的费用(用多少付多少)。
- Zipkin 的成本公式Zipkin 的 ES 集群必须 7×24 小时运行,索引常驻内存,因此成本是“存储 + 常驻索引”。
查询机制对比
在 Zipkin 的查件柜台,工作人员会先问你:“寄件服务叫什么?收件操作叫什么?”然后对着 ES 的索引卡片逐一翻找。它的查询模型是“服务名 + 操作名 + 时间范围”的组合检索,能够找到符合条件的所有 Trace。但问题在于,当数据量大时,这种“翻卡片”的方式响应变慢;而且 Zipkin 早期不支持标签(Tags)深度过滤,定位一个“失败支付”具体发生在哪一跳,往往要人工逐个排查。
Jaeger 的查询柜台明显更先进。它允许你用标签直接筛选,比如 error=true 或者 http.status_code=500,然后基于这些 KV 组合快速定位目标链路。这相当于在快递面单上做了“多层索引卡”,既支持服务维度检索,也支持标签维度过滤。代价就是每写入一个 Span,都要同步更新这些标签索引,产生“写放大”。
Tempo 的取件方式干脆利落——柜台只问一个问题:“运单号(Trace ID)是多少?”你只要给出 Trace ID,它直接去对象存储里精确取件,速度快得惊人。它不擅长做“条件模糊搜索”,但 Grafana 生态的 Tempo 通常搭配 Loki(日志)和 Prometheus(指标)一起工作:先用日志或指标锁定一个有问题的 Trace ID,再交给 Tempo 精确拉取完整链路。
查件方式的差异决定了各自的适用场景。那么这三家分拣中心在“日常运营”中,谁更省人省力?我们看看部署与性能这盘棋。
- Tempo 的哈希直达Tempo 对 Trace ID 做哈希,直接映射到对象存储中的文件路径,取件只需一次磁盘读取,复杂度 O(1)。
- Zipkin 的索引扫描Zipkin 需要扫描 ES 索引卡片,复杂度随索引规模增长,数据量大时延迟明显上升。
部署与性能
如果用一个词总结 Zipkin 的部署体验,那就是“重”。它需要你自建 ES 集群,生产环境至少 3 节点起步;用 Java 写的服务端内存占用高,启动慢,但社区资料多,出了问题网上到处有解药。对于没有专职 ES 运维的中小团队,这像养了一只食量巨大的看门犬。
Jaeger 的部署轻盈不少。官方提供 All-in-One 模式用于测试,生产环境可拆分为 collector、query、agent 三组件,每个都是无状态的 Go 二进制,内存占用远低于 Java 系。配合 Kubernetes Operator,一个 YAML 就能拉起整套系统。不过它的性能同样受存储后端制约——写入 Cassandra 时吞吐高,写入 ES 时则受索引瓶颈限制。
Tempo 的部署最“云里云气”。后端存储直接用云厂商的对象存储,无需维护任何索引集群。它的查询组件是可选的无状态微服务,按需伸缩。这种架构下,Tempo 几乎可以把运维成本降到最低,但代价是查询体验需要依赖 Grafana 生态的配合才能完整体现。
现在你心里大概有数了:没有绝对的优劣,只有匹配度的问题。那到底该选哪家?我们进入选型决策指南。
- 写入吞吐公式写入吞吐 S 由批处理大小除以索引耗时加上存储 IO 决定,索引越重、吞吐越低。
- 索引放大系数索引放大 = 实际写入磁盘的数据量 / 有效链路数据量。Zipkin/Jaeger 通常大于 2,Tempo 接近 1。
选型决策指南
老周把三家分拣中心的情况列成了一张表,开始按自己的条件做排除法。第一步看技术栈亲和性:如果团队已经是 Java 主导、ES 运维成熟,选 Zipkin 顺理成章;如果公司全面云原生化、Go 微服务为主,Jaeger 如鱼得水;如果已经重度拥抱 Grafana(Loki + Prometheus + Grafana),那 Tempo 是必然之选,因为它与 Grafana 的 UI 无缝集成。
第二步看规模与预算。小规模(日均 Trace 百万级以下)用 Zipkin 的 ES 方案最稳妥,开发资源好找;中大规模(日均千万级)且预算有限,Tempo 的对象存储路线能省下惊人的存储成本;追求极致查询性能与标签过滤能力、预算充足,Jaeger + Cassandra 是硬核选择。
第三步看团队运维能力。Zippkin 需要你懂 ES、Jaeger 需要你懂 Cassandra(如果选它)或 ES、Tempo 几乎不需要你懂存储运维,但要求你理解对象存储与 Grafana 生态。最后还要考虑可观测性整合度:Tempo 与 Prometheus 的“指标→链路”跳转体验业内有口皆碑,Zipkin 则相对封闭。
选型定下来只是开始。真正让老周睡不着觉的,是如何把正在运行的链路数据从旧系统搬到新系统,不能断、不能丢、不能影响业务。这就要说到迁移与落地了。
- 技术栈亲和性得分Affinity 衡量后端与团队现有语言、基础设施的匹配程度——Java 团队配 Zipkin、Go 团队配 Jaeger 都是高分。
- 成本匹配度得分成本占预算比例越低,该项得分越高,用来体现 Tempo 在超大规模下的成本优势。
- 生态整合度得分EcoIntegrate 衡量与现有监控体系(Prometheus、Loki、Grafana)联动的顺畅程度,Tempo 在此项常常拉满。
迁移与落地
老周的团队原本用的是 Zipkin,现在决定转向 Tempo 已降低存储成本。迁移第一步是协议兼容——庆幸的是,Zipkin 的客户端(如 Brave、Zipkin 官方库)可以通过 Zipkin 兼容 API 直接把 Span 数据发给 Jaeger 或 Tempo。Tempo 内置了 Zipkin API 的接收端点,这意味着老周根本不用改业务代码,只需要把数据上报的地址从 Zipkin 服务器指向 Tempo,老系统就能“无缝换轨”。
第二步是接入 OpenTelemetry(OTel)做统一收口。OTel 是一个与厂商无关的遥测标准,它的 SDK 支持导出到 Jaeger、Zipkin、Tempo 等所有主流后端。老周的做法是:在应用层统一埋点 OTel,由 OTel Collector 作为“总转运站”,按需把数据同时发给新旧两个后端做“双写”。双写期间对比两边数据完整性,验证新系统没问题后再逐步减少向旧系统的流量,直到完全切换。
最后一步是风险管控。老周在设计方案时特意加了一条“开关”:通过 OTel Collector 的动态配置,可以随时把新后端从数据流中摘除,避免新系统故障拖垮业务。同时,老周在 Tempo 上配置了采样策略——全量生产流量的 10% 双写,验证通过后再放大到 100%。这个过程,就像给高铁换轨,先把列车引到临时便道,确认无误后再拆除旧轨。
至此,老周对三家分拣中心从仓库到柜台再到换轨策略都有了全面认识。其实选型没有银弹,重要的是你把团队的技术栈、成本预算、运维能力和生态现状放在桌面上,对照三家特点做理性的匹配。
- 双写一致性双写期间新旧后端的数据一致比例,通过定时比对 Trace ID 集合得出,越高越安全。
- 数据丢失率新系统未能接收到的 Trace 比例,目标为零;分母 +1 是为了防止除零并使“零丢失”得满分。
- 回滚能力通过 OTel Collector 动态切换配置,能在 5 分钟内切回旧系统的能力,是迁移的保险绳。
AI 应用场景
场景一:AI 推理服务链路诊断
阶段:模型推理与在线服务监控
解决的问题:一个 AI 图片识别服务由“网关→鉴权→特征提取→模型推理→结果后处理”5 个微服务组成。传统方式中,模型推理变慢时,你只知道整体延迟升高,却不知瓶颈在哪一环。接入 Jaeger 后,每个服务的 Span 都记录耗时,用标签 model_version=v2 过滤出特定模型版本的所有链路,瞬间定位到“特征提取”环节耗时异常——原来是新版本的归一化逻辑引入了性能回退。
具体案例:某智能客服公司为每个会话请求生成 Trace ID,通过 Jaeger 的标签检索功能筛查 sentiment_model=bert-large 的链路数据,发现 GPU 服务在高并发下的排队时间激增,据此触发弹性扩容策略。
场景二:AI 数据管道可观测性
阶段:数据处理与特征工程
解决的问题:离线特征管道每天处理 TB 级数据,管道由 Spark 任务、特征存储写入、模型训练触发等多个阶段组成。使用 Tempo 后,每个阶段作为 Span 上报,Trace ID 与数据分片 ID 绑定。当某天的特征数据质量异常时,数据工程师从 Grafana 看板上发现异常指标,点击跳转到 Tempo 拉取对应分片的完整处理链路,快速定位是“写入特征库超时”还是“上游数据源延迟”。
具体案例:某推荐系统团队用 Tempo 追踪“用户行为日志→特征拼接→模型打分”的批处理链路,配合 Loki 日志跳转,将平均故障排查时间从 2 小时缩短到 15 分钟。
场景三:AI Agent 多工具调用追踪
阶段:大模型应用编排
解决的问题:一个 AI Agent 在回答问题时可能依次调用搜索工具、代码解释器、知识库检索等多个外部服务。每个工具调用就是一个 Span,Trace 完整还原 Agent 的思考与动作序列。使用 Zipkin 兼容 API 上报数据到 Tempo,可以在 Grafana 上按 Trace ID 回放 Agent 的全部工具调用时间线,找出哪个外部 API 拖慢了整体响应。
具体案例:某创业公司的 AI 编程助手接入 OTel SDK 后,所有插件调用链路自动上报至 Tempo,工程师通过链路火焰图观察到“代码检索服务”的 p99 延迟高达 8 秒,据此为向量数据库增加了缓存层。
Python 演示
"""简化模拟:三大追踪后端的 Span 存储与查询对比"""import hashlibimport timefrom dataclasses import dataclass, fieldfrom typing import Dict, List, Optional @dataclassclass Span: trace_id: str span_id: str parent_span_id: Optional[str] service_name: str operation_name: str duration_ms: float tags: Dict[str, str] = field(default_factory=dict) class ZipkinBackend: """老牌邮局:全量索引,查询靠服务名+操作名""" name = "Zipkin" def __init__(self): self.spans: List[Span] = [] # 模拟 ES 文档库 self.index: Dict[tuple, list] = {} # (service, operation) -> span 列表 def write(self, span: Span): self.spans.append(span) key = (span.service_name, span.operation_name) self.index.setdefault(key, []).append(span) def query(self, service_name: str, operation_name: str) -> List[Span]: key = (service_name, operation_name) return self.index.get(key, []) class JaegerBackend: """极速快运:在 Zipkin 基础上增加标签索引""" name = "Jaeger" def __init__(self): self.spans: List[Span] = [] self.tag_index: Dict[str, Dict[str, list]] = {} # tag_key -> tag_value -> spans def write(self, span: Span): self.spans.append(span) for k, v in span.tags.items(): self.tag_index.setdefault(k, {}).setdefault(v, []).append(span) def query(self, tag_key: str, tag_value: str) -> List[Span]: return self.tag_index.get(tag_key, {}).get(tag_value, []) class TempoBackend: """云仓一体:零索引,TraceID 哈希直达对象存储""" name = "Tempo" def __init__(self): self.store: Dict[str, List[Span]] = {} # object_key -> trace spans def write(self, span: Span): # TraceID 哈希计算对象存储路径 key = hashlib.md5(span.trace_id.encode()).hexdigest()[:16] self.store.setdefault(key, []).append(span) def query(self, trace_id: str) -> List[Span]: key = hashlib.md5(trace_id.encode()).hexdigest()[:16] return self.store.get(key, []) def build_demo_trace() -> List[Span]: """构造一条模拟链路:网关 -> 商品服务 -> 支付服务""" return [ Span("abc123", "s1", None, "api-gateway", "POST /checkout", 105.0, {"http.status_code": "200"}), Span("abc123", "s2", "s1", "product-svc", "getStock", 45.0, {"http.status_code": "200"}), Span("abc123", "s3", "s1", "payment-svc", "charge", 58.0, {"error": "true", "http.status_code": "500"}), ] if __name__ == "__main__": trace = build_demo_trace() backends = [ZipkinBackend(), JaegerBackend(), TempoBackend()] for backend in backends: for span in trace: backend.write(span) print(f"--- {backend.name} ---") if isinstance(backend, ZipkinBackend): result = backend.query("payment-svc", "charge") print(f"按服务+操作查询: {len(result)} 个 Span, 含错误标签: {result[0].tags}") elif isinstance(backend, JaegerBackend): result = backend.query("error", "true") print(f"按错误标签查询: {len(result)} 个 Span, duration_ms={result[0].duration_ms}") elif isinstance(backend, TempoBackend): result = backend.query("abc123") print(f"按 TraceID 精确取件: {len(result)} 个 Span, 耗时最长 {max(s.duration_ms for s in result)}ms")
key = (span.service_name, span.operation_name)Zipkin 建立“服务名+操作名”的联合索引卡片,查询必须提供这两个字段self.tag_index.setdefault(k, {}).setdefault(v, []).append(span)Jaeger 为每个标签键值对建立倒排索引,实现多维过滤hashlib.md5(span.trace_id.encode()).hexdigest()[:16]Tempo 用 Trace ID 的哈希值作为对象存储路径,实现 O(1) 直达取件if isinstance(backend, ZipkinBackend)不同后端的查询方式差异通过类型判断演示,体现选型对查询模式的决定性影响
常见误区
误区Tempo 按 Trace ID 查询,不能像 Jaeger 那样直接按标签搜索链路,所以它的性能全面落后于 Jaeger,不适合生产环境。
正解这混淆了“查询方式”与“查询性能”。Tempo 的 Trace ID 精确查询是 O(1) 的哈希寻址,比 Jaeger 的索引扫描更快。它只是不支持复杂的按标签搜索——这个短板在真实场景中往往被 Loki 和 Prometheus 补齐:先用日志和指标定位 Trace ID,再交给 Tempo 精准取件。就像你查快递不必翻遍仓库找“所有红色箱子”,只需报出运单号,仓库的自动化机械臂直接取货。
为何常见:初学者习惯用“能否条件搜索”来衡量后端能力强弱,而忽略了可观测性体系的整体配合。
误区Jaeger 是 CNCF 项目、Go 语言、性能好,所以任何场景都应该直接选 Jaeger,Zipkin 已经过时了。
正解Zipkin 成熟稳定、社区庞大、Java 生态兼容性好,在 ES 运维经验丰富的团队手中依然高效。选型的关键是匹配度——就像你选快递公司,寄同城急件可能选闪送(Jaeger),但寄偏远乡村可能邮政(Zipkin)反而更可靠。Jaeger 如果搭配 ES 存储,同样的索引放大问题依然存在;而 Zipkin 的 Brave 客户端在 Java 技术栈中至今仍是零侵入集成的典范。
为何常见:人们容易受“新项目优于老项目”的直觉影响,忽略存量系统、团队技能、运维成本等现实约束。
误区Tempo 零索引的设计让它存储成本极低,所以无脑选 Tempo 就是最佳省钱方案。
正解Tempo 把索引成本转移到了“查询时的计算成本”。低频的按需查询确实便宜,但如果你需要频繁做标签过滤分析(例如:按 error=true 快速扫描全部失败链路),Tempo 无能为力,只能靠 Loki 侧面绕过。这就好比省了货架钱(索引)的仓库,找货时必须全家齐上阵(大量计算资源)。在日均几百万级 Trace 的小规模下,Zipkin + ES 单机版反而更简单划算。
为何常见:将“零索引”简单等同于“零成本”,忽略了查询模式对总成本结构的影响。
误区从 Zipkin 切换到 Jaeger 或 Tempo 必须重写所有微服务的埋点代码,工程浩大风险高,因此宁愿留在旧系统。
正解三大后端之间并非孤立。Zipkin 兼容 API 允许 Zipkin 客户端的数据直接上报给 Jaeger 或 Tempo;更现代的做法是先用 OpenTelemetry SDK 统一埋点,再通过 OTel Collector 配置多路导出,实现“双写新旧系统”的灰度切换,业务代码完全不动。这就像你的手机号可以携号转网,换的是运营商,号码不变,联系人无感知。
为何常见:直觉上会认为后端存储不同、API 格式不同,必然要求改数据源,但忽略了网关层(OTel Collector)的存在意义。
误区Zipkin 和 Jaeger、Tempo 的追踪数据格式不互通,不能同时使用两个后端作数据比对。
正解Zipkin 的 Zipkin v2 JSON 和 OpenTelemetry 的 OTLP 格式在字段层面高度兼容(serviceName、operationName、tags、traceId 等语义一一对应),Jaeger 与 Tempo 都支持直接接收 Zipkin 格式的 span。因此“双写”比想象中的容易。真正的差异不在“能否收”,而在“收了之后怎么存、怎么查”——这正是本篇文章的核心观点。
为何常见:初学者从各自的官方文档入手,看到的是不同的 API 名称和配置参数,误以为底层数据模型也是彼此隔离的孤岛。
面试题
Q1. 分布式追踪中,一个 Trace 是由什么组成的?
- A.多个日志事件的集合
- B.多个 Span 按树形结构组成的完整请求轨迹← 正确答案
- C.多个指标数据的聚合
- D.单个服务的性能快照
答案:B。Trace 是若干 Span 按 parentSpanId 关系组成的树形结构,记录一次完整请求的全链路信息。Span 是追踪最小单元,多个 Span 通过父子关系拼接成 Trace。日志事件集合属于日志系统范畴,指标聚合是 Metrics 系统的职责,均不符合 Trace 定义。
Q2. 以下哪个后端使用 Go 语言开发并获得 CNCF 孵化?
- A.Zipkin
- B.Jaeger← 正确答案
- C.Tempo
- D.SkyWalking
答案:B。Jaeger 由 Uber 用 Go 语言开发,2017 年捐给 CNCF 并成为毕业项目。Zipkin 由 Twitter 用 Java 开发,Tempo 是 Grafana Labs 用 Go 开发但未走 CNCF 路线,SkyWalking 是 Apache 项目使用 Java 开发。
Q3. 关于 Tempo 的存储设计,以下描述正确的是?
- A.必须依赖 Elasticsearch 提供索引能力
- B.直接使用对象存储(S3/GCS/OSS),实现零索引← 正确答案
- C.使用 Cassandra 存储全量索引
- D.将数据存入内存数据库以实现高速查询
答案:B。Tempo 的核心特点是不建索引,直接把 Span 压缩后存入对象存储,查询时通过 Trace ID 哈希直达。ES 是 Zipkin 常用存储,Cassandra 是 Jaeger 可选存储,内存数据库不是任何主流后端的设计选择。
Q4. 在 Zipkin 的查询模型中,用户主要通过什么条件检索 Trace?
- A.Trace ID 哈希取模
- B.标签键值对深度过滤
- C.服务名 + 操作名 + 时间范围← 正确答案
- D.日志内容全文搜索
答案:C。Zipkin 的查询模型基于服务名(serviceName)加操作名(operationName)加时间范围的组合检索。标签深度过滤是 Jaeger 的强项,Trace ID 哈希直达是 Tempo 的方式,日志全文搜索属于 Loki 的职责范围。
Q5. 从 Zipkin 迁移到 Tempo 时,最省力的数据上报方案是?
- A.重写所有微服务的埋点 SDK
- B.使用 Tempo 内置的 Zipkin 兼容 API 接收端点← 正确答案
- C.通过 Kafka 手工转发数据
- D.放弃旧数据,直接全量重新上线
答案:B。Tempo 内置 Zipkin API 的接收端点,Zipkin 客户端无需改动代码即可把 Span 上报给 Tempo。重写 SDK 成本高且无必要,Kafka 转发属于多余环节,放弃数据则违背迁移的初衷。这也是 Tempo 吸引 Zipkin 存量用户的核心卖点。
Q6. 关于 Jaeger 与 Tempo 的查询性能对比,以下说法正确的是?
- A.Tempo 支持按标签快速过滤,Jaeger 只能按 Trace ID 查
- B.Tempo 的 Trace ID 精确查询是 O(1) 哈希寻址,快于 Jaeger 的索引扫描← 正确答案
- C.Jaeger 因为使用 Go 语言一定比 Tempo 查询更快
- D.两者查询性能完全相同,没有可比性
答案:B。Tempo 按 Trace ID 哈希算出对象存储路径直接取件,复杂度 O(1);Jaeger 需要走索引扫描加存储读取,链路更长。A 与事实相反(Jaeger 才支持标签过滤),C 是错误归因,D 无视了架构差异。
Q7. 在链路追踪系统中,“索引写入放大效应”指的是?
- A.索引文件占用的磁盘空间逐年膨胀
- B.每条 Span 数据在写入时还要同步更新多套索引,放大写入成本← 正确答案
- C.查询变慢导致索引重建频率上升
- D.索引数量越多历史数据越分散
答案:B。写入放大指每新增一条 Span,除存储原始数据外,还需同步更新服务索引、标签索引等多套倒排索引,真实写入的磁盘数据远超有效数据量。这是 Zipkin(ES 索引)与 Jaeger(标签索引)的共性代价,也是 Tempo 零索引设计的省钱根本。
Q8. 以下哪些因素应该纳入追踪后端选型的评估维度?
- A.团队现有技术栈的亲和性← 正确答案
- B.业务规模与存储预算约束← 正确答案
- C.与现有监控体系(Grafana/Prometheus)的整合度← 正确答案
- D.后端开发语言的执行效率
答案:ABC。选型评估应关注技术栈亲和性(A)、规模预算(B)和生态整合度(C)。后端语言执行效率仅是工程实现的细节,并非选型核心考量——用户关心的是功能、成本、运维与生态,而非后端自身用什么语言实现。
Q9. 关于 Tempo 的查询机制,以下描述正确的是哪些?
- A.支持基于 Trace ID 的高效精确查询← 正确答案
- B.不支持复杂的标签条件搜索← 正确答案
- C.需要单独部署 Elasticsearch 作为查询索引
- D.通常与 Loki 配合完成“日志定位 ID,Tempo 取链路”← 正确答案
答案:ABD。Tempo 的查询以 Trace ID 哈希直达为核心,不做标签索引所以不支持复杂条件搜索,同时它依赖对象存储而非 ES。生产落地时“先由 Loki 从日志中找到 Trace ID,再到 Tempo 拉全链路”是最常见的实践路径。
Q10. 关于 OpenTelemetry(OTel)在追踪迁移中的作用,以下说法正确的是?
- A.OTel Collector 支持同时导出数据到新旧两套后端实现双写← 正确答案
- B.OTel 的 OTLP 格式与 Zipkin v2 格式在字段语义上不兼容
- C.OTel SDK 统一埋点后切换后端无需改动业务代码← 正确答案
- D.OTel 只支持链路追踪,不支持日志与指标
答案:AC。OTel Collector 确实支持多 exporter 配置实现双写,SDK 埋点后更换后端只需改 Collector 配置。OTLP 与 Zipkin v2 的字段语义高度兼容(Jaeger/Tempo 均支持 Zipkin 格式)。OTel 是涵盖 traces、metrics、logs 的三大信号统一标准,并非只做链路追踪。