Jaeger ClickHouse 存储后端基准测试指南:1 亿 Span 数据集下的压缩率、写入吞吐与查询延迟
2026/9/13 17:37:50 网站建设 项目流程

Jaeger ClickHouse 存储后端基准测试指南:1 亿 Span 数据集下的压缩率、写入吞吐与查询延迟

【免费下载链接】jaegerCNCF Jaeger, a Distributed Tracing Platform项目地址: https://gitcode.com/GitHub_Trending/ja/jaeger

本篇指南完整解读 Jaeger 官方仓库中 BENCHMARKING.md 记录的 ClickHouse 存储后端基准测试:在 1000 万 Span(100 万 Trace)真实数据集上,该后端取得了 8.6x 的数据压缩率、约 5.2 万 spans/sec 的写入吞吐,以及毫秒级服务/操作检索和百毫秒级按 Trace ID 取数的查询延迟。读完本文,你将理解这些数字背后的表结构与索引设计、掌握复现基准测试的方法,并学会如何为你的 ClickHouse 集群预估容量与查询性能。

基准测试概览

本基准测试面向 Jaeger 新一代存储架构(internal/storage/v2)中的 ClickHouse 存储后端(internal/storage/v2/clickhouse)。它回答了一个关键问题:当 ClickHouse 作为 Jaeger 的追踪数据仓库时,在海量 Span 数据下能获得怎样的存储效率与查询性能?

测试结果分为三部分:

维度关键结果
压缩率spans 表 5.99 GiB 未压缩数据被压缩至 722.09 MiB,压缩比 8.6x
写入吞吐1000 万 Span 总计耗时 191.8 s,平均 52,129 spans/sec
查询延迟服务/操作检索 3–4 ms,Trace ID 取数 101 ms,属性搜索 1,769 ms

以下各节将结合仓库源码逐项解读这些数字的由来与含义。

测试环境与数据集

测试环境

基准测试在一台 Oracle Cloud 的 VM.Standard2.4 实例上执行,硬件配置如下:

组件详情
VMOracle Cloud VM.Standard2.4(4 OCPUs,Intel Xeon Platinum 8167M)
内存60 GB
磁盘47 GB 块存储
操作系统Oracle Linux 9
ClickHouse26(单节点)

需要注意,这是单节点部署:4 个 vCPU 的规格意味着基准结果反映的是中小型实例上的性能表现,生产环境通过水平扩展(多节点分片/副本)可获得更优吞吐。

数据集

参数
Trace 总数1,000,000
每个 Trace 的 Span 数10(1 个父 Span + 9 个子 Span)
Span 总数10,000,000
服务数10
分区数(按天)10
每个 Span 的属性数11(分布于 97 个不同 key、1000 个不同值)

数据集覆盖了 10 天时间窗口,对应 spans 表PARTITION BY toDate(start_time)的按天分区设计(见 create_spans_table.sql)。97 个属性 key 与 1000 个属性值的规模,对属性元数据缓存与类型化属性查询构成真实压力。

存储压缩率:8.6x 从何而来

实测结果

指标
未压缩大小5.99 GiB
压缩后大小722.09 MiB
压缩比8.6x

压缩率主要来自三个设计选择:

  1. 列式存储天然压缩友好:ClickHouse MergeTree 按列压缩,同类数据(如service_name)聚集在一起,重复值压缩效率极高。
  2. 类型化属性数组:Jaeger 的 ClickHouse 后端没有像 OTel-contrib 实现那样把所有属性值统一转成字符串,而是将 key/value 拆分并按类型分列存储——bool_attributesdouble_attributesint_attributesstr_attributescomplex_attributes各自使用Array(Bool)Array(Float64)Array(Int64)Array(String)等列(见 create_spans_table.sql)。同类型数值列可比混合类型字符串列获得更好的压缩比。
  3. 复合类型 JSON 序列化:slice/map 等复杂类型统一序列化为 JSON 字符串存入complex_attributes,避免深层嵌套结构的存储开销。

从源码看表结构

spans 表是追踪数据的主表,其核心设计(create_spans_table.sql):

CREATE TABLE IF NOT EXISTS spans ( id String, trace_id String, ... start_time DateTime64(9), duration Int64, bool_attributes Nested (key String, value Bool), double_attributes Nested (key String, value Float64), int_attributes Nested (key String, value Int64), str_attributes Nested (key String, value String), complex_attributes Nested (key String, value String), events Nested (...), links Nested (...), ... INDEX idx_trace_id trace_id TYPE bloom_filter GRANULARITY 1, INDEX idx_duration duration TYPE minmax GRANULARITY 1 ) ENGINE = MergeTree PARTITION BY toDate(start_time) ORDER BY (service_name, name, toDateTime(start_time))

两个跳过索引直接服务于基准测试中的两类查询:

  • idx_trace_id(bloom_filter)让「按 Trace ID 取数」不必全表扫描;
  • idx_duration(minmax)让「按持续时间范围搜索」能快速裁剪数据块。

排序键(service_name, name, toDateTime(start_time))则与「按服务搜索」「按操作搜索」的查询模式对齐。这一表结构与 OTel-contrib 的 ClickHouse exporter 相比有显著差异,详见 README.md 中的说明。

写入吞吐:52,129 spans/sec

指标
Span 总数10,000,000
总写入耗时191.8 s
吞吐量52,129 spans/sec

写入路径的高吞吐依赖两个关键机制:

  1. ch-go批量插入:写操作使用ch-gochpool以 batch 模式写入(见 README.md)。在 writer.go 中,WriteTraces首先PrepareBatch,将ResourceSpans → ScopeSpans → Spans逐层遍历后逐条batch.Append,最后一次性batch.Send,将网络往返次数压缩到最低。
  2. 按类型拆分属性列dbmodel.ToRow将 span 的属性、Resource 属性、Scope 属性、Event 属性、Link 属性分别转换为类型化 key/value 数组(见 dbmodel/from.go),写入时直接对齐InsertSpan语句中 60 余个占位符列(见 sql/queries.go),避免了类型转换开销。

写入侧的连接专门用于写、不开启埋点插桩(见 writer.go 注释),防止追踪自身产生递归写入流量。

查询延迟:Retrieval 与 Search 两个维度

基准测试将查询分为两类,每个查询运行 3 次取平均

Retrieval Queries(按 ID/名称直取)

查询平均耗时
获取服务列表(Retrieve services)3 ms
获取操作列表(Retrieve operations)4 ms
按 Trace ID 取 Trace(Get trace by ID)101 ms

服务与操作检索的毫秒级延迟来自两张物化视图表

  • services +create_services_mv.sql物化视图,配合SELECT name FROM services GROUP BY name查询(queries.go);
  • operations +create_operations_mv.sql物化视图,按service_namespan_kind过滤(queries.go)。

按 Trace ID 取数耗时 101 ms,由SelectSpansByTraceIDSELECT ... FROM spans s WHERE s.trace_id = ?,见 queries.go)配合idx_trace_idbloom_filter 索引支撑。取数后还需在 Go 侧完成 span 行反序列化与 trace 组装(reader.go)。

Search Queries(带过滤条件的搜索)

查询平均耗时
按服务搜索37 ms
按操作搜索38 ms
按持续时间范围搜索43 ms
按时间戳范围搜索47 ms
按属性搜索1,769 ms
按全部条件组合搜索139 ms

搜索路径先执行buildFindTraceIDsQuery构造「查 Trace ID」的子查询,再用buildFindTracesQuery将子查询嵌入WHERE s.trace_id IN (...)外层取数(query_builder.go)。内部子查询基于SearchTraceIDsBaseWHERE 1=1便于无条件下追加AND条件,见 queries.go),并以trace_id_timestamps表 JOIN 补全 Trace 的起止时间(queries.go)。

属性搜索(1,769 ms)显著慢于其他查询,原因是属性查询需要对嵌套数组执行arrayExists((key, value) -> key = ? AND value = ?, ...)的逐行扫描(query_builder.go),且需依次检查 span、resource、scope、events、links 五个层级(buildSimpleAttributeCondition,query_builder.go)。若读者关注属性检索性能,这是当前实现的主要瓶颈所在。

值得注意,组合全部条件反而快于单独属性搜索(139 ms vs 1,769 ms):额外的过滤条件(服务名、时间范围等)借助排序键与分区裁剪大幅缩小了需要扫描的 Span 集合。这提示一个实用调优思路——属性搜索应尽量与其他条件组合使用。

字符串属性的类型还原机制

基准数据集中 11 个属性/span 分布于 97 个 key,字符串属性的查询走buildStringAttributeCondition路径:查询服务以AsString()形式传入所有属性,后端需要查 attribute_metadata 表获知该 key 实际存储的类型(bool/double/int/str/bytes/map/slice)与层级(resource/scope/span),再通过strconv反向解析为正确类型生成条件(query_builder.go)。属性元数据本身带有 LRU 缓存,TTL 与容量分别由配置项attribute_metadata_cache_ttl(默认 1h)与attribute_metadata_cache_max_size(默认 1000)控制(config.go)。

复现基准测试

原文档给出如下复现指引:

See the clickhouse-benchmarking repository for setup and reproduction instructions.

(该仓库为 Jaeger 团队成员的独立 benchmark 工程,包含环境搭建、数据集生成、schema 初始化与查询脚本。)

在复现前,你需要先在本仓库中启动 ClickHouse 存储后端。最直接的方式是使用官方示例配置 config-clickhouse.yaml,它演示了jaeger_storage扩展的完整配置:

extensions: jaeger_storage: backends: some-storage: clickhouse: addresses: - localhost:9000 database: jaeger auth: basic: username: default password: password create_schema: true

其中create_schema: true会执行本仓库 sql 目录 下全部建表与物化视图脚本(spans、services、operations、trace_id_timestamps、attribute_metadata、dependencies 及其物化视图),这些 SQL 通过go:embed嵌入二进制(queries.go)。

关键配置项

ClickHouse 后端的完整配置项定义在 config.go,复现基准时可重点关注:

配置项默认值说明
protocolnative连接协议,可选native/http
addresses必填ClickHouse 服务器地址列表
databasejaeger目标数据库
auth.basic用户名/密码认证
create_schemafalse启动时自动建表
default_search_depth1000未指定 limit 时的默认搜索深度(返回的 Trace ID 上限)
max_search_depth10000允许的最大搜索深度
attribute_metadata_cache_ttl1h属性元数据缓存 TTL,0 表示永不过期
attribute_metadata_cache_max_size1000属性元数据缓存条目上限,0 表示禁用
ttl0Span 数据自动删除的 TTL,0 表示禁用

配置校验逻辑(config.go)要求:ttl必须为非负且为整秒数;default_search_depthmax_search_depth必须为正数(否则每次搜索都会返回空结果)。搜索深度限制在 query_builder.go 中强制执行——超过max_search_depth的查询会被直接拒绝。

数据生成与写入

复现 1000 万 Span 的数据集有两种途径:

  1. 外部 benchmark 仓库:按其setup/native目录指引生成原生 schema 与数据集(原文档推荐路径)。
  2. 仓库自带工具:使用 cmd/tracegen 生成追踪数据并经 OTLP 接收器写入 Jaeger;写入路径会经过 writer.go 的批量插入逻辑,与基准测试的写入侧行为一致。

读基准测试时应关注的要点

  1. 环境差异:4 vCPU/60 GB 单节点配置下测得的结果,在更大规格或集群部署下数字会变化,请勿直接外推为生产容量承诺。
  2. 属性搜索是当前热点:1,769 ms 的属性单独搜索耗时明显高于其他查询,若你的业务大量依赖属性过滤,应结合attribute_metadata缓存命中率与查询组合方式做针对性优化。
  3. 压缩率支撑成本评估:8.6x 压缩比意味着 5.99 GiB 原始数据仅需约 0.7 GiB 存储,可作为磁盘容量规划的下限参考。
  4. 复现的起点在仓库内:从 config-clickhouse.yaml 出发搭建环境,再运行 benchmark 仓库的 schema 与查询脚本,即可得到可对比的数字。

参考源码索引

  • 后端入口与配置:factory.go、config.go
  • 建表与物化视图 SQL:sql 目录
  • 写入实现:tracestore/writer.go
  • 读取实现:tracestore/reader.go
  • 查询构造器:tracestore/query_builder.go
  • 类型化属性映射:tracestore/dbmodel/from.go
  • 指标(RED 指标)存储:metricstore/reader.go
  • 官方部署配置示例:cmd/jaeger/config-clickhouse.yaml、cmd/jaeger/config-spm-clickhouse.yaml
  • 官方集成测试:cmd/jaeger/internal/integration/clickhouse_test.go
  • 本基准测试文档:BENCHMARKING.md

【免费下载链接】jaegerCNCF Jaeger, a Distributed Tracing Platform项目地址: https://gitcode.com/GitHub_Trending/ja/jaeger

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询