介绍 OBSERVE 统一查询引擎如何用类 SQL 语法同时检索日志、指标与链路,给出常用查询语句、全文检索陷阱与性能优化建议,帮助运维快速定位线上问题。
运维排查日志最常见的动作是 grep -E "pattern" app.log,接着 tail -f、awk '{print $NF}'、sort | uniq -c。这套命令在单机、单文件时代够用,一旦日志集中到平台、分散在几十个索引和几百个服务里,grep 就开始吃力:跨服务关联难、聚合统计慢、字段过滤得靠正则硬扛。更麻烦的是,很多团队指标放在 Prometheus、日志放在 Elasticsearch、链路放在 Jaeger,三套系统的查询语言互不相通,排查一个跨服务问题要在三个界面之间反复切换。
OBSERVE 的统一查询引擎把日志、指标、链路三者的查询接口收敛成一套类 SQL 语法。你不用记三种 DSL,一个 SELECT ... FROM logs WHERE ... 就能同时完成过滤、聚合、排序,结果还可以直接下钻到原始日志。语法基于标准 SQL 扩展了时间窗口函数,学过 SQL 的人十分钟就能上手,团队里不需要专门的查询语言专家。
查最近 5 分钟返回 502 最多的上游服务:
SELECT upstream, COUNT(*) AS cnt
FROM logs
WHERE status = 502 AND time > now() - 5m
GROUP BY upstream
ORDER BY cnt DESC
LIMIT 10
按分钟统计 QPS 与 P99 延迟:
SELECT time_bucket(1m, ts) AS bucket,
COUNT(*) AS qps,
quantile(0.99, latency_ms) AS p99
FROM logs
WHERE service = 'order-api'
GROUP BY bucket
ORDER BY bucket
统计各服务 15 分钟内的错误率,找出最先出问题的那个:
SELECT service,
SUM(CASE WHEN status >= 500 THEN 1 ELSE 0 END) / COUNT(*) AS err_rate
FROM logs
WHERE time > now() - 15m
GROUP BY service
HAVING err_rate > 0.01
ORDER BY err_rate DESC
用窗口函数看每个服务的慢请求随时间的变化趋势:
SELECT service, time_bucket(1m, ts) AS bucket,
RANK() OVER (PARTITION BY service ORDER BY COUNT(*) DESC) AS rk
FROM logs
WHERE latency_ms > 500 AND time > now() - 1h
GROUP BY service, bucket
日志全文检索有个坑:WHERE message LIKE '%timeout%' 会退化成全表扫描。改用 WHERE message MATCH 'timeout' 走倒排索引,百万级日志下能快一到两个数量级。数字字段也别包引号,WHERE latency_ms > 500 走数值索引,WHERE latency_ms > '500' 反而退化成字符串比较。
链路数据的 trace_id 是日志与指标的关联键。排查流程变成:先按 trace_id 查出完整调用链,找到耗时最长的那个 Span,再 WHERE trace_id = 'xxx' 拉出该请求跨服务产生的全部日志,最后叠加对应时间段的指标曲线,看 CPU、GC、连接池有没有同步抖动。三个动作在同一套 SQL 里完成,不用在三个页面间来回切换。这是统一查询最大的价值:把「先猜是哪一类数据的问题」这一步省掉,直接在同一份语句里把三类数据串起来看。
统一查询不是简单地在三个引擎上面套一层语法糖。底层日志走列式存储 + 倒排索引,指标走预聚合 + 降采样,链路走图索引;同一份 SQL 会被自动拆解、路由到对应的存储,再合并结果。这样 SELECT COUNT(*) FROM logs WHERE status=502 不用把整页日志都读出来计数,而是直接读索引统计。这也是为什么它能扛住大索引下的交互式查询,而不是像 grep 一样线性扫描。
WHERE 尽量落在索引列上,避免全扫描。执行前看查询计划,出现 full scan 提示就先改写。GROUP BY 时先加时间窗口或 LIMIT,防止聚合内存暴涨;引擎对超限聚合会自动截断并提示。time_bucket 就不要用 GROUP BY ts,后者会产生海量小分组,速度慢很多。一次用户反馈「下单偶尔卡 3 秒」。先查错误率没异常,接着按 service 分组的 P99 里发现 order-api 在几个时间点冲到 3s;再用 WHERE service='order-api' AND latency_ms > 2000 抽出慢请求的 trace_id;最后 WHERE trace_id IN (...) 拉出完整调用链,定位到支付回调里有个同步查账的 SQL 走了全表。整个过程没有切过页面,也没有换过查询语言,从看到告警到定位根因大约十分钟。
最后提醒一句:把排查时反复用到的查询存成收藏或固定到大盘,下次同类问题直接点开,不用现写 SQL。团队里沉淀的标准排查查询越多,新人接手故障时上手越快,这也是统一查询平台用得越久越顺手的原因。