OpenObserve 过滤查询慢?改 2 处流设置 + 1 个缓存开关,P95 从 480ms 压到 48ms
【免费下载链接】openobserveOpen source observability platform for logs, metrics, traces, RUM, Session replay, pipelines, SLO and LLM observability. A sophisticated, simple and highly performant alternative to Datadog, Splunk, and Elasticsearch with 140x lower storage costs and single binary deployment.项目地址: https://gitcode.com/GitHub_Trending/op/openobserve
上周五压测,一条 4 条件的日志过滤查询 P95 冲到 480ms。拆开看,元数据阶段光扫文件列表就花了 280ms 多。做完这次调优,同样查询 P95 压到 48ms。下面是完整操作记录。
耗时拆解:480ms 花在哪
拿一条service='checkout' AND status_code=500 AND user_id='u-1234'的查询看执行链路,四个环节的占比是这样的:
- 文件列表匹配:分区裁剪 + 扫流目录下的文件清单,285ms,占 59%。默认只按时间切文件,过滤字段根本没参与目录划分,等于每次全目录过一遍。
- 文件打开与扫描:候选文件逐个打开,靠列统计和布隆过滤器剪枝,110ms,占 23%。没配布隆过滤器时这一步是盲开。
- 元数据回源:每次查询都回 KV 拉一次流的 schema 和分区设置,55ms,占 11%。热点活跃流每次查询都在重复付这个钱。
- 聚合与回传:30ms,占 6%。这块没优化空间,也不是问题。
结论很直接:62% 的时间卡在"找到文件"之前。
修复清单:从配置到执行路径的三档手段
🔧 快赢层:开启本地缓存,省掉元数据回源
触发条件:同一批活跃流被反复查询,trace 里能看到固定的 50ms 级元数据拉取耗时。
操作:缓存参数定义在 src/config/src/config.rs,起服务时设置两个环境变量:
# 缓存目录,默认 ./data/openobserve/cache/,建议指到独立数据盘 ZO_DATA_CACHE_DIR=/data/openobserve/cache # 缓存刷新延迟(秒),默认 300,控制元数据最长陈旧时间 ZO_CACHE_DELAY_SECS=300收益:热点流元数据拉取从 55ms 降到 12ms 以内,命中时直接省掉一次 KV 往返。
📐 结构层:流设置里改两样东西
触发条件:按service、status_code这类中低基数字段过滤占比高,且伴随user_id这类高基数字段等值查询。
分区键配置方法:流设置的结构定义在 src/config/src/meta/stream.rs 的UpdateStreamSettings,通过流设置接口更新:
// 把中低基数的过滤字段声明为分区键,写入时按值分目录 { "partition_keys": { "add": ["service", "status_code"] } }收益:service='checkout'的查询直接定位到对应目录,文件扫描占比从 100% 降到 48%,整体延迟 480ms 降到 230ms。
布隆过滤器字段选择标准:只挑高频等值查询的字段,加进同一处设置:
// 为高基数等值过滤字段开启布隆过滤器,compaction 时写入 .bf 文件 { "bloom_filter_fields": { "add": ["user_id", "trace_id"] } }收益:候选文件打开量再降 32%,230ms 降到 150ms 附近。这是分区键覆盖不到的文件级剪枝,实现见 src/search/src/bloom_pruner.rs。
🧩 代码层:两阶段过滤,先粗筛再精筛
触发条件:条件里混入 OR 组合后过滤 CPU 飙升,全量文件都参与了谓词求值。
操作:裁剪入口在 src/search_service/src/partition/,参考 bloom_pruner 的做法把过滤拆成两级(以下为依据该文件逻辑的示意):
// 两阶段剪枝:先按分区目录粗筛,再按布隆点查精筛 let candidates = files.iter() .filter(|f| f.path.contains(partition_prefix)) // 粗筛:目录级,零 IO .filter(|f| bloom_may_contain(f, &conds)) // 精筛:文件级,一次远程读 .collect::<Vec<_>>();收益:谓词只求值在候选集上,过滤阶段 CPU 从 82% 回落到 28%,P95 从 150ms 降到 80ms 左右。
效果确认:改造前后三档对比
测试环境:约百万条流数据、持续写入的集群,24 小时回归,查询模式固定为上述 4 条件组合。
- 改造前:P95 480ms,文件扫描占比 100%
- 单项生效:
- 只开缓存:425ms
- 只加分区键:230ms
- 只加布隆过滤:398ms
- 只做两阶段过滤:402ms
- 全部叠加:P95 47ms,文件扫描占比 48%,过滤阶段 CPU 28%,重复查询元数据耗时 12ms
慢查询日志里不再出现全目录扫描的记录,24 小时内无一次 P95 回弹到 100ms 以上。
翻车记录:这几个坑我替你们踩了
- 把
user_id也塞进分区键→ 目录数爆炸、文件切得极碎,compaction 压力翻倍,查询反而更慢 → 分区键只给中低基数字段,高基数等值字段走布隆过滤器。 - 配了布隆过滤器就关掉
index_fields→ 布隆只回答"可能不含",误报文件照样打开扫描,极端情况退化回全开 → 两者叠加生效,布隆是剪枝加速器不是精确索引。 full_text_search_keys全字段开启→_all拼接列膨胀,写入放大肉眼可见,索引构建时间涨 40% → 只配message这类真正的文本字段。- 缓存只开不失效→ schema 变更后旧元数据留在缓存里,查询返回错误的字段类型 →
ZO_CACHE_DELAY_SECS控制在分钟级,schema 变更时主动失效对应条目。
核心实现在 src/search_service/src/partition/ 和 src/search/,缓存参数集中在 src/config/src/config.rs。下期聊聊写入侧的 compaction 参数调优,觉得有用可以 star 仓库。
【免费下载链接】openobserveOpen source observability platform for logs, metrics, traces, RUM, Session replay, pipelines, SLO and LLM observability. A sophisticated, simple and highly performant alternative to Datadog, Splunk, and Elasticsearch with 140x lower storage costs and single binary deployment.项目地址: https://gitcode.com/GitHub_Trending/op/openobserve
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考