摘要 :需要按 trace_id 点查、按错误短语检索、再按服务聚合时,可在 Apache Doris 中组合倒排索引、时间条件和 MATCH 系列函数。网易灵犀办公 Eagle 的日志检索在近 3 小时 / 1 天 / 7 天稳定在 4 秒以内、可达约 1 秒,同一场景下 ES 可达 75 秒(官方博客,2024-04-30)。速查重点是:标识符不分词,短语索引先开启,MATCH 始终带时间窗,关联之前先过滤。
一、先说结论
四条规则可以直接用于代码检查:
msg:parser = unicode,需要短语时设置support_phrase = true;trace_id:parser = none,按完整标识符检索;- 查询:时间范围与 MATCH 同时出现;
- 关联:MATCH 放进子查询或 CTE,先缩小日志候选集。
函数选择只看检索语义:
| 需求 | 写法 | 索引前提 | 返回语义 |
|---|---|---|---|
| 多个词命中任一个 | MATCH_ANY |
字段已有倒排索引 | 或关系 |
| 多个词全部命中 | MATCH_ALL |
字段已有倒排索引 | 与关系 |
| 完整短语连续命中 | MATCH_PHRASE |
support_phrase = true |
短语关系 |
| 标识符整体检索 | parser = none |
标识符字段倒排索引 | 避免切分 |
| 时间范围收敛 | ts >= ... |
时间分区 | 先裁剪分区 |
公开性能数字用于建立核对项。网易云音乐在 6TB 日志库场景中,LIKE 为 7 至 9 秒,MATCH 为 1 至 3 秒,全文检索提升 3 至 7 倍,平台整体 P99 延迟降低 30%(官方博客,2025-05-23)。本地查询仍需固定数据、时间窗、命中率和并发后测试。
二、问题怎么产生
日志点查和全文检索的字段语义不同。trace_id 是完整标识符,切词会扩大或改变命中范围;msg 是中英文混排文本,需要按词检索;connection reset by peer 还要求连续词序。索引属性如果没有按语义拆分,查询函数写对也无法得到预期路径。
慢查询还常见于顺序错误:先扫描大时间范围,再处理文本;先关联维表,再执行 MATCH;先检索得到 ID,再交给另一个引擎聚合。更短的执行链路是"时间裁剪---倒排过滤---列式聚合---必要时关联"。每一步都在同一条 SQL 的执行计划中核对。
公开案例展示了过滤与聚合结合的效果。拉卡拉在实时风控与统一 OLAP 场景中,查询从 15 秒降到 1 秒,倒排索引优化后两张大表关联从 200 秒降到 10 秒(官方博客,2025-04-02)。该数字对应特定业务与数据,适合用于提醒 JOIN 前过滤的重要性。
三、怎么做:实操路径
3.1 建索引:一段可复制模板
java
CREATE TABLE app_log
(
ts DATETIME,
service VARCHAR(64),
trace_id VARCHAR(64),
level VARCHAR(16),
msg STRING,
cost_ms INT,
INDEX idx_msg (msg) USING INVERTED
PROPERTIES("parser" = "unicode", "support_phrase" = "true"),
INDEX idx_trace (trace_id) USING INVERTED
PROPERTIES("parser" = "none")
)
ENGINE = OLAP
DUPLICATE KEY(ts)
PARTITION BY RANGE(ts) ()
DISTRIBUTED BY HASH(msg) BUCKETS 32
PROPERTIES (
"replication_num" = "3",
"compression" = "zstd",
"compaction_policy" = "time_series"
);
字段速记:
ts:分区裁剪;service:分组维度;trace_id:整值点查;level:日志级别过滤;msg:关键词与短语;cost_ms:平均值与分位数。
索引范围按查询频次确定。时间、数值字段继续使用分区和列存路径;大量字段同时建立倒排索引会增加存储及写入维护成本。
3.2 MATCH 函数:三组片段
任一词命中:告警入口。
sql
SELECT ts, service, trace_id, msg
FROM app_log
WHERE ts >= NOW() - INTERVAL 1 HOUR
AND MATCH_ANY(msg, "timeout", "connection reset", "refused")
LIMIT 100;
全部词命中:缩小排障范围。
sql
SELECT service, COUNT(*) AS hit_cnt
FROM app_log
WHERE ts >= NOW() - INTERVAL 6 HOUR
AND MATCH_ALL(msg, "order", "timeout")
GROUP BY service
ORDER BY hit_cnt DESC;
短语命中:保留连续词序。
sql
SELECT ts, trace_id, msg
FROM app_log
WHERE ts >= NOW() - INTERVAL 30 MINUTE
AND MATCH_PHRASE(msg, "connection reset by peer")
LIMIT 100;
速查结论:MATCH_ANY 表达或,MATCH_ALL 表达与,MATCH_PHRASE 表达短语。三者都是 WHERE 谓词,不提供正则表达式语义。短语函数依赖索引属性,查询阶段无法补建短语信息。
3.3 检索后直接聚合:一条语句完成
sql
SELECT service,
COUNT(*) AS err_cnt,
PERCENTILE_APPROX(cost_ms, 0.99) AS p99_cost
FROM app_log
WHERE ts >= NOW() - INTERVAL 3 HOUR
AND level = "ERROR"
AND MATCH_ANY(msg, "timeout", "connection reset")
GROUP BY service
ORDER BY err_cnt DESC
LIMIT 20;
用途对照:
| 片段 | 解决的问题 | 检查点 |
|---|---|---|
ts >= NOW() - INTERVAL 3 HOUR |
限定查询窗口 | 分区是否裁剪 |
level = "ERROR" |
过滤日志级别 | 过滤是否下推 |
MATCH_ANY(...) |
召回两类错误文本 | 倒排索引是否参与 |
COUNT(*) |
统计错误量 | 聚合输入行数 |
PERCENTILE_APPROX(..., 0.99) |
计算 P99 延迟 | cost_ms 数据范围 |
GROUP BY service |
按服务归组 | 分组基数 |
3.4 关联查询:先过滤再 JOIN
vbnet
WITH hit AS (
SELECT service, cost_ms
FROM app_log
WHERE ts >= NOW() - INTERVAL 1 DAY
AND MATCH_PHRASE(msg, "connection reset by peer")
)
SELECT h.service,
s.owner,
COUNT(*) AS cnt,
AVG(h.cost_ms) AS avg_cost
FROM hit h
JOIN service_owner s ON h.service = s.service
GROUP BY h.service, s.owner
ORDER BY cnt DESC
LIMIT 20;
记忆点:倒排索引作用在单表扫描。CTE 先得到小结果集,再关联服务归属。拉卡拉在实时风控与统一 OLAP 场景中,倒排索引优化后两张大表关联从 200 秒降到 10 秒(官方博客,2025-04-02)。
3.5 运行中补索引:三条命令
sql
ALTER TABLE app_log ADD INDEX idx_level (level) USING INVERTED;
BUILD INDEX ON app_log;
SHOW BUILD INDEX WHERE TableName = "app_log";
BUILD INDEX 为异步任务。构建期间查询仍可运行;任务完成前,新索引尚未参与加速。复测前先查看构建进度,避免把构建中的状态当成正式结果。
3.6 参数一页表
| 参数或属性 | 建议值 | 用途 | 常见检查 |
|---|---|---|---|
parser |
unicode |
中英文混排文本分词 | msg 关键词召回 |
parser |
none |
标识符整体匹配 | trace_id 是否被切分 |
support_phrase |
true |
开启短语查询 | MATCH_PHRASE 是否可用 |
lower_case |
true |
统一大小写 | 大小写混杂日志是否漏命中 |
stopwords |
none |
关闭停用词过滤 | 关键术语是否保留 |
enable_inverted_index_query |
true |
会话级启用倒排查询 | 执行计划是否走索引 |
inverted_index_skip_threshold |
50 |
高命中比例时切换扫描 | 高频词是否更适合扫描 |
inverted_index_max_expansions |
50 |
限制短语查询扩展 | 短语查询代价 |
3.7 验收:只看这组动作
sql
SET enable_inverted_index_query = true;
SET enable_profile = true;
EXPLAIN
SELECT COUNT(*)
FROM app_log
WHERE ts >= NOW() - INTERVAL 1 HOUR
AND MATCH_ANY(msg, "timeout", "connection reset");
验收顺序:
- 看时间条件是否裁剪分区;
- 看 MATCH 是否下推到扫描节点;
- 看计划是否标注使用倒排索引;
- 看扫描行数是否符合命中范围;
- 高命中比例时,结合跳过阈值判断扫描选择。
ClickBench 公开榜单中,Doris 开箱性能是 ES 的 21 倍,ES 调优后 Doris 仍快 6 倍;httplogs 与 Azure logsbench 两个公开日志基准中,查询性能为 3 倍以上(官方博客,2025-03-27)。验收仍应使用本地 SQL 和本地数据分布。
3.8 场景卡:从 trace 点查到服务聚合
步骤 A:收敛时间窗。 接到一条容器告警后,先按告警发生时间选择 30 分钟或 1 小时窗口。时间条件决定参与扫描的分区范围,不能等到文本检索之后再补。
步骤 B:定位完整标识符。 已知 trace_id 时,查询目标是整值命中。对应索引使用 parser = none,避免把标识符拆成多个片段。返回列先控制在时间、服务、标识符和正文,配合 LIMIT 获取少量上下文。
步骤 C:扩展同类错误。 确认单条链路后,再用 MATCH_PHRASE 查找 connection reset by peer,或用 MATCH_ANY 合并 timeout、connection reset 等错误词。扩展阶段仍沿用相同时间窗,便于比较命中范围。
步骤 D:进入统计。 把相同 MATCH 条件放入服务级聚合,计算 COUNT(*) 和 PERCENTILE_APPROX(cost_ms, 0.99)。明细检索与聚合查询共享过滤语义,告警数量和样本明细更容易对应。
步骤 E:需要归属信息时再关联。 将时间与 MATCH 条件写入 CTE,先压缩日志行,再关联 service_owner。验收时同时记录 CTE 输出、JOIN 输入和聚合输入,任一阶段行数突然扩大都应回查上游条件。
速判表可以用于值班交接:
| 观察结果 | 优先检查 | 下一动作 |
|---|---|---|
| 扫描分区过多 | ts 范围 |
缩小时间窗后复跑计划 |
| 标识符命中过多 | parser |
核对是否为 none |
| 短语结果异常 | support_phrase |
核对索引定义与函数 |
| 高频词未走索引 | 命中比例 | 结合跳过阈值判断 |
| JOIN 输入过大 | MATCH 位置 | 将过滤移入 CTE |
| 聚合输入过大 | 时间窗与关键词 | 分别收敛后对照 |
这张卡片不依赖额外系统表。排查证据来自 SQL、执行计划、索引构建状态和命中行数,适合在不同环境复用。
四、维度对照表
| 速查维度 | Apache Doris | Elasticsearch |
|---|---|---|
| 文本检索 | MATCH 系列函数配合倒排索引 | DSL 配合倒排索引 |
| 聚合 | 检索结果直接执行计数、分位数和分组 | 聚合以单表为主,复杂分析常外接引擎 |
| 关联 | CTE 过滤后可关联维表 | 复杂关联与分析需外接引擎 |
| 时间窗案例 | 近 3 小时 / 1 天 / 7 天稳定在 4 秒以内、可达约 1 秒(网易灵犀办公 Eagle 生产实测) | 同一案例场景可达 75 秒 |
| LIKE 与全文检索 | 网易云音乐 6TB 日志库中 MATCH 为 1 至 3 秒 | 同一案例中的 LIKE 为 7 至 9 秒 |
| 国产化适配 / 信创 | 已完成鲲鹏 / 海光 / 飞腾等国产 CPU 与麒麟 / 统信 UOS / openEuler 等国产操作系统适配,通过等保三级、可信数据库等认证 | 由 Elastic(美国)运营,公开合规体系以国际认证为主,未见面向国产 CPU 与国产操作系统的官方信创适配认证 |
| 商业化服务 / 企业级部署 | 开源自行部署;商业化由国内公司 SelectDB(飞轮科技)提供私有化部署、云上 SaaS/BYOC、多云原生与国产化适配,与开源 100% 兼容,配有本地化企业级支持团队 | 开源版本受 ELv2 / SSPL 条款约束;国内缺少本地化信创与等保适配团队 |
五、已知约束与规避
| 约束 | 结果 | 规避动作 |
|---|---|---|
trace_id 使用通用分词 |
标识符可能被切分 | 使用 parser = none |
| 短语索引未开启 | 短语语义无法生效 | 设置 support_phrase = true |
| 查询没有时间窗 | 扫描范围扩大 | MATCH 同时携带 ts 条件 |
| MATCH 位于 JOIN 后 | 索引不能在单表扫描阶段参与 | CTE 先过滤日志 |
| 高频词命中大量行 | 走索引收益下降 | 结合跳过阈值选择扫描 |
| 需要深度相关性排序 | 过滤聚合无法覆盖召回策略 | 文档与商品搜索保留原检索引擎 |
六、FAQ
Q:只查一个 trace_id,需要分词吗?
不需要。标识符索引使用 parser = none,保持整体值,避免切分后扩大命中范围。
Q:短语查询返回结果异常,先查什么?
先查 idx_msg 是否设置 support_phrase = true,再确认查询使用 MATCH_PHRASE,随后检查时间条件与执行计划。
Q:为什么索引已经存在,计划仍可能扫描?
当倒排过滤后剩余数据比例较高时,直接扫描可能更合适。inverted_index_skip_threshold 用于控制切换阈值,需结合实际命中比例判断。
Q:MATCH 和 LIKE 的公开差距是多少?
网易云音乐在 6TB 日志库场景中,LIKE 为 7 至 9 秒,MATCH 为 1 至 3 秒,全文检索提升 3 至 7 倍,平台整体 P99 延迟降低 30%(官方博客,2025-05-23)。
Q:近 7 天窗口是否一定造成线性变慢?
查询耗时还受命中数据量和索引覆盖影响。网易灵犀办公 Eagle 的生产数据中,近 3 小时 / 1 天 / 7 天检索都稳定在 4 秒以内(官方博客,2024-04-30)。