先亮观点:日志分析 90% 的成本,在你写下第一行 log.info 的时候就定了
去年冬天帮一个朋友看账单。做 SaaS 的,日活不到 8 万,Datadog 一个月 1.7 万美金,其中 log ingest 占了 9000 多。我让他们把 7 天的日志量导出来看看:每天原始文本 620GB,采集端压缩后 110GB,而真正的索引查询命中率——就是那些日志到底被查过没有——不到 0.6%。
这个数字不是个例。我前后看过七八家公司的日志链路,命中率超过 5% 的一个都没有。我们花 100% 的钱,存了 99.4% 永远不会被看第二眼的文本。
所以这里有个可能有点反直觉的话:日志分析的成本和性能,九成在你写日志和收日志的那一刻就决定了,跟后面选 ELK 还是 Loki 关系没那么大。大部分团队做优化的顺序是反的——先去压测引擎的 QPS、比谁家倒排索引快,最后才想起来日志本身从来没做过分级和采样。
先别问选哪个引擎,先问你这三个问题
我一般会先反过来问对方三句话:
- 你要的是「精确定位某一次请求的完整上下文」,还是「统计某类错误每小时出现多少次」?
- 查询的时间范围是 15 分钟内,还是 90 天前?
- 出问题的时候,是人在终端里翻,还是告警系统自动在查?
这三个问题基本能劈开方案。说人话就是:
15 分钟内 + 人工查 → Loki 完全够用,日志量再小一点,journalctl 加 grep 都能扛一阵。 90 天范围 + 聚合统计 → 别用日志做,那是 ClickHouse 或者 Prometheus 的活。 跨服务追一个请求 → 你得先有 trace_id 并且它被打印在每条日志里,否则存哪都是废纸。
我见过最典型的浪费,是一个团队用 ES 存了两年的 access log,就为了算每天的 PV 和 5xx 比例。这俩数用指标做,一年的数据也就几十 MB。
三家横向比:参数和实测量都摊开
下面这张表是我自己攒的,数字来自几个真实环境,不是官网抄的。前提:日增原始文本 1TB,保留 30 天,机器是云上常规机型。
| 维度 | Elasticsearch 8.x | Loki 3.x | ClickHouse 24.x |
|---|---|---|---|
| 索引方式 | 倒排索引,全文检索强 | 只索引 label,正文只压缩不索引 | 列存 + 稀疏索引,标量聚合极强 |
| 压缩比(对原始文本) | 1.5:1 ~ 3:1 | 8:1 ~ 15:1 | 10:1 ~ 20:1(ZSTD 默认级别) |
| 1TB/天落盘占用 | 约 350~600GB | 约 70~120GB | 约 60~100GB |
| 全文搜索 | 强 | 弱(靠 bloom filter 碰运气) | 有,但要建 tokenbf_v1 索引,代价大 |
| 高基数耐受 | 中,mapping explosion 是真会炸 | 差,label 组合数上千就开始抖 | 强,宽表列基本不怕 |
| 最疼的地方 | JVM 堆、refresh、段合并 | 没法真全文检索 | 没有原生采集管道,得自己写 |
补几个具体参数,都是我踩过或者帮人调过的:
Elasticsearch:index.refresh_interval 默认 1s,改成 30s,写入吞吐一般能涨 20% 到 40%,代价是最新数据有 30 秒延迟,对日志场景完全可接受。单个 shard 官方建议 10~50GB,我实际会控在 20~40GB,超过 50GB 的 shard 恢复一次能等到怀疑人生。bulk 请求 5~15MB 比较稳。
Loki:默认 max_query_series 是 5000,超了直接报 the query hit the max number of series limit。chunk_target_size 默认 1.5MiB,max_chunk_age 默认 1h,max_entries_limit_per_query 默认 5000 条。这几个值不改,出问题时你就会看到查询莫名其妙少数据。还有它的 label,千万别把 request_id、user_id 这种塞进去,label 组合数是它唯一的死穴。
ClickHouse:index_granularity 默认 8192,日志表我一般建 ORDER BY (service, level, toStartOfHour(ts)),分区按天 PARTITION BY toDate(ts),超过 90 天 TTL ts + INTERVAL 90 DAY TO VOLUME 'cold'。压缩用 CODEC(ZSTD(3)) 就够,调到 9 收益递减得很厉害但 CPU 涨得明显。
我认为真正该做的,是把优化顺序倒过来
说个我自己总结的、可能不太主流的三步:
第一步,先删日志,不加机器。 生产环境的 DEBUG 级别日志直接不发,不是在采集端 drop——在采集端丢等于你已经付了 CPU 和带宽的钱。这一个动作,我在三个团队里做的效果分别是砍掉 62%、71%、58% 的量。改一行日志框架配置的事。
第二步,采样,但只采非关键的。 用 OpenTelemetry Collector 的 tail_sampling,或者 Vector 的 remap,规则很简单:status_code >= 500 或 level=ERROR 保留 100%,WARN 保留 20%,INFO 用 probabilistic 采 1%~5%,DEBUG 0%。错误日志你永远不该采样,因为出事的时候你就是在找那一两条。
第三步,才轮到分层和换引擎。 热数据 7 天放 SSD,温数据 30 天放 HDD,90 天以上的丢对象存储,ES 用 ILM 自动滚,Loki 靠 retention_period 配合 S3,ClickHouse 走 TTL TO VOLUME。
顺序反过来做,你会在第一步就把成本砍掉一半以上,后面根本不用换引擎。
顺手提一个容易被忽略的迁移坑
采集端还有个时间炸弹:Promtail 已经在 2025 年 3 月 2 日 EOL 了,Grafana 官方推的是 Alloy。很多团队的 Loki 集群还在跑 Promtail,短期没事,但新特性和安全补丁就没有了。迁移不算难,配置要重写一遍,工作量大概 2 到 4 个人天。
我去年迁过一个,14 个业务线、300 多台机器,坑主要在两处:一是原来 Promtail 用 pipeline_stages 做的解析,得改成 loki.process 里的 stage 语法;二是 static_configs 的通配符路径在 Alloy 里行为有细微差别,有几台机器的日志静默丢了三天才发现。所以迁完一定要对比新旧两边的 sum(rate({job=...}[5m])),差超过 5% 就得查。
最后说句可能不讨喜的
日志不是数据资产,是安全气囊。 它 99% 的时间不产生任何价值,但出事故的那 1% 你得靠它活命。所以优化目标从来不是「存得更多」,而是「在保住关键证据的前提下,用最低成本让那 1% 查得到」。
按这个标准,很多团队其实只需要:错误日志 100% 保留 90 天,INFO 采样 5% 保留 7 天,DEBUG 一条不留。这么配下来,日 1TB 的原始量能压到 80GB 左右,一台 8C16G 加 2TB 盘的机器就能扛。
至于选 ELK 还是 Loki 还是 ClickHouse——等你把上面这几步做完,再回头看这个问题,你会发现它已经不重要了。