导读:
分布式链路追踪作为解决分布式应用可观测问题的重要技术,得物全链路追踪(简称Trace2.0)基于OpenTelemetry提供的可观测标准方案实现新一代的一站式全链路观测诊断平台,并通过全量采集Trace帮助业务提高故障诊断、性能优化、架构治理的效率。
全量采集Trace数据(日增数百TB 、数千亿条Span数据)并以较低的成本保证数据的实时处理与高效查询,对Trace2.0后端整体的可观测性解决方案提出了极高的要求。本文将详细介绍Trace2.0背后的架构设计、尾部采样和冷热存储方案,以及我们是如何通过自建存储实现进一步的降本增效(存储成本下降66%)。

全链路追踪Trace2.0从数据接入侧、计算、存储到查询整体模块架构如上图所示。这里说一下各组件的核心能力:
得物早期的全链路追踪方案出于对存储成本的考虑,在客户端设置了1%的采样率,导致研发排查问题时经常查询不到想看的Trace链路。那么Trace2.0为了解决这个问题,就不能仅仅只是简单地将客户端的采样率调整为100%,而是需要在客户端全量采集Trace数据的同时,合理地控制Trace存储成本。且从实践经验来看,Trace数据的价值分布是不均匀的,随着时间的推移Trace的数据价值是急速降低的。
全量存储Trace数据不仅会造成巨大的成本浪费,还会显著地影响整条数据处理链路的性能以及稳定性。所以,如果我们能够只保存那些有价值、大概率会被用户实际查询的Trace,就能取得成本与收益的平衡。那什么是有价值的Trace呢?根据日常排查经验,我们发现业务研发主要关心以下四类优先级高场景:
在这个背景下,并结合业界的实践经验,落地Trace2.0的过程中设计了尾部采样&冷热分层存储方案,方案如下:

整体处理流程如下:
接下来再介绍一下尾部采样中Bloom Filter的设计细节,如下图所示:

整体处理流程如下:
综上所述,Trace2.0仅使用了较少的资源就完成了尾部采样和冷热分层存储。既为公司节约了成本,又保存了几乎所有「有价值」Trace,解决了业务研发日常排查时查询不到想看的Trace的问题。
Trace2.0建设初期采用了SLS专为OpenTelemetry定制的Trace方案 【1】,提供了Trace查询、调用分析、拓扑分析等功能,如下图所示:

SLS-Trace主要处理流程如下:
随着Trace2.0在公司内部全面铺开,SLS的存储成本压力变得越来越大,为了响应公司“利用技术手段实现降本提效”的号召,我们决定自建存储。
目前业内比较流行的全链路追踪开源项目(SkyWalking、Pinpoint、Jaeger等)采用的存储大都是基于ES或者HBase实现的。而近几年新兴的开源全链路追踪开源项目(Uptrace【3】、Signoz【4】等)采用的存储大都是基于ClickHouse实现的,同时将Span数据清洗出来的指标数据也存储在ClickHouse中。且ClickHouse的物化视图(很好用)也很好地解决了指标数据降采样(DownSampling)的问题。最终经过一番调研,我们决定基于ClickHouse来自建新的存储解决方案。整体架构图如下:

整体处理流程如下:
计算&持久化SpanMetrics数据:OTel Storage会根据Span的Service、SpanName、Host、StatusCode等属性统计并生成「30秒」粒度的总调用次数、总耗时、最大耗时、最小耗时、分位线等指标数据,并写入到SpanMetrics表;
- -- span_metrics_10m_mv
- CREATE MATERIALIZED VIEW IF NOT EXISTS '{database}'.span_metrics_10m_mv_local
- on cluster '{cluster}'
- TO '{database}'.span_metrics_10m_local
- AS
- SELECT a.serviceName as serviceName,
- a.spanName as spanName,
- a.kind as kind,
- a.statusCode as statusCode,
- toStartOfTenMinutes(a.timeBucket) as timeBucket,
- sum(a.count) as count,
- sum(a.timeSum) as timeSum,
- max(a.timeMax) as timeMax,
- min(a.timeMin) as timeMin
- FROM '{database}'.span_metrics_30s_local as a
- GROUP BY a.serviceName, a.spanName, a.kind, a.statusCode,
- toStartOfTenMinutes(a.timeBucket);
ClickHouse写入细节
ClickHouse使用Distributed引擎实现了Distributed(分布式)表机制,可以在所有分片(本地表)上建立视图,实现分布式查询。并且Distributed表自身不会存储任何数据,它会通过读取或写入其他远端节点的表来进行数据处理。SpanData表创建语句如下所示:
- -- span_data
- CREATE TABLE IF NOT EXISTS '{database}'.span_data_local ON CLUSTER '{cluster}'
- (
- traceID FixedString(32),
- spanID FixedString(16),
- startTime DateTime64(6 ) Codec (Delta, Default),
- body String CODEC (ZSTD(3))
- ) ENGINE = MergeTree
- ORDER BY (traceID,startTime,spanID)
- PARTITION BY toStartOfTenMinutes(startTime)
- TTL toDate(startTime) + INTERVAL '{TTL}' HOUR;
-
- -- span_data_distributed
- CREATE TABLE IF NOT EXISTS '{database}'.span_data_all ON CLUSTER '{cluster}'
- as '{database}'.span_data_local
- ENGINE = Distributed('{cluster}', '{database}', span_data_local,
- xxHash64(concat(traceID,spanID,toString(toDateTime(startTime,6)))));
整体写入流程比较简单(注意:避免使用分布式表),如下所示:

上线效果
全链路追踪是一个典型的写多读少的场景,因此我们采用了ClickHouse ZSTD压缩算法对数据进行了压缩,压缩后的压缩比高达12,效果非常好。目前ClickHouse冷热集群各使用数十台16C64G ESSD机器,单机写入速度25w/s(ClickHouse写入的行数)。相比于初期的阿里云SLS-Trace方案,存储成本下降66%,查询速度也从800+ms下降至490+ms。
下一步规划
目前Trace2.0将Span的原始明细数据也存储在了ClickHouse中,导致ClickHouse的磁盘使用率会有些偏高,后续考虑将Span明细数据先写入HDFS/OSS等块存储设备中,ClickHouse来记录每个Span在块存储中的offset,从而进一步降低ClickHouse的存储成本。
关于我们:
得物监控团队提供一站式的可观测性平台,负责链路追踪、时序数据库、日志系统,包括自定义大盘、应用大盘、业务监控、智能告警、AIOPS等排障分析。
欢迎对可观测性/监控/告警/AIOPS 等领域感兴趣的同学加入我们。
引用
【1】SLS-Trace方案 释放Trace的价值-SLS OpenTelemetry新功能直击痛点-阿里云开发者社区
【2】SLS-Trace Contrib https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/alibabacloudlogserviceexporter
【3】Uptrace Distributed Traces, Logs & Errors (tracing, monitoring, observability, APM)
【4】Signoz Open source APM | SigNoz
【5】Uptrace Schema设计https://github.com/uptrace/uptrace/tree/v0.2.16/pkg/bunapp/migrations
本篇是《得物云原生全链路追踪Trace2.0》系列开篇,更多内容请关注“得物技术”公众号。
得物云原生全链路追踪Trace2.0架构实践
得物云原生全链路追踪Trace2.0产品篇
得物云原生全链路追踪Trace2.0采集篇
得物云原生全链路追踪Trace2.0数据挖掘篇
*文/南风
@得物技术公众号