说实话,这个话题我酝酿了很久了。可观测性领域的存储选型,从两三年前到现在,几乎每隔半年就会有一轮新的争论。很多人只要看到“可观测性数据”,第一反应就是“上时序数据库”。没错,指标数据、监控曲线、告警趋势,这些听起来天生就是时序数据库的主场。但如果你真的在一线做过大规模可观测性平台,会发现一个反直觉的现象:时序数据库最凶险的对手,根本不是另一款时序数据库,而是那些根本不叫时序数据库的存储系统。
作为一个长期在监控与大数据场景里来回折腾的从业者,这篇“从一到无穷大 #63”我打算认真掰扯一下这个问题:在可观测领域,谁才是时序数据库真正的对手?我又该用什么思路来选型?文章会从三类真正有威胁的替代方案讲起,再拆一下时序数据库自身的结构设计逻辑、优势边界,最后给出一套能落地的选型清单和避坑经验。不管你是刚开始搭监控体系,还是准备把现有存储换掉,这篇都能给你一些实打实的参考。
1. 先说结论:可观测性领域的竞争格局变了
1.1 从三大支柱说起,时序数据库的“舒适区”
可观测性领域传统的“三大支柱”——Metrics(指标)、Logs(日志)、Traces(链路追踪),基本定义了过去十年监控体系的存储格局。时序数据库在Metrics这一块几乎是绝对优势,Prometheus、InfluxDB、VictoriaMetrics、TimescaleDB这些名字,做监控的人没有不知道的。
为什么时序数据库在指标场景这么能打?原因其实很直白:指标数据天生就是“带时间戳的数值序列”,写入模式高度固定,几乎都是append-only(只追加,不修改),查询模式也相对统一,大多按时间范围做聚合、降采样、阈值判断。这种高度模式化的数据形态,正好跟时序数据库的LSM-Tree存储结构、倒排标签索引、以及Gorilla压缩算法严丝合缝地对上了。
但舒适区待久了,问题就来了。可观测性领域这几年最大的变化,就是三大支柱的边界正在快速模糊。以前大家各存各的,指标进时序库,日志进ES,链路进Jaeger/Tempo。现在呢?业务分析要看指标趋势,链路排查要看日志上下文,告警要看多维标签聚合。用户不想管你的存储架构,就想用一套体系把所有观测数据打通。在这种需求下,时序数据库那种“专精指标”的定位,反而成了某种掣肘。
你可以想象一下这个场景:公司做一次大规模故障复盘,老板丢过来一个问题——这个请求从登录到下单,为什么延迟涨了3倍?你手上有Prometheus的指标、Grafana的曲线、ES的日志、Jaeger的调用链,但要把这些数据串到同一个时间轴上做关联分析,几乎得靠人肉拼凑。这种体验在中小规模下还能忍,数据量一上来,你会发现时序数据库的边界问题越来越扎眼。
1.2 真正的对手不在同一条赛道上
很多人聊时序数据库的对手,喜欢拿Prometheus对标VictoriaMetrics,拿InfluxDB对标TimescaleDB,觉得这是同类产品之间的竞争。但我在实际工作中的体会是,这种竞争根本伤不到时序数据库的筋骨,因为用户一旦用了某个时序库,迁移成本极高——PromQL生态、告警规则、Grafana数据源、历史数据迁移,都是沉没成本。同类产品之间的争夺,更像是在分食同一个蛋糕,而不是抢蛋糕。
真正的威胁,来自那些用完全不同存储哲学来处理可观测性数据的产品。我总结下来主要有三类:
- 列式OLAP数据库,以ClickHouse为代表,用一套SQL统一承接指标、日志、链路数据;
- 存储与查询分离的架构,以Grafana Loki和Thanos/Cortex/Mimir为代表,把成本重心从计算转移到对象存储;
- 面向AI应用场景的垂直可观测平台,比如Langfuse这类产品,它们根本不把时序数据当作核心模型。
这三类“对手”的进攻路径各不相同,但共同点是:不跟你讲“时序数据库该怎么优化”,而是直接换个问题——“如果我不需要专门存时序数据,是不是也能把可观测性做出来?”这个问题的杀伤力,比任何同类竞品都大。
2. 真正的对手一:列式OLAP数据库,尤其是ClickHouse
2.1 ClickHouse为什么能在可观测领域攻城略地
过去几年,ClickHouse在可观测性领域的地位几乎可以用“野蛮生长”来形容。从Uber的监控平台、Cloudflare的可观测性数据存储,到国内各家大厂的日志平台、链路存储,背后都有ClickHouse的影子。你去看ClickHouse的中文社区,聊得最多的不是BI报表,而是日志、链路、Metrics三合一。
为什么是ClickHouse?我理解核心有几点。第一,统一的查询体验。ClickHouse提供标准SQL,不管查指标还是查日志,都是同一套语法。相比PromQL这种专用语言,SQL的通用性让数据分析师、后端开发甚至运维都能快速上手。第二,列式存储带来的暴力压缩和极快扫描。可观测性数据大多带有重复度极高的标签值(如环境、服务名、状态码),列式压缩比非常高,存储成本甚至可以比时序数据库更低。第三,一个集群统一管理,运维成本大幅下降。时序数据库通常只能管指标,日志还得另起炉灶,而ClickHouse可以把三类数据全部收编,从架构上看确实很有吸引力。
举个具体的场景。某家公司有几千个微服务,每个服务都暴露了CPU、内存、QPS等指标,过去的方案是Prometheus多级联邦再配Thanos长期存储。数据规模上来之后,查询变慢、对象存储费用越来越高。后来他们把指标数据通过remote write协议写到ClickHouse,用一条SQL就能替代几十个PromQL查询,比如按服务维度聚合的复杂JOIN,这在PromQL里很难写,在SQL里却非常简单。
2.2 一个指标查询场景的对比与SQL示例
光说优点有点空洞,我给个实际例子。假设我们要查“最近15分钟,每个服务的P99延迟和错误率”,经典做法是在Grafana里用PromQL拼表达式:
// PromQL 写法 histogram_quantile(0.99, sum by (le, service) (rate(http_request_duration_seconds_bucket[5m]))) sum by (service) (rate(http_requests_total{status=~"5.."}[5m])) / sum by (service) (rate(http_requests_total[5m]))这套表达式在指标量少的时候没问题,但一旦服务数量多、标签维度复杂,Prometheus的查询引擎计算压力会很大,Grafana渲染也容易超时。如果落到ClickHouse,情况就完全不一样。你可以设计一张统一的指标事件表:
CREATE TABLE metrics.events ( `timestamp` DateTime64(3) CODEC(Delta, ZSTD), `service` LowCardinality(String), `metric_name` LowCardinality(String), `labels` Map(String, String), `value` Float64 CODEC(Gorilla, ZSTD) ) ENGINE = MergeTree PARTITION BY toDate(timestamp) ORDER BY (metric_name, service, timestamp);然后一次SQL把P99和错误率都算出来:
SELECT service, quantile(0.99)(value) AS p99_latency, countIf(labels['status'] LIKE '5%') / count() AS error_rate FROM metrics.events WHERE metric_name = 'http_request_duration_seconds' AND timestamp >= now() - INTERVAL 15 MINUTE GROUP BY service;这里我用了LowCardinality来优化服务名列,用Map类型来存储动态标签,用quantile聚合函数直接算分位数。在实际落地中,这种表的查询性能可以做到每秒扫描几亿行,这是传统时序数据库很难想象的。这也是为什么现在很多做可观测性存储的团队,开始把Prometheus的存查分离,让Prometheus只负责采集和告警,最终分析查询全部走ClickHouse。
3. 真正的对手二:存储与查询分离的架构
3.1 Loki模式:对象存储加按需查询,成本低到可怕
时序数据库的核心是把数据在本地做好索引和压缩,计算和存储是绑在一起的。但Grafana Loki给出了一个完全不同的思路:索引只存标签,日志内容直接扔到对象存储(S3、MinIO、OSS),查询时再去对象存储里拉数据。这个设计初看有点反常识——没有索引怎么查询?但它的逻辑是:可观测性数据尤其是日志数据,冷数据访问频率极低,没必要用昂贵的本地索引把它们养着;丢到对象存储里,存储成本可以降到原来的十分之一甚至更低。
这种架构的杀伤力在成本对比上特别明显。同样的日志量,Elasticsearch可能需要好几台高配机器跑索引,而Loki可以只要少量querier节点,数据主体在对象存储里,存储费用按量计费,查询时临时加载。对于日志量巨大但查询频率不高的公司来说,用Loki和用ES的成本差距可能是一个数量级。这一点对可观测性建设的预算决策影响非常大,因为日志往往是可观测性数据里体量最大、价值密度最低的一类。
但说实话,Loki模式并不是银弹。把数据放到对象存储之后,查询延迟确实会变高,尤其是查那些时间跨度长、标签筛选条件少的冷数据,可能会从毫秒级退化到秒级甚至十秒级。在我自己的实践里,这是很多工程师难以接受的:习惯了ES那种点一下就能看到日志全文的体验,用Loki查一次要等好几秒,交互上就会觉得“卡”。所以Loki更适合那些对实时查询要求不高、但日志量巨大且以合规存储和事后审计为主的场景。
3.2 成本驱动的架构变迁正在改变选型逻辑
可观测性数据的生命周期管理和成本控制,这几年变得越来越重要。数据量永远在涨,但预算不会永远跟着涨。时序数据库传统上讲究“热数据优先”,把最近一段时间的数据放在本地高性能存储,旧数据要么降采样,要么淘汰。这种做法在指标场景下还能接受,因为指标价值衰减快;但日志和链路数据往往需要保留更长时间来做趋势分析和安全审计。
在这种需求下,对象存储作为冷层已经成为主流趋势。时序数据库阵营也做出了妥协,比如Thanos和Mimir都支持把块数据落到对象存储,Prometheus热数据只保留短时间,通过Sidecar上传长期块。这是对“存储与查询分离”思路的一种吸收,但本质上,时序数据库仍然要维护自己的索引和合并逻辑,复杂度并没有降低。
我见过不少团队,最初用的是时序数据库存指标,后来面对日志和链路时直接选择Loki方案,再往后干脆把所有数据统一到对象存储加查询引擎的架构里,时序数据库的角色被压缩成一个采集缓冲层。这本身就是“对手”在改变选型逻辑的一个信号:当你不再需要为每一种数据形态专门维护一套存储时,时序数据库的不可替代性就会被逐步稀释。
4. 真正的对手三:面向AI应用的垂直可观测平台
4.1 Langfuse的启示:可观测性的定义在变化
聊到Langfuse,可能很多人第一反应是“这是什么”。Langfuse是一个开源的LLM应用可观测性平台,专门用来跟踪AI应用的请求链路、Prompt输入输出、Token消耗、评估打标、Session回放等。从它身上,你能非常清晰地看到可观测性领域正在发生的第三个方向变化:可观测性的核心对象,正在从“服务器和微服务”转向“模型和业务行为”。
传统可观测性回答的是“系统出了什么问题”,比如CPU是否打满、某个接口的P99延迟是否飙高、错误码是否增多。但有了LLM应用之后,问题变成了“这次模型调用为什么回答质量差”“哪个Prompt版本导致Token消耗暴增”“某次会话里用户和模型的交互过程是怎样的”。这些问题已经远远超出时序数据的表达范畴——你没法用一个时间序列来刻画一次RAG检索的上下文,也没法用指标曲线来表达一个Prompt模板的血缘关系。
Langfuse这类平台的存储模型,更多是事件型、关系型和向量型的混合。它需要保存多轮对话记录、调用参数、评价结果、甚至向量化后的语义内容,这些数据是高度嵌套和非结构化的,天然适合文档模型或者关系模型。时序数据库在这些场景里能做什么?可能只是在底层记录一下模型API调用延迟和Token数量的指标,但核心业务洞察完全轮不到它。
4.2 AI场景下时序数据的“失语”
我们再往深一层看,AI应用可观测性的数据特征,跟传统时序数据差的不是一点半点。传统时序数据是“规律产生、确定性采集”,一个监控Agent按固定间隔上报指标数值;AI可观测性数据则是“事件驱动、高基数、长尾分布”的典型代表。一次对话可能产生几十个事件,每个事件都带不同的Metadata、不同的Token统计、不同的评估分数;这些数据的关联关系更像一张图,而不是一条时间线。
我自己参与过几个AI项目的数据采集方案设计,最后的存储选型几乎没有考虑时序数据库。原因很简单:产品要看的仪表盘不是“过去24小时的QPS曲线”,而是“不同模型的调用对比表格”“Prompt版本的评估分数分布”“用户会话的语义聚类”。这些分析需求,要么用分析型数据库做聚合,要么用专门的AI可观测平台自带的存储,时序数据库的核心能力完全匹配不上。
当然,AI应用也需要监控底层资源,比如GPU利用率、推理服务的请求延迟、模型部署的并发数,这些仍然是时序数据库的活儿。但请注意,这块在整套AI可观测性体系里只占很小的份额,更像是一个附属模块。当可观测性的抓手从“性能指标”转向“行为理解”,时序数据库就从一个主场选手变成了场边候补,这种角色的转变,对选型的影响是根本性的。
5. 拆开时序数据库的看家本领:结构设计逻辑
5.1 写入路径与存储结构,为什么时序数据库快
要理解时序数据库为什么会在这场竞争中处于现在的位置,得先拆一下它的结构设计。时序数据库的核心写入路径,几乎都基于LSM-Tree(Log-Structured Merge-Tree)。你可以把LSM-Tree想象成一个堆满东西的书桌:先在内存里搭一个有序缓冲(MemTable),数据按时间序排好,写满之后一次性落到磁盘,形成一个小文件(SSTable)。后续再把多个小文件逐步合并成大文件。
这种设计的最大好处是写入路径极度平滑。数据进来只做顺序追加,不做随机写,也不做原地更新,所以能扛住监控场景下那种“一秒几百万点”的持续高并发写入。相比关系型数据库用B+Tree维护实时更新索引,时序数据库在写入性能上完全不是一个量级。
但代价也很明显:合并操作是持续的后台任务,一旦数据规模大到一定程度,合并本身会消耗大量CPU和IO。如果你同时写多个高基数的时序(后面会讲高基数问题),合并风暴能把一个节点拖到几乎不可用。这也是很多团队在规模上来之后,对自建时序数据库集群感到头疼的重要原因。
5.2 索引、压缩和生命周期管理
时序数据库的第二个看家本领是倒排索引。通俗说,就是为每个标签(比如service="checkout"、host="10.0.2.15")建立一个反向列表,记录哪些时间序列属于这个标签。查询时通过标签组合筛选,能快速定位到一批序列,而不是全表扫描。这一点在Prometheus的TSDB里被发扬光大,每个标签对应一个倒排表,查询时取交集,速度非常快。
但是,倒排索引有“高基数”这个致命的死穴。什么是高基数?假设你给HTTP请求加一个标签user_id,用户量有1000万,那就有1000万个不同的标签值。每个新用户ID都会创建一条新的时间序列,倒排索引和后续的数据文件都会膨胀。内存里要维护的序列元数据越来越多,压缩率会断崖式下降,查询也开始变慢。这是时序数据库最经典的性能问题,几乎所有主流时序库都会在高基数场景下崩过。
再看压缩。时序数据压缩算法目前的主流是Gorilla,它的思路很巧妙:对相邻时间戳做delta-of-delta编码,对相邻数值做XOR运算,大部分情况下相邻点的差值很小、XOR结果为0,所以压缩率非常高。这也是为什么时序数据库能比传统数据库在指标存储上节省几十倍空间的核心原因。
不过,Gorilla的压缩是建立在“数据是平滑连续时间序列”的假设之上的。如果你塞的是突发性强、取值随机的事件数据,压缩率会大打折扣。这就解释了为什么时序数据库在存日志、存事件时表现不如专用的分析型存储——它的优化逻辑天生服务于“周期性采集的数值曲线”,而不是“稀疏杂乱的行为记录”。
6. 实操选型:什么时候继续用TSDB,什么时候换赛道
6.1 一张决策清单帮你理清思路
讲了这么多对手,肯定有人会问:“那到底该选什么?”我给一个自己常用的决策框架,按数据形态、查询模式、成本结构和团队能力四个维度来判断。
如果数据形态是“稳定的数值序列”,比如服务器CPU、内存、QPS、P99延迟,查询模式是“最近几小时/几天的时间范围聚合”,那时序数据库依然是最省心的选择,不要犹豫,直接上Prometheus或VictoriaMetrics。
如果数据形态是“高基数指标”,比如带user_id、request_id、api_key等维度的业务指标,而且需要经常做多维组合查询,我会强烈建议把查询层放到ClickHouse或Doris这类分析型数据库。时序数据库不是不能做,但成本和性能曲线会让你欲哭无泪。
如果数据形态是“海量日志”,重点不是实时全文检索,而是长期的合规存储和低频调查,那Loki这种对象存储加按需查询的架构最划算。ES当然也好,但预算不充裕的时候,Loki的存储成本优势是真真切切的。
如果数据形态是“AI应用事件/会话/Trace”,比如LLM调用的链路和评估数据,那就别死盯着时序数据库了,应该考虑Langfuse这类垂直平台,或者直接用PostgreSQL/MongoDB/ClickHouse自建一套事件存储,再在外面套一层语义检索和向量搜索。
在这个框架里,很多团队其实会发现自己处在混合状态。没关系,可观测性本身就不是一个存储引擎能包打天下的。
6.2 混合架构才是现实中的主流
坦白讲,我很少看到有团队能靠一套存储解决所有可观测性问题。大部分现实中的架构是“指标进时序数据库,日志进ES或对象存储,链路和分析进ClickHouse”。这种混合架构看似复杂,但只要把边界理清楚,选型逻辑就顺了。
比如我自己的一个典型实践:Prometheus负责采集和告警,短期指标保留15天在TSDB;超过15天的指标通过Thanos落对象存储,用于月度复盘和容量规划。日志走Loki,只保留全文索引在最近7天,超过7天的直接归档到S3。链路数据使用Jaeger,后端存储指向ClickHouse,用于长周期的调用链聚合分析。AI应用的Prompt评估和会话记录,则单独上一套Langfuse。
这样做最大的好处是每类数据都能在自己最擅长的存储里发挥优势,不会被“单一大而全”的架构拖垮。坏处也明显:组件多,运维复杂。但这恰恰是可观测性领域现阶段需要接受的事实——数据形态决定存储形态,架构可以简化,但不应该用一套方案去硬适配所有场景。
7. 常见问题排查与实践心得
7.1 高基数问题,怎么发现和缓解
高基数问题可能是时序数据库在可观测性场景中最常见的故障之一。症状通常是这样:某天发现Prometheus的内存飙升、查询变慢,Grafana上的图表转圈,但采集器本身没问题。排查看数据源,最直接的方法是查序列数。
在Prometheus里,可以执行:
// 查看每个指标名的序列数量 topk(10, count by (__name__)({__name__=~".+"}))还能按标签维度的取值数量排查:
// 查看某个高基数标签的大致基数 count(count by (user_id)({__name__="http_request_duration_seconds_bucket"}))如果发现一个标签的取值数量上了十万甚至百万,基本可以断定高基数问题就在这。缓解手段有几种:第一,业务维度和基础设施维度分离,业务指标尽量不带超高基数的唯一ID标签;第二,给Prometheus加一层缓存或者用VictoriaMetrics这类兼容PromQL的存储,他们对高基数的容忍度更高;第三,如果业务场景实在绕不开,就把分析需求迁到ClickHouse,让TSDB只保留核心监控指标。
7.2 我自己踩过的几个选型坑
最后分享几个实际踩坑的教训。我第一次把指标全部从Prometheus迁到ClickHouse的时候,一度以为自己找到了终极答案,但跑了一段时间发现两个问题:一是PromQL生态太强了,Alertmanager的告警规则、Grafana的现成Dashboard、各种Exporter都是围绕Prometheus设计的,迁移之后这些资产全部需要重写;二是ClickHouse在大量小查询(比如几十个Dashboard同时Refresh)下的表现,并不像大量分析查询下那么稳定,反而偶尔会出现查询排队。最后我的方案是Prometheus继续做热数据和告警,ClickHouse做深度的分析查询和跨数据源聚合,两者共存。
另一个坑是在Loki的配置上。早期我只开标签索引,没做缓存和分层,结果每次查历史日志都要从对象存储拉大量数据,Grafana上的查询慢得让人抓狂。后来加了Redis缓存、设置了不同保留期的分层存储规则,并把部分高频查询的日志指标做了预聚合,体验才算回归正常。
这些坑并不代表哪个方案不行,更多是提醒自己:选型不能只看Demo效果或者某个维度的优势,一定要结合数据量、查询模式、团队运维能力综合判断。可观测性存储没有银弹,只要还有不同类型的数据存在,就会有不同类型的存储系统各司其职。
回到标题里的问题:时序数据库在可观测领域的真正对手是谁?我的答案是,它不是一个名字,而是一股趋势——当数据形态和分析范式都在变,存储选型就必须跟着变。时序数据库仍然会长期存在于监控体系里,但它会从一个“默认答案”变成“众多答案之一”。对做技术决策的我们来说,最重要的不是站队某一类存储,而是看透数据背后的真实需求。