3. 可观测性数据模型与存储选型

可观测性平台的能力上限,往往在数据模型和存储选型阶段就已经被决定。采集端再完善,如果模型设计无法表达真实查询需求,或者存储引擎与访问模式不匹配,最终都会表现为“数据都在,但查不出来”或“查得出来,但成本失控”。本节围绕四类核心数据模型——时序、日志、追踪、事件——讨论设计要点,并给出主流存储引擎在生产场景下的对比与选型判断。

四类数据模型的设计要点

时序模型的核心是“指标名 + 标签集 + 时间戳 + 数值”。设计时最关键的是标签基数控制:标签是索引维度,不是描述字段。例如 http_requests_total{service="order", method="POST", status="500"} 中,service、method、status 适合作为标签,而 user_id、request_id 这类高基数维度一旦进入标签,会迅速膨胀索引并拖垮写入与查询。生产经验是:单指标的标签组合数控制在十万级以内,超过则改用日志或追踪承载明细。此外,指标应遵循“预聚合 + 降采样”策略,原始精度保留 15 天,之后按 5 分钟、1 小时粒度归档,以控制长期存储成本。

日志模型的设计要点在于“结构化程度”与“检索字段”的平衡。纯文本日志写入便宜但查询昂贵;全字段结构化写入昂贵但查询高效。生产上通常采用折中方案:保留原始消息体,同时抽取 5~10 个高频检索字段(如 level、service、trace_id、error_code)建立倒排索引,其余字段仅存储不索引。这样既保证 trace_id 关联查询的秒级响应,又避免索引膨胀。

追踪模型以 Span 为基本单元,通过 trace_id 串联、parent_span_id 构建调用树。设计要点是采样策略与属性裁剪:头部采样(Head-based)在入口决定是否记录,实现简单但可能丢失慢请求;尾部采样(Tail-based)在链路结束后根据耗时、错误状态决定保留,能精准捕获异常链路,但需要缓冲全部 Span,对内存和延迟有要求。属性方面,每个 Span 建议只保留 10~20 个关键属性,避免把整个请求体塞入 Span。

事件模型用于表达部署、扩缩容、告警、配置变更等离散动作,核心字段是 event_type、timestamp、source、payload 和关联的资源标识。事件模型的价值在于“因果关联”:当指标异常时,能快速判断是否由某次发布或配置变更触发。设计上应保证事件与指标、追踪共享同一套资源标识(如 service、instance、cluster),否则关联分析会退化为人工比对。

四类数据模型 → 存储引擎匹配关系 时序模型 metric + labels + ts 低基数 · 预聚合 · 降采样 日志模型 message + 索引字段 倒排索引 · 冷热分层 追踪模型 span + trace_id + parent 采样策略 · 属性裁剪 事件模型 event_type + payload 资源标识 · 因果关联 Prometheus / VictoriaMetrics 写入 100万~500万 samples/s 查询 P99 50~200ms Elasticsearch / Loki / ClickHouse 写入 10万~100万 docs/s 查询 P99 200ms~5s Jaeger / Tempo / ClickHouse 写入 5万~50万 spans/s 查询 P99 100ms~2s Kafka + ClickHouse / PG 写入 1万~20万 events/s 查询 P99 50ms~500ms 生产选型决策要点 写入吞吐优先 时序 → VictoriaMetrics / Prometheus 日志 → ClickHouse / Loki 查询延迟优先 日志 → Elasticsearch 倒排索引 追踪 → Jaeger + ES 后端 成本敏感 对象存储 + 列存(Parquet/ORC) 冷热分层 · 降采样 · TTL 策略 常见反模式 · 高基数标签进时序索引 · 全字段索引日志导致膨胀 · 追踪全量采样无裁剪 · 事件与指标资源标识不统一

存储引擎的生产对比

下表给出四类模型在典型生产规模下的引擎对比。需要说明的是,表中的吞吐与延迟为合理假设的示例区间,用于建立量级判断,实际数值受硬件、分片、索引策略和查询复杂度影响显著。

数据模型 代表引擎 写入吞吐(示例量级) 查询延迟(示例量级) 成本特征
时序 Prometheus / VictoriaMetrics 100万~500万 samples/s P99 50~200ms 压缩率高,长期存储成本低
日志 Elasticsearch / Loki / ClickHouse 10万~100万 docs/s P99 200ms~5s ES 索引成本高,ClickHouse 压缩优
追踪 Jaeger / Tempo / ClickHouse 5万~50万 spans/s P99 100ms~2s 采样率直接决定成本
事件 Kafka + ClickHouse / PostgreSQL 1万~20万 events/s P99 50ms~500ms 数据量小,成本可控

选型判断与落地建议

选型不是“选一个最强的引擎”,而是“让访问模式与存储特性对齐”。如果查询以聚合、降采样、范围扫描为主,列存或时序引擎更合适;如果查询以全文检索、多条件过滤为主,倒排索引引擎更合适;如果查询以 trace_id 点查和调用树重建为主,则需要支持高并发点查和嵌套结构展开的引擎。

一个可落地的组合方案是:时序数据写入 VictoriaMetrics,保留 15 天原始精度,之后降采样到 5 分钟粒度保留 1 年;日志写入 ClickHouse,按天分区,trace_id 建跳数索引,30 天后转对象存储;追踪采用尾部采样,仅保留错误链路和 P99 以上慢链路,写入 ClickHouse;事件写入 Kafka 后落 ClickHouse,与指标、追踪共享 service 和 instance 标识。这样在写入吞吐、查询延迟和成本之间取得平衡,也便于后续做跨模型的关联诊断。

判断存储选型是否合理,可以问三个问题:第一,最频繁的查询能否在 1 秒内返回?第二,单位数据的存储成本是否随数据量线性增长?第三,当指标异常时,能否在 3 次点击内关联到对应日志和追踪?如果答案是否定的,说明模型或引擎需要重新评估。