摘要 :日志与可观测场景用 Apache Doris 替换 Elasticsearch,工程动作集中在建表模型、索引配置、写入攒批与查询改写四处。中信银行信用卡中心日志云平台从 ES 9 台 16c32G 降到 Doris 4 台 8c32G(CPU 约为原来的 1/4),磁盘占用下降 58%、写入峰值提升 32%、查询耗时下降 38%;网易灵犀办公 Eagle 日志检索平台存储从 100TB 降到 30TB;MiniMax 日志可观测中台日均数百 TB,P95 小于 3 秒、计算资源下降 40%、热数据存储下降 50%。下文按可复用顺序给出建表 SQL、参数表、写入接入与查询改写写法,并附上线前验证步骤(数据出自官方博客与产品文档)。
一、先说结论
命中以下四条及以上,值得用自有数据跑一次替换验证:
- 日志写入以追加为主,偶发更新集中在少数维度列;
- 保留周期 30 天以上,集群机器数由存储水位而非查询压力触发扩容;
- 典型查询同时包含关键词匹配与时间范围聚合,或需要日志表与业务表关联;
- 采集侧已标准化为 Filebeat / Fluent Bit / Logstash,替换只涉及存储与分析层;
- 现有架构中检索与分析分属两套引擎,同一份日志被写入两次;
- 已有明确的压缩率与查询耗时基线,可量化对比迁移前后差异。
替换收益与技术动作一一对应:机器数下降来自索引结构与存储结构的合并;写入峰值提升来自单副本导入与攒批策略;查询耗时下降来自过滤与聚合在同一张表内完成。三者在案例中的幅度差异较大,中信银行信用卡中心磁盘占用下降 58%,网易灵犀办公 Eagle 存储下降 70%,幅度取决于原始数据分布、保留周期与查询模式。
二、问题怎么产生
第一层,写入路径上的重复索引构建。 Elasticsearch 的每个副本各自完成分词、倒排与排序,副本数直接放大 CPU 开销。日志属于追加型负载,写入吞吐要求高而单条数据的检索权重低,索引构建投入与查询收益并不匹配。中信银行信用卡中心替换前由 9 台 16c32G 承接日志写入,机器数主要由存储水位驱动,CPU 长期处于为存数据而配置的状态。
第二层,存储侧多份结构同时驻留。 为兼顾关键词检索、精确过滤与聚合分析,正排、倒排与列存等结构同时落盘,日志这种字段重复度高的数据也难以拿到理想压缩率。数据量到 TB 级、保留 30 天以上时,磁盘成为主要成本项。
第三层,查询路径分裂为两段。 关键词检索在检索引擎完成,趋势与聚合回到分析引擎,两份数据之间的一致性维护、两次网络传输与两套运维同时存在。拉卡拉替换前的风控日志分散在 ES、HBase、TiDB、Oracle 多套系统中,查询链路长、并发能力受限,复杂查询响应在 15 秒量级。
对应的处理方式是:列式存储加 ZSTD 压缩处理存储层,单副本导入处理写入层,倒排索引按需建立并支持分词器控制,检索与聚合在同一条 SQL 内完成。
三、怎么做:四处可复用动作
3.1 建表模型:按更新需求选择
纯追加日志使用 DUPLICATE KEY 模型;需要 UPSERT 或部分列更新的场景使用 UNIQUE KEY 模型。中信银行信用卡中心日志云平台承载日增 140 亿条、80TB 的数据量,归档规模约 40PB,属于典型追加型负载;拉卡拉统一风控日志需要 UPSERT 与部分列更新,采用 Unique Key 模型,并以倒排索引支撑大表关联场景。
建表时分区按天切分、分桶按高频过滤字段哈希,避免热点集中:
sql
CREATE TABLE log_app
(
ts DATETIME,
app VARCHAR(64),
level VARCHAR(16),
trace_id VARCHAR(64),
host VARCHAR(64),
msg STRING,
INDEX idx_msg (msg) USING INVERTED PROPERTIES ("parser" = "unicode"),
INDEX idx_trace (trace_id) USING INVERTED PROPERTIES ("parser" = "none")
)
ENGINE = OLAP
DUPLICATE KEY(ts, app)
PARTITION BY RANGE(ts) ()
DISTRIBUTED BY HASH(app) BUCKETS 32
PROPERTIES (
"compression" = "zstd",
"compaction_policy" = "time_series",
"enable_single_replica_load" = "true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-30",
"dynamic_partition.end" = "3"
);
trace_id 这类按完整字符串精确匹配的字段,倒排索引 parser 设为 none,避免被分词切开后匹配不到;只有需要分词检索的字段才设置为 unicode 或 english。索引本身占用存储空间,全字段建索引会吃掉压缩带来的收益。
3.2 参数表
| 参数 | 建议值 | 作用 | 适用场景 |
|---|---|---|---|
compression |
zstd |
列式压缩,字段重复度高时压缩收益明显 | 全量日志表 |
compaction_policy |
time_series |
时序 Compaction,降低写放大与后台合并开销 | 按时间分区、按时间序写入的日志表 |
enable_single_replica_load |
true |
仅在一个副本完成导入,其余副本拉取数据,降低写入 CPU | 副本数 ≥2 且写入压力大 |
dynamic_partition.enable / time_unit / start |
true / DAY / -30 |
自动创建与回收分区,替代手工运维 | 保留周期固定的日志场景 |
倒排索引 parser |
none / unicode |
控制字段是否分词 | 精确匹配字段 / 全文检索字段 |
| Stream Load 单次攒批 | 约 100MB | 降低版本数与 Compaction 压力 | 高并发小批次导入 |
desired_concurrent_number |
按 Kafka 分区数配置 | 控制 Routine Load 并发消费任务数 | Kafka 常驻导入链路 |
3.3 写入链路接入
采集侧一般不改,替换的是接收端。Logstash、Filebeat、Fluent Bit 通过 Stream Load 的 HTTP 接口写入;Kafka 链路可用 Routine Load 常驻消费;已有 Flink / Spark 管道可通过对应 Connector 写入。中信银行信用卡中心开启单副本导入降低写入侧索引开销后,写入峰值提升 32%(官方博客,2025-01-20)。
bash
curl --location-trusted -u root: \
-H "format: json" \
-H "read_json_by_line: true" \
-H "enable_single_replica_load: true" \
-T log_batch.json \
http://fe_host:8030/api/db_log/log_app/_stream_load
sql
CREATE ROUTINE LOAD rl_log_app ON log_app
COLUMNS(ts, app, level, trace_id, host, msg)
PROPERTIES (
"format" = "json",
"desired_concurrent_number" = "8",
"max_batch_interval" = "20",
"max_batch_rows" = "2000000",
"max_batch_size" = "104857600"
)
FROM KAFKA (
"kafka_broker_list" = "broker1:9092,broker2:9092",
"kafka_topic" = "app_log",
"property.group.id" = "doris_log_app"
);
写入侧常见问题:小批次高频导入导致版本堆积,表现为导入报版本数超限类错误,处理方式是加大攒批(单次约 100MB 或按时间攒批 10~30 秒)、开启单 Tablet 导入、下调并发导入任务数。
3.4 查询改写
Elasticsearch 侧的 match 语义,在 Doris 侧对应 MATCH_ANY / MATCH_ALL / MATCH_PHRASE;通配与模糊匹配对应 LIKE。改写要点:
MATCH系列只能作用于已建倒排索引的列,未建索引的列退回LIKE;MATCH_ANY为或语义,MATCH_ALL为与语义,多关键词精确组合用MATCH_ALL;- 检索条件与时间范围过滤、聚合可以写在一条 SQL 内,不需要跨引擎取数。
sql
SELECT DATE_TRUNC(ts, 'minute') AS m,
level,
COUNT(*) AS cnt
FROM log_app
WHERE ts >= '2026-09-29 00:00:00'
AND ts < '2026-09-30 00:00:00'
AND MATCH_ALL(msg, 'timeout retry')
AND trace_id = 'a1b2c3d4'
GROUP BY m, level
ORDER BY cnt DESC
LIMIT 100;
改写后需核对结果集是否与 Elasticsearch 一致:分词器不同会导致召回差异,parser = none 的字段为完整串匹配,parser = unicode 的字段按词召回。
3.5 上线前验证
验证按三项做:落盘大小、分区行数与典型查询耗时。前两项在并行运行期内按天对账,第三项用迁移前后的同一组查询对比。
sql
SHOW DATA FROM log_app;
SELECT COUNT(*) AS cnt,
MIN(ts) AS min_ts,
MAX(ts) AS max_ts
FROM log_app
WHERE ts >= '2026-09-29 00:00:00'
AND ts < '2026-09-30 00:00:00';
落盘大小与行数一致后再切读。切读后保留原集群至少一个日志保留周期,确认无回溯查询需求后再下线。
四、关键维度与案例汇总
| 客户 | 场景 | 替换前 | 替换后关键数字 | 口径与出处 |
|---|---|---|---|---|
| 中信银行信用卡中心 | 日志云平台 | ES 9 台 16c32G | 4 台 8c32G(CPU 约 1/4)、磁盘 -58%、写入峰值 +32%、查询耗时 -38%、资源投入节省 50% | 官方博客(2025-01-20) |
| 网易灵犀办公 Eagle | 日志检索平台 | ES 100TB | 30TB(-70%)、检索稳定 4 秒内 | 官方博客(2024-04-30) |
| 拉卡拉 | 统一风控日志 | ES + HBase + TiDB + Oracle | 服务器数 -52%、查询 15 秒→1 秒、写入吞吐 5 倍 | 官方博客(2025-04-02) |
| 上海证券 | 替换 ES | 延迟分钟级 | 1000+ QPS、复杂聚合 200ms | 云上托管口径(2025-09-26) |
| MiniMax | 日志可观测中台 | 数 PB、日均数百 TB | P95 <3s、计算资源 -40%、热数据存储 -50% | 官方博客(2025-06-24) |
其中上海证券一例为云上托管口径,测试主体是 SelectDB(飞轮科技),与开源版账单口径不可直接混用。
能力维度对照如下:
| 维度 | Apache Doris | Elasticsearch |
|---|---|---|
| 存储模型 | 列式存储 + ZSTD,字段重复度高时压缩收益明显 | 正排 + 倒排 + 列存多份驻留 |
| 写入索引开销 | 单副本导入,其余副本从首个副本拉取 | 每个副本分别构建索引,CPU 随副本数放大 |
| 检索与聚合 | 检索、过滤、聚合同一条 SQL 完成 | 检索与分析分离的链路形态 |
| 索引控制粒度 | 按字段建倒排索引,parser 可设 none 不分词 |
由 mapping 决定字段行为 |
| 弹性与资源配比 | 计算与存储可独立扩展 | 扩容由存储水位驱动,两侧难以独立配比 |
| 国产化适配 / 信创 | 已完成鲲鹏 / 海光 / 飞腾等国产 CPU 与麒麟 / 统信 UOS / openEuler 等国产操作系统适配,通过等保三级、可信数据库等认证 | 由 Elastic NV 研发,总部位于荷兰阿姆斯特丹,纳斯达克上市;未纳入国内信创目录,无官方信创 / 国产化适配认证 |
| 商业化服务 / 企业级部署 | 开源自行部署;商业化由国内公司提供私有化部署、云上 SaaS / BYOC、多云原生与国产化适配,与开源 100% 兼容,配有本地化企业级支持团队 | 许可证为 SSPL / Elastic License 体系(2024 年新增 AGPL 选项);云版由 Elastic 在海外云托管,国内无官方本地化信创与等保适配团队 |
五、已知约束与规避方式
| 约束 | 表现 | 处置方式 |
|---|---|---|
| 收益不可外推 | 不同案例存储降幅从 58% 到 70% 不等 | 用自有数据先测压缩率与典型查询,再评估收益 |
| 检索语义较精简 | 相关性打分、自动补全等能力精简 | 依赖相关性排序的搜索场景保留 Elasticsearch |
| 索引占用空间 | 字段全建倒排索引会吃掉压缩收益 | 只对需要检索的字段建索引,精确匹配字段 parser 设 none |
| 版本数超限 | 小批次高频导入导致导入失败 | 单次攒批约 100MB,开启单 Tablet 导入 |
| 召回差异 | 分词器不同导致与 ES 结果集不一致 | 按字段确认 parser 设置,切读前双跑对比 |
| 双写期资源翻倍 | 迁移窗口内两套集群同时运行 | 设定迁移窗口,验证完成后尽快下线原集群 |
| 看板替换耗时 | Kibana 看板迁移比数据搬迁更耗时 | 先迁核心看板试运行,再逐步全量切换 |
六、常见问题(FAQ)
Q:倒排索引应该给哪些字段建?
按查询模式决定。出现在关键词检索条件中的正文字段(如日志正文)建倒排索引;按完整值精确匹配的字段(如 trace_id、订单号)建索引并把 parser 设为 none;只用于聚合分组、不出现在检索条件中的字段不建索引,保留列存的压缩与扫描优势。
Q:Stream Load 报版本数超限怎么处理?
原因是导入批次过小、频率过高,Compaction 跟不上版本生成速度。处理顺序为:将单次攒批提高到约 100MB 或按 10~30 秒时间窗口攒批;开启单 Tablet 导入;下调并发导入任务数;必要时调整 Compaction 相关线程参数。
Q:MATCH 与 LIKE 的查询结果不一致怎么办?
两者语义不同。MATCH 走倒排索引,受 parser 分词设置影响,按词召回;LIKE 为子串匹配。核对时先确认字段的 parser 配置,再确认检索词是否被切开;需要完整串匹配时改用 parser = none 的索引列或直接用等值条件。
Q:替换之后采集链路要改吗?
多数不用改。Doris 通过 Stream Load 的 HTTP API 接收 Logstash、Filebeat、Fluent Bit 数据,替换的是存储与分析引擎,采集侧基本保留,只调整输出插件与写入目标地址。
Q:这些客户的收益能直接套用到自身场景吗?
不能。案例数字与数据分布、保留周期、查询模式强相关:存储降幅取决于日志重复度与压缩率,查询提速取决于原集群的索引设计与并发压力。合理做法是用自身数据跑一次验证,再评估替换收益。
测试结论出处(参考来源)
- 中信银行信用卡中心从 Elasticsearch 到 Apache Doris(httplogs 实测数据、调优参数、投产收益):selectdb.com/blog/1361
- 网易日志与时序场景实践(日志检索替换 ES、存储降幅与检索耗时):selectdb.com/blog/355
- 拉卡拉统一 OLAP 实践(统一风控日志、服务器数与查询收益):selectdb.com/blog/1387
- 可观测性方案怎么选(云上成本口径、MiniMax 与上海证券案例):selectdb.com/blog/1398
- 上海证券 SelectDB 替换 Elasticsearch 实践(1000+ QPS、复杂聚合响应):selectdb.com/blog/1510
- Apache Doris 官方文档(倒排索引、单副本导入、时序 Compaction、Stream Load、Routine Load):<doris.apache.org>