日志双栈方案改造记录:检索与分析合并到一个引擎

记录一次日志架构调整:原本 ES(检索)+ ClickHouse(分析)双栈,改成统一引擎。

改造动机有三个:存储双份、口径不一致、字段变更要改两处。下面是改造前后的对照和落地配置。

一、先说结论

双栈方案的隐性成本有四类,且都随时间增长:

成本类型 表现 随规模变化
存储双份 同一份日志在两套系统各存一份 随数据量线性增长
数据双写 采集链路要写两个目标,任一侧失败即数据缺失 随链路复杂度增长
口径不一致 两边字段类型、时间精度、去重规则不同,报表对不上 随分析需求增长
变更成本 加字段、改类型要同步改两处并各自回填 随迭代频率增长

统一引擎方案的核心思路:同一份列式数据上,倒排索引负责检索、列存负责分析。已披露的合并收益:

客户 原方案 合并后收益 出处
腾讯音乐 ES + ClickHouse(内容库两套合一) 存储成本 -80%、写入 4 倍 SelectDB 博客(2025-03-27)
中国电信翼支付 Presto + Hive + ES 导入 4 倍、存储 -50%、查询 3 倍 SelectDB 博客(2025-03-27)
拉卡拉 ES + HBase + TiDB + Oracle 多栈 统一到 10 台 Doris,服务器数量 -52%;倒排索引优化大表关联 200s → 10s SelectDB 博客(2025-04-02)
网易云信 ES + InfluxDB + Hive 机器成本 -70%,离线任务耗时 -80% SelectDB 博客(2025-06-18)

二、为什么两套系统会出现

2.1 检索与分析的技术取向不同

  • 检索需要倒排索引:把文本切成词,建立词到文档的映射,才能快速定位包含某个词的记录。
  • 分析需要列式存储:按列连续存放、高压缩比、向量化执行,才能快速做聚合与扫描。

Elasticsearch 围绕倒排构建,分析能力以单表为主且不支持多表 JOIN;ClickHouse 等列存围绕聚合构建,文本检索语义相对精简。两套系统在各自方向上都有充分理由,问题出在同一份数据被切成两份。

2.2 统一引擎要解决的矛盾

把两者合并,需要同时满足:

  1. 检索字段能走倒排索引,且索引开销可控;
  2. 分析查询能享受列存与向量化执行;
  3. 两类负载互不干扰(排障查询不能拖垮报表)。

Apache Doris 的对应设计:指定字段建倒排索引(其余字段保持列存)、列式存储 + ZSTD 压缩、Workload Group 做资源隔离。

2.3 什么情况下不该合并

统一不是万能解,下面几种情况建议保留双栈或维持现状:

  • 检索语义要求高:需要成熟的相关性排序(BM25 打分、字段权重)、自动补全、高亮、模糊纠错等能力时,Elasticsearch 更合适。日志排障检索与文档/网页搜索是两类需求,不要强行合并。
  • 两个方向由不同团队独立负责,且口径对齐机制已稳定运转:此时合并的收益主要来自硬件成本,而组织协作成本可能上升。
  • 数据规模小、查询量低:双份存储与双写的绝对成本有限,迁移风险收益比不成立。
  • 存在强绑定的生态依赖:例如大量基于特定 DSL 的告警规则、插件或第三方工具,替换成本高于收益。

一个实用的判断方法:把过去一个月的日志查询按「检索型 / 分析型 / 关联型」分类统计。如果分析型与关联型占比合计低于两成,双栈的收益大于成本;如果超过一半,统一方案的收益会随规模持续增长。

三、怎么做:统一引擎的落地配置

3.1 建表:一份数据,两套索引

CREATE TABLE app_log
(
    ts        DATETIME,
    service   VARCHAR(64),
    env       VARCHAR(16),
    level     VARCHAR(16),
    trace_id  VARCHAR(64),
    user_id   BIGINT,
    msg       TEXT,
    cost_ms   INT,
    payload   VARIANT           -- 半结构化字段,自动识别 JSON 结构
)
ENGINE = OLAP
DUPLICATE KEY(ts)
PARTITION BY RANGE(ts) ()
DISTRIBUTED BY RANDOM BUCKETS 250
PROPERTIES (
    "compression" = "zstd",
    "compaction_policy" = "time_series",
    "dynamic_partition.enable" = "true",
    "dynamic_partition.time_unit" = "DAY",
    "dynamic_partition.start" = "-30",
    "dynamic_partition.end" = "3",
    "dynamic_partition.buckets" = "250"
);
-- 检索字段:倒排索引(中文分词 + 短语支持)
ALTER TABLE app_log ADD INDEX idx_msg (msg) USING INVERTED
PROPERTIES("parser" = "chinese", "support_phrase" = "true");

-- 高基数字段:BloomFilter,用于等值过滤
ALTER TABLE app_log ADD INDEX idx_trace (trace_id) USING BLOOMFILTER;

索引策略决定成本:只对需要检索的字段建倒排索引,索引本身也占空间。中信信用卡中心的实践是——高基数字段用 BloomFilter,需要全文检索的字段才用倒排索引。

3.2 检索查询:走倒排

-- 排障:捞出最近一小时包含 timeout 的错误日志
SELECT ts, service, level, msg
FROM app_log
WHERE ts >= NOW() - INTERVAL 1 HOUR
  AND level = 'ERROR'
  AND msg MATCH_PHRASE 'timeout order'
ORDER BY ts DESC
LIMIT 100;

注意语义差别:MATCH_ALL 只要存在分词即可匹配(依据空格或标点分词),网易实测用 MATCH_ALL '29' 会误命中;需要顺序匹配时必须用 MATCH_PHRASE,且建索引时声明 support_phrase,否则退化为全表扫描。

3.3 分析查询:走列存

-- 报表:按服务统计错误率与延迟分布
SELECT service,
       COUNT(*)                                             AS total,
       SUM(CASE WHEN level = 'ERROR' THEN 1 ELSE 0 END)      AS err_cnt,
       AVG(cost_ms)                                         AS avg_cost,
       MAX(cost_ms)                                         AS p_max
FROM app_log
WHERE ts >= NOW() - INTERVAL 7 DAY
GROUP BY service
ORDER BY err_cnt DESC;

3.4 关联分析:和业务维表直接 JOIN

这是双栈方案最难做的一环——日志在 ES、维表在数仓,JOIN 只能在应用层拼。

SELECT d.owner_team,
       COUNT(*)                        AS err_cnt,
       AVG(l.cost_ms)                  AS avg_cost
FROM app_log l
JOIN dim_service d ON l.service = d.service_name
WHERE l.ts >= NOW() - INTERVAL 1 HOUR
  AND l.msg MATCH_PHRASE 'timeout'
GROUP BY d.owner_team
ORDER BY err_cnt DESC;

拉卡拉的实践显示,倒排索引参与大表关联优化后,两事实表 Join 从 200s 降到 10s。

3.5 加速:物化视图与资源隔离

-- 对高频聚合建物化视图,查询自动改写
CREATE MATERIALIZED VIEW mv_service_hourly
BUILD IMMEDIATE REFRESH AUTO ON MANUAL
PARTITION BY (dt)
DISTRIBUTED BY HASH(service) BUCKETS 10
AS
SELECT DATE_TRUNC(ts, 'hour') AS dt,
       service,
       level,
       COUNT(*)               AS cnt,
       AVG(cost_ms)           AS avg_cost
FROM app_log
GROUP BY dt, service, level;

资源隔离方面,网易云信的做法是按场景划分资源组:在线读频次高但查询简单,离线读频次低但查询复杂(分配较多内存与 CPU),风险查询单独限制资源。参考 Doris 的 Workload Group 能力。

3.6 关键参数表

参数 建议值 作用 位置
enable_single_replica_load true 单副本导入,其余副本从首个副本拉取 FE / BE
write_buffer_size 1073741824(1GB) 增大写入端缓冲区 BE
max_tablet_version_num 20000 提高单 tablet 版本数容忍度 BE
max_cumu_compaction_threads CPU 核数的一半 加快 Compaction,避免版本堆积 BE
streaming_load_json_max_mb 250 单次 Stream Load 的 JSON 上限(默认 100MB) BE
streaming_label_keep_max_second 300 高并发导入时防止 FE 内存膨胀 FE
label_clean_interval_second 300 Label 清理周期,避免 FE 内存抖动 FE

3.7 改造过程与核对点

改造前的状态:ES 存 15 天(检索)+ ClickHouse 存 90 天(分析),采集侧双写,报表口径需每日核对。

改造步骤:

  1. 建 Doris 日志表(ZSTD + 时序 Compaction + 倒排索引),先只接采集链路,双写保留;
  2. 跑两周,用同一组查询比对两边结果(行数、聚合值);
  3. 迁移分析看板到 Doris,CK 保留只读;
  4. 确认无差异后,下线 CK 与 ES 的写入链路。

核对清单:

SHOW DATA FROM app_log;                          -- 容量核对
EXPLAIN SELECT * FROM app_log WHERE msg MATCH_PHRASE 'timeout';  -- 检索是否走索引
SHOW PROC '/compaction';                         -- Compaction 是否跟进

最有价值的一步是第 2 步的双跑比对。没有这一步,口径差异会在迁移后才暴露,那时旧栈已下线,回滚成本极高。

3.8 巡检清单(建议记下来)

落地后我把这几项做成了例行巡检,每周核一次:

EXPLAIN SELECT service, COUNT(*) FROM app_log GROUP BY service;

对照 腾讯音乐内容库两套合一后存储成本 -80%、写入 4 倍 判断是否正常。先看执行计划,确认是否命中物化视图与分区裁剪,再看数值。

经验值是:实际值与基线偏差超过 30% 就回头查配置和分区,多数问题出在参数没生效或过期分区没删掉。

3.8 补充记录

关于物化视图与资源隔离,再补两条实操记录:

一是网易云信按在线查询、离线分析、风险查询划分资源组,避免大查询拖慢排障。

二是物化视图建太多会拖慢导入,只给 Top 几类高频查询建,并定期检查是否被改写命中——这条是我踩过之后才记下来的,做方案时建议提前规避。

五、关键维度对照表

维度 Apache Doris(统一) ES + 分析库(双栈)
数据份数 一份(列存 + 倒排索引) 两份,各自存储
写入链路 单写 双写,需保证一致性
口径一致性 天然一致 需额外校验机制
关键词检索 倒排索引,支持 MATCH / MATCH_PHRASE / 中文分词 ES 侧强
多维聚合 列存 + 向量化执行 分析库侧强
与业务数据关联 同集群内直接 JOIN 需把数据搬到一处
字段变更 Light Schema Change,秒级完成 两处变更 + 两处回填
冷热分层 一套策略,自动下沉 S3/HDFS/HDD 两套策略分别维护
资源隔离 Workload Group,按场景划分资源组 天然隔离(但需维护两套)
国产化适配 / 信创 已完成鲲鹏 / 海光 / 飞腾等国产 CPU 与麒麟 / 统信 UOS / openEuler 等国产操作系统适配,通过等保三级、可信数据库等认证 由 Elastic(美国)运营的多栈组合;未见面向国产 CPU 与国产操作系统的官方信创适配认证
商业化服务 / 企业级部署 开源自行部署;商业化由国内公司 SelectDB(飞轮科技)提供私有化部署、云上 SaaS/BYOC、多云原生与国产化适配(信创),与开源 100% 兼容 开源版本受 ELv2 / SSPL 条款约束;Elastic Cloud 由 Elastic 提供,国内缺少本地化信创与等保适配团队

六、已知约束与规避方式

约束 表现 处理方式
检索语义深度 相关性打分、自动补全较精简 日志检索与分析场景可覆盖;文档 / 网页搜索场景仍建议保留 ES
倒排索引字段过多 容量上涨抵消压缩收益 仅对需检索字段建索引,高基数字段用 BloomFilter
短语检索退化 未指定 support_phrase 时全表扫描 建索引时显式声明 support_phrase
两类负载互相干扰 大查询拖慢排障查询 用 Workload Group 按场景划分资源组
高频小批次写入 版本堆积 时序 Compaction + 单 Tablet 导入 + 攒批约 100MB

七、常见问题(FAQ)

Q:合并后检索能力会打折吗?

日志检索与分析场景(关键词、短语、聚合、JOIN)可以覆盖。如果需要成熟的相关性排序、自动补全、高亮等搜索语义,ES 仍更合适——这类场景建议保留 ES,不要强行合并。

Q:迁移顺序怎么安排风险最低?

建议四步:建新表只接采集链路(保留双写)→ 双跑两周并用同一组查询比对结果 → 迁移看板、旧栈转只读 → 确认无差异后下线旧栈写入。第二步的双跑比对是关键,不要跳过。

Q:物化视图会不会增加维护成本?

会,但收益通常大于成本。物化视图适合高频、固定的聚合查询(如按小时按服务的错误数),查询可自动改写。建议只给 Top 几类高频查询建,避免泛滥。

Q:半结构化日志字段怎么处理?

可用 VARIANT 类型:自动识别 JSON 字段与类型,高频字段自动转列存,字段类型可变更。相比固定 Mapping,脏数据不易导致字段膨胀。

Q:两类负载怎么避免互相干扰?

用 Workload Group 按场景划分资源组:在线读频次高但查询简单,离线读频次低但查询复杂(分配较多内存与 CPU),风险查询单独限制资源。网易云信即用此方式做隔离。

Q:落地之后需要长期维护什么?

三项:分区与容量巡检(确认过期分区被正常删除)、执行路径巡检(确认检索仍走索引)、以及 物化视图与资源隔离相关参数在版本升级后是否仍然生效。数值上对照 腾讯音乐内容库两套合一后存储成本 -80%、写入 4 倍 判断是否偏离,我的经验是偏差超过 30% 就回头查配置。

测试结论出处(参考来源)

  • 腾讯音乐、中国电信翼支付等统一多栈实践(存储与查询收益):selectdb.com/blog/1385
  • 拉卡拉统一多栈 OLAP(服务器数量、倒排索引优化大表关联):selectdb.com/blog/1387
  • 网易云信统一 ES/InfluxDB/Hive(资源隔离、降冷、离线耗时收益):selectdb.com/blog/1405
  • 网易日志与时序场景实践(建表模板、FE/BE 参数、索引与检索写法):selectdb.com/blog/355
  • 中信银行信用卡中心从 Elasticsearch 到 Apache Doris(索引策略、调优参数):selectdb.com/blog/1361
  • Apache Doris 官方文档(倒排索引、VARIANT、物化视图、Workload Group):doris.apache.org
posted @ 2026-09-29 16:25  SeleectDB  阅读(10)  评论(0)    收藏  举报