成本从哪里来
一条 trace 的成本大致分三段:
| 开销项 | 占比(典型) | 主要驱动 |
|---|---|---|
| ClickHouse 存储 | 40-55% | trace/observation/score 行数,input/output 冗余字段 |
| S3 对象存储 | 15-25% | 大 prompt 原文、多模态 blob |
| 计算(Web/Worker) | 15-25% | 写入吞吐、UI 查询 QPS |
| LLM-as-Judge | 可高可低 | 采样率 × 裁判模型单价 |
ClickHouse 是大头,优化投入产出比最高。
ClickHouse 表结构速读
Langfuse 主要写三张表:
默认分区键是 toYYYYMM(start_time),ORDER BY 基本上是 (project_id, toDate(start_time), id)。这意味着:
- 按时间范围 + project_id 过滤的查询极快(走主键索引)
- 不带时间条件的全表扫极慢(典型"UI 卡死")
- 按月分区,删除老数据 = DROP PARTITION,秒级
① TTL:老数据自动消失
最简单、最直接的降本手段。Langfuse 支持在项目级设 retention,Worker 会发对应 ALTER 改 TTL。如果想在 ClickHouse 层面再加一道保险:
ALTER TABLE observations MODIFY TTL start_time + INTERVAL 90 DAY DELETE; ALTER TABLE traces MODIFY TTL start_time + INTERVAL 180 DAY DELETE; ALTER TABLE scores MODIFY TTL timestamp + INTERVAL 180 DAY DELETE;
典型留存策略:
- 详细 observation:90 天(够查近期 bug)
- trace + score:180 天(看季度趋势)
- 聚合指标:永久(做成 MV / Projection 另存,下面讲)
TTL 只在 part merge 时触发。改完一两天才真正瘦身属于正常。想立刻清:
ALTER TABLE ... MATERIALIZE TTL IN PARTITION '202511',或者直接 DROP PARTITION。
② 分区裁剪:查询永远带时间
Langfuse UI 默认就带时间过滤,自己写 SQL 查 ClickHouse 时容易忘。一个反例:
-- 慢: 扫全表 SELECT count() FROM observations WHERE user_id = 'u_42'; -- 快: 分区裁剪 + 主键前缀 SELECT count() FROM observations WHERE project_id = 'proj_x' AND start_time >= now() - INTERVAL 7 DAY AND user_id = 'u_42';
任何写分析脚本的人都要背这个模板,否则一个慢查询能把 ClickHouse CPU 打满 10 分钟,UI 全线变红。
③ Projection:把聚合"预算"下来
UI Dashboard 常问"按天 token 消耗"、"按模型 P95 延迟"。这种 GROUP BY 查询每次扫全表很浪费。ClickHouse 的 Projection 让它自动 materialize:
ALTER TABLE observations ADD PROJECTION proj_daily_token
(
SELECT
project_id,
toDate(start_time) AS day,
model,
sum(prompt_tokens) AS prompt_tokens,
sum(completion_tokens) AS completion_tokens,
count() AS call_count
GROUP BY project_id, day, model
);
ALTER TABLE observations MATERIALIZE PROJECTION proj_daily_token;
之后任何符合这个聚合形状的查询,ClickHouse 自动走 Projection——毫秒级。主表改行 Projection 会同步更新,写入侧有轻微代价(5-10%),但 Dashboard 性能是量级提升。
④ 冷热分层:热盘 SSD + 冷盘 S3
近 30 天的 trace 90% 被查,老数据 99% 只是躺着。ClickHouse 支持多卷存储策略,把老 part 自动移到 S3:
<!-- clickhouse config.xml -->
<storage_configuration>
<disks>
<hot>
<path>/var/lib/clickhouse/</path>
</hot>
<s3_cold>
<type>s3</type>
<endpoint>https://s3.amazonaws.com/langfuse-ch-cold/</endpoint>
<access_key_id>AKIA...</access_key_id>
<secret_access_key>...</secret_access_key>
</s3_cold>
</disks>
<policies>
<tiered>
<volumes>
<hot><disk>hot</disk></hot>
<cold><disk>s3_cold</disk></cold>
</volumes>
</tiered>
</policies>
</storage_configuration>
ALTER TABLE observations MODIFY SETTING storage_policy = 'tiered'; -- 30 天前的 part 自动下沉到 S3 ALTER TABLE observations MODIFY TTL start_time + INTERVAL 30 DAY TO VOLUME 'cold', start_time + INTERVAL 90 DAY DELETE;
效果:热盘留 30 天,冷盘存 30-90 天,90 天自动删。热盘 SSD 成本是 S3 的 20 倍以上,这一层能把存储账单砍 60%+。
⑤ 写入侧:批量 + 压缩
Langfuse Worker 已经做了批量,但有几个参数值得调:
langfuse:
worker:
env:
- name: CLICKHOUSE_WRITE_BATCH_SIZE
value: "2000" # 默认 500, 大批写更省
- name: CLICKHOUSE_WRITE_FLUSH_INTERVAL_MS
value: "2000" # 默认 500ms, 拉到 2s
ClickHouse 表默认 ZSTD 压缩就很好,不用动。如果存储还紧,把 input_ref / output_ref 的 CODEC(ZSTD(3)) 调到 ZSTD(9)——CPU 多花一点,存储省一成。
⑥ 采样:不是所有 trace 都要存
真正的降本核弹是"不收"。几档采样策略按需选:
# SDK 层简单采样例子 import random, hashlib def should_sample(user_id: str, rate: float) -> bool: # VIP 100%, 其他按 rate if is_vip(user_id): return True h = int(hashlib.md5(user_id.encode()).hexdigest(), 16) return (h % 10000) / 10000 < rate if should_sample(user_id, 0.2): trace = lf.trace(...) else: trace = None # 完全跳过 Langfuse
⑦ 监控三要三不要
日常看这几个指标就够:
- 要看:ClickHouse merge 队列长度(堆积即磁盘 IO 瓶颈)
- 要看:Redis
ingestion队列长度(堆积即 Worker 跟不上) - 要看:S3 bucket 大小日增量(blob 膨胀最容易悄悄炸钱)
- 不要看:trace 总数(没意义,看增量)
- 不要看:ClickHouse CPU 瞬时(merge 期本来就抖)
- 不要看:Postgres 大小(几乎不增长)
容量规划公式
粗估一下,以帮你要预算:
每日 trace 数 T 每 trace 平均 observation 数 O (典型 3-8) 每 observation 平均 body 大小 B (典型 2-10 KB, 走 S3) ClickHouse 增量 ≈ T * O * 200 bytes (已经排除 body, 只算结构化字段 + 索引) S3 增量 ≈ T * O * B * 0.4 (ZSTD 压缩 ≈ 0.4) 按 90 天留存 + 3 副本: ClickHouse 磁盘 ≈ 日增量 * 90 * 3 S3 花费 ≈ 日增量 * 90(无副本, S3 自己冗余)
代入 T=10M, O=5, B=5KB:
- ClickHouse 每天 ~10 GB,留 90 天 3 副本 ≈ 2.7 TB 热盘
- S3 每天 ~100 GB,90 天 9 TB;按 AWS S3 Standard 大约 $200/月
- ClickHouse 热盘 SSD 2.7 TB,按 gp3 大约 $220/月
- 如果启用冷分层,30 天后 S3 化,热盘降到 900 GB ≈ $72/月
也就是说,1000 万 trace/天的存储开销在 $300-500 区间,相比同量级的 LangSmith SaaS 每月几千美元便宜一个量级。这就是自建的经济理由。
常见慢查询排查
-- 最近 1 小时内跑得最慢的查询 SELECT query_duration_ms, substring(query, 1, 200) AS q, read_rows, memory_usage FROM system.query_log WHERE event_time > now() - INTERVAL 1 HOUR AND type = 'QueryFinish' ORDER BY query_duration_ms DESC LIMIT 20;
典型病因:
- 查询没带 project_id / 没带时间范围 → 分区全扫
- 大 IN 列表(上千个 trace_id) → 让调用方按时间切片
- JSON metadata 字段做过滤 → 考虑加
MATERIALIZED虚拟列或 skip index - 频繁聚合相同维度 → 加 Projection
本章小结
- ClickHouse 存储占大头,TTL + 冷热分层就能砍 60% 账单
- 查询永远带 project_id + 时间范围,走分区裁剪
- Dashboard 用 Projection 预聚合,查询从秒级变毫秒级
- 采样按价值分层:VIP / 错误 100%,其他按 10-30% 头采
- 监控三件:CH merge 队列、Redis ingestion 队列、S3 bucket 日增
- 容量预估公式算清楚再买实例,10M trace/日大约 $300-500/月就能撑住