2. Tempo 架构基础:微服务与核心组件

在深入值班台查询性能调优之前,必须先理解 Tempo 的底层架构。Tempo 采用微服务架构,将链路数据的接收、持久化、查询与压缩等职责拆分为独立的服务组件。这种设计带来了水平扩展能力与故障隔离性,但也对组件间的协作效率提出了更高要求——任何组件的瓶颈都会直接反映在值班台的查询延迟上。

2.1 整体微服务架构概览

Tempo 的架构遵循“写路径”与“读路径”分离的原则。写入链路数据时,数据流经 Distributor → Ingester → 后端存储;读取链路数据时,查询请求由 Querier 从存储中检索,并通过 Query Frontend 进行缓存与合并。Compactor 则在后台异步执行数据压缩与去重,优化存储效率。

Tempo 微服务架构总览 Tempo 微服务架构总览 写路径 Distributor Ingester 后端存储 读路径 Query Frontend Querier 后端存储 后台 Compactor 说明:写路径与读路径分离,Compactor 异步执行压缩,不阻塞实时查询。

2.2 核心组件详解

每个组件在链路数据的生命周期中承担特定职责。下表从功能定位、数据形态和性能敏感点三个维度进行对比:

组件 功能定位 数据形态 值班台查询性能敏感点
Distributor 接收链路数据,进行哈希分片与负载均衡 原始 Trace 批次 低(无状态,可水平扩展)
Ingester 缓存最近写入的链路数据,构建内存索引 未压缩的 Span 块 高——内存索引效率直接影响最新数据查询
Querier 执行查询请求,从 Ingester 和后端存储合并结果 查询中间结果 高——查询并发与合并策略决定响应时间
Query Frontend 查询路由、缓存、分片与合并 查询请求/响应 中——缓存命中率影响 P99 延迟
Compactor 合并小文件、去重、压缩存储 压缩后的块文件 低(异步后台任务)
后端存储 持久化链路数据(对象存储或 Cassandra) Parquet/Proto 块 中——IO 吞吐与块布局影响扫描效率

2.3 Ingester:内存中的“热数据层”

Ingester 是值班台查询性能调优的首要关注点。它维护最近一段时间(通常 30 分钟~1 小时)的链路数据,所有新写入的 Span 首先进入 Ingester 的内存。当 Ingester 的内存块达到阈值或时间窗口到期时,数据被刷写到后端存储。

  • 内存索引结构:Ingester 使用 TraceID → Span 列表 的哈希索引,支持 O(1) 精确查找;同时维护按时间排序的倒排索引,支持时间范围扫描。
  • 数据块(Block):每个 Ingester 实例管理多个内存块,块大小由 ingester.max-block-duration 和 ingester.max-block-bytes 控制。
  • 故障风险:Ingester 重启或 OOM 会导致未刷写的数据丢失,因此生产环境必须配置 WAL(Write-Ahead Log)或副本。
调优要点:值班台查询“最近 15 分钟”的链路时,如果延迟偏高,应优先检查 Ingester 的内存索引命中率。若命中率低于 90%,考虑增加 Ingester 副本数或调整块大小参数。

2.4 Querier:查询执行的“调度中枢”

Querier 接收来自 Query Frontend 的查询请求,同时向所有 Ingester 实例和后端存储发起子查询,最后合并结果返回。Querier 的查询策略直接影响值班台的响应速度:

  • Ingester 查询:Querier 通过 gRPC 流式接口并行查询所有 Ingester,获取内存中的热数据。
  • 存储查询:Querier 根据时间范围扫描后端存储中的块文件,利用索引文件(index)快速定位目标块。
  • 合并策略:Querier 按 TraceID 对多个来源的 Span 进行去重与排序,确保返回完整的 Trace 树。
性能观察:如果值班台查询“全量链路”时出现超时,通常是因为 Querier 需要扫描大量存储块。此时应检查 querier.max-concurrent-queries 和存储端的块大小配置。

2.5 Compactor:存储效率的“后台管家”

Compactor 定期扫描后端存储中的小文件,将其合并为更大的块,同时执行数据去重和压缩。虽然 Compactor 不直接参与查询路径,但它的工作质量决定了存储层的扫描效率:

  • 合并粒度:默认合并后的块大小为 512MB,可通过 compactor.compaction-block-size 调整。
  • 去重逻辑:Compactor 会丢弃重复的 Span 数据,减少存储冗余。
  • 压缩算法:支持 Snappy、LZ4、Zstd,其中 Zstd 压缩比最高但 CPU 开销较大。
Compactor 工作流程 Compactor 工作流程 小文件 1 (10MB) 小文件 2 (8MB) 小文件 3 (12MB) Compactor 合并 + 去重 + 压缩 大文件 (512MB) Zstd 压缩 说明:Compactor 减少小文件数量,降低 Querier 扫描时的 IOPS 开销。

2.6 组件协作与值班台查询路径

当值班台发起一次链路查询时,请求经过的完整路径如下:

  1. Query Frontend 接收查询请求,检查结果缓存;若命中则直接返回。
  2. 若未命中,Query Frontend 将查询分片后发送给 Querier。
  3. Querier 并行查询所有 Ingester 实例(获取热数据)和后端存储(获取冷数据)。
  4. Ingester 返回内存中的 Span 列表;后端存储返回匹配的块文件。
  5. Querier 合并、去重、排序后返回完整 Trace 给 Query Frontend。
  6. Query Frontend 将结果缓存并返回给值班台。
常见瓶颈:如果 Ingester 实例数过多,Querier 的 gRPC 连接数会线性增长,可能导致 Querier 端 CPU 或网络瓶颈。建议 Ingester 与 Querier 的比例控制在 3:1 以内,并启用连接池复用。

2.7 架构设计对调优的启示

理解 Tempo 的微服务架构后,可以总结出以下调优原则:

  • 写路径调优:关注 Distributor 的哈希均衡与 Ingester 的内存块大小,避免数据倾斜导致单点过热。
  • 读路径调优:优先优化 Query Frontend 的缓存策略和 Querier 的并发度,减少存储扫描次数。
  • 存储层调优:合理配置 Compactor 的合并周期与压缩算法,平衡存储成本与查询性能。