← 返回文章列表
产品动态 4 分钟阅读 炬鲸团队

日志检索为什么快:冷热分层、倒排索引与字段提取

拆解炬鲸 OBSERVE 日志检索的三大核心设计:冷热分层存储、倒排索引和字段提取,解释它们如何把大容量日志的查询耗时从分钟级压到秒级,并给出索引与字段配置的具体建议。

冷热分层:先把存储和查询成本压下来

日志数据量巨大,但绝大多数查询集中在最近几小时到几天。炬鲸 OBSERVE 默认把数据分成热、温、冷三层:热层落在 SSD 上,保存最近 24 小时,写入即建索引;温层用普通盘,保存最近 30 天;冷层走对象存储,可保留 180 天以上。查询时系统自动路由,命中热层的数据毫秒级返回,冷层数据按需解冻后返回。

分层不是简单的"按时间切目录"。系统会根据数据量和租户配置动态调整边界。举个例子:某业务高峰期 1 小时产生 500GB 日志,热层会自动扩展到 48 小时,避免高峰期频繁查询命中冷层、拖成慢查询。反过来,冷数据长期无人访问时,也会自动降级到更便宜的归档层,降低对象存储费用。

分层还有一个隐藏收益:合规留存。很多行业要求日志保留 180 天甚至更久,如果全部放 SSD,成本无法接受;把 90% 以上的历史数据压到对象存储,冷层查询虽然慢几秒,但取证和审计场景完全够用。

倒排索引:全文检索的地基

全文检索依赖倒排索引。写入时把日志正文按分词器切词,建立"词 → 文档位置"的映射,查询时直接按词命中,而不是逐条扫描。默认分词器支持中英文混合分词,也允许自定义分隔符和正则规则。

这里有个高频踩坑点:traceId、requestId、userId 这类字段不需要分词,应该配置成 keyword 类型。否则 requestId:abc-123-xyz 会被切碎,检索时可能漏报。另一个坑是把整段 JSON 日志当纯文本处理,查 status:500 时因为 status 被分词而搜不到——正确做法是在采集端把 JSON 字段打平,让 status 成为独立字段。

索引不是越多越好。倒排索引会占用额外的磁盘和内存,写入时还要更新索引结构。一个 100 字段的日志如果全建全文索引,写入吞吐会明显下降。所以默认只对日志正文建全文索引,其余字段按需开启。

字段提取:从"搜全文"到"按列查"

全文检索快,结构化查询更快。OBSERVE 支持在采集管道里用 grok、正则或 JSON 解析自动提取字段,比如从 Nginx 日志抽出 status、upstream_response_time、request_uri。提取后的字段可以建立列式索引,查询 upstream_response_time > 3 时直接走列扫描,不需要遍历所有文档,耗时能差一个数量级。

举一个 grok 的例子,一行 Nginx 访问日志:

192.168.1.10 - - [06/Oct/2026:09:15:32 +0800] "GET /api/order?id=123 HTTP/1.1" 200 512 0.042

对应的 grok 模式:

%{IP:client_ip} - - \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{URIPATH:path}%{DATA:query} HTTP/%{NUMBER:version}" %{INT:status} %{INT:bytes} %{NUMBER:upstream_response_time}

提取后 status、upstream_response_time 都成了可排序、可聚合的独立字段,排查慢请求时直接 upstream_response_time > 1 就能筛出全部慢接口。

但不要把所有字段都建索引。索引越多,写入越慢,查询反而可能变慢。一般业务提取 10~20 个高频字段足够,其余交给全文检索兜底。建议每月 review 一次字段使用情况,把长期无人查询的字段下线。

实践建议

  1. 热层覆盖高峰查询窗口,冷层按月归档,数据量大的业务可设自动降级策略;
  2. traceId、requestId、userId 用 keyword,别用默认分词;
  3. 高频过滤字段建列式索引,低频字段保留全文检索即可;
  4. 定期清理无用字段,避免索引膨胀拖累写入和查询性能。