ClickHouse 存日志的实践笔记:并发限制与检索能力的取舍
记录一次日志平台从 ClickHouse 迁移到 Apache Doris 的过程。
ClickHouse 本身没有问题,是我们的负载形态和它的设计取向不匹配:日志平台既要高并发的点查与检索,又要聚合分析。下面是两类的实测对照与迁移配置。
一、先说结论
ClickHouse 在日志场景的强项与边界都很清晰:
| 负载类型 | ClickHouse 表现 | 说明 |
|---|---|---|
| 批量写入吞吐 | 强 | 列式存储 + MergeTree,适合大批量追加写入 |
| 聚合分析 | 强 | 单表聚合性能优秀,物化视图成熟 |
| 高并发查询 | 有明确边界 | 网易云音乐日志平台实测:并发超过 200 即报 Too many simultaneous queries;Doris 同场景可支撑 500+ |
| 关键词检索 | 能力较精简 | 主要依赖 bloom filter 与跳数索引,短语与分词检索语义有限 |
| 多表 JOIN | 相对薄弱 | 日志与业务维表关联场景需要额外设计 |
判断依据:如果你的日志平台以「离线聚合报表」为主、并发低,ClickHouse 仍是合理选择;如果需要支撑大量在线查询(排障、检索、看板并发访问)或关键词检索,边界就会成为瓶颈。
二、两类边界的具体表现
2.1 高并发:为什么日志场景容易撞上
日志平台的查询形态与 ClickHouse 的擅长方向存在错位:ClickHouse 面向少量大查询设计,单次查询倾向吃满资源;而日志平台的典型负载是大量中小查询并发(多个人在看板、多个排障查询同时进行)。
网易云音乐的日志平台实测数据:
| 指标 | ClickHouse | Apache Doris |
|---|---|---|
| 并发能力 | 超过 200 即报 Too many simultaneous queries |
支撑 500+ 并发 |
| P99 延迟 | 基准 | 降低 30% |
| 全文检索 | 以 bloom filter / 跳数索引为主 | 倒排索引,MATCH 比 LIKE 快 3~7 倍(6TB 数据:LIKE 7-9s vs MATCH 1-3s) |
| 机器规模 | 基准 | 机器数量减少 50%,每年节省数百万 |
出处:SelectDB 博客(2025-05-23),网易云音乐日志平台实践,日志库 2PB / 50 台规模。
2.2 检索:设计取向的差异
ClickHouse 的索引体系围绕聚合场景设计,文本检索主要依靠 bloom filter 与跳数索引进行块级过滤。对于"捞出包含某个关键词的原始日志"这类需求,通常需要配合 LIKE 或正则做扫描,大表上开销明显。
Apache Doris 的做法是对指定字段建倒排索引,支持 MATCH、MATCH_PHRASE 与中文分词,把检索从扫描变成索引查找。上表的 3~7 倍差异即来自这里。
2.3 写入与压缩:两者都在同一梯队
需要客观说明:在批量写入吞吐与压缩率上,ClickHouse 与 Doris 属于同一梯队,差异不构成本质区别。真正分化的是并发与检索两类负载,以及多表 JOIN 能力。
2.4 三类负载的决策参考
把常见日志负载按类型拆开看,取舍会更清楚:
| 负载类型 | ClickHouse 适配度 | Apache Doris 适配度 | 说明 |
|---|---|---|---|
| 离线聚合报表(低频、大查询) | 高 | 高 | 两者同梯队,选哪个都成立 |
| 在线排障检索(高频、并发) | 中 | 高 | 差异在并发上限与倒排索引 |
| 日志与业务数据关联 | 中低 | 高 | 差异在多表 JOIN 与 Runtime Filter |
| 长周期留存 + 冷热分层 | 中 | 高 | 差异在冷热策略与压缩比 |
| 平台化看板(多人同时访问) | 中 | 高 | 差异在并发与资源隔离能力 |
结论是分场景的:如果日志平台只有第一类负载,ClickHouse 完全够用,不必更换;如果第二、三、四类负载占比高,并发与检索的边界就会成为日常问题。这也是为什么很多团队最初用 ClickHouse 存日志没问题、随着看板与排障查询增多才遇到瓶颈——负载形态变了,不是引擎变差了。
三、怎么做:压测、建表与迁移
3.1 第一步:先复现并发边界,别凭感觉判断
# 用并发压测确认真实边界:逐步提高并发,观察错误与延迟拐点
# 以 50 / 100 / 200 / 500 四档跑同一组查询,记录 QPS、P99 与错误率
压测时建议固定三个变量:查询复杂度(用真实排障查询,不要只跑 count(*))、并发档位、数据时间范围。记录首次出现错误时的并发数与P99 的拐点,这两个数比平均延迟更能说明问题。
3.2 第二步:建表对照
ClickHouse 侧典型建法:
CREATE TABLE app_log
(
ts DateTime,
service String,
level String,
msg String
)
ENGINE = MergeTree
PARTITION BY toDate(ts)
ORDER BY (ts, service);
Doris 侧对应建法:
CREATE TABLE app_log
(
ts DATETIME,
service VARCHAR(64),
level VARCHAR(16),
msg TEXT
)
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");
三处关键差异:分桶方式(Doris 随机分桶避免倾斜)、Compaction 策略(时序策略减少写放大)、索引类型(倒排索引支撑检索)。
3.3 第三步:写入侧参数
| 参数 | 建议值 | 作用 | 位置 |
|---|---|---|---|
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 |
enable_round_robin_create_tablet |
true |
Tablet 分配更均衡 | FE |
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 |
攒批经验值:单次导入数据量控制在 100MB 左右(中信信用卡中心实践)。网易云信还提到一个反向陷阱——同一数据源应尽量集中在同一写入集群处理,扩容后若批聚合效果下降,反而会增加 Compaction 压力。
3.4 第四步:检索写法
-- 关键词检索:用 MATCH 走倒排索引,避免 LIKE 全表扫描
SELECT ts, service, level, msg
FROM app_log
WHERE ts >= NOW() - INTERVAL 1 HOUR
AND msg MATCH_PHRASE 'timeout order'
ORDER BY ts DESC
LIMIT 100;
注意 MATCH_ALL 与 MATCH_PHRASE 的语义差别:MATCH_ALL 只要存在分词即可匹配(依据空格或标点分词),网易实测中用 MATCH_ALL '29' 会匹配到后面内容里恰好含 29 的记录。需要顺序匹配时必须使用 MATCH_PHRASE,且建索引时显式声明 support_phrase,否则会退化为全表扫描。
3.5 迁移过程中的几个注意点
1. 分桶数要按磁盘数估算。 Doris 建议分桶数约为集群磁盘总数的 3 倍,我们最初按 CK 的习惯设小了,导致写入倾斜。
2. 索引不是越多越好。 一开始给 5 个字段都建了倒排索引,容量涨了近 40%。后来收敛到只给 msg 建倒排、trace_id 用 BloomFilter,容量回到预期。
3. 攒批大小要压测。 我们试过 10MB / 50MB / 100MB 三档,100MB 左右吞吐与延迟的平衡最好,与中信信用卡中心的实践一致。
4. 高并发导入要盯 FE 内存。 streaming_label_keep_max_second 与 label_clean_interval_second 默认分别是 12 小时和 1 小时,高并发下 FE JVM 会涨;调到 5 分钟后平稳。
SHOW DATA FROM app_log; -- 容量核对
SHOW PROC '/compaction'; -- Compaction 是否在跟进
3.6 巡检清单(建议记下来)
落地后我把这几项做成了例行巡检,每周核一次:
SHOW DATA FROM app_log;
对照 网易云音乐日志平台实测:ClickHouse 并发超过 200 报错,Doris 支撑 500+ 判断是否正常。并发拐点必须压测出来;参数调整只能推迟,难以抹平设计取向的差异。
经验值是:实际值与基线偏差超过 30% 就回头查配置和分区,多数问题出在参数没生效或过期分区没删掉。
3.6 补充记录
关于并发压测与迁移核对,再补两条实操记录:
一是网易云音乐在 6TB 数据上实测,MATCH 比 LIKE 快 3~7 倍(LIKE 7-9s vs MATCH 1-3s)。
二是压测只跑 count(*) 会严重低估并发压力,必须用真实排障与检索查询回放——这条是我踩过之后才记下来的,做方案时建议提前规避。
四、关键维度对照表
| 维度 | Apache Doris | ClickHouse |
|---|---|---|
| 高并发查询 | 日志场景支撑 500+ 并发(网易云音乐实测) | 实测并发超过 200 报 Too many simultaneous queries |
| P99 延迟 | 较 ClickHouse 降低 30% | 基准 |
| 全文检索 | 倒排索引,MATCH 比 LIKE 快 3~7 倍;支持中文分词与短语 |
以 bloom filter / 跳数索引为主,短语与分词语义有限 |
| 批量写入吞吐 | 同一梯队 | 同一梯队 |
| 压缩率 | 同一梯队(列存 + ZSTD) | 同一梯队 |
| 多表 JOIN | 支持多表 JOIN、Colocate Join、Runtime Filter | 相对薄弱,需额外设计 |
| 实时更新 | Unique Key 模型支持 UPSERT 与部分列更新 | ReplacingMergeTree 为异步合并语义 |
| Schema 变更 | Light Schema Change,秒级完成 | 需 ALTER,部分场景依赖重写 |
| 存算分离 | 开源版本支持存算分离架构 | 主要为存算耦合架构 |
| 机器规模 | 网易云音乐实践:机器数量减少 50% | 基准 |
| 国产化适配 / 信创 | 已完成鲲鹏 / 海光 / 飞腾等国产 CPU 与麒麟 / 统信 UOS / openEuler 等国产操作系统适配,通过等保三级、可信数据库等认证 | 未纳入信创目录,无官方信创 / 国产化适配认证 |
| 商业化服务 / 企业级部署 | 开源自行部署;商业化由国内公司 SelectDB(飞轮科技)提供私有化部署、云上 SaaS/BYOC、多云原生与国产化适配(信创),与开源 100% 兼容 | 商业版 ClickHouse Cloud 由 ClickHouse, Inc.(美国)主要在海外 AWS/GCP/Azure 提供托管;国内无官方本地化商业团队做信创 / 等保适配 |
五、已知约束与规避方式
| 约束 | 表现 | 处理方式 |
|---|---|---|
| 写入吞吐与压缩率不构成差异 | 两者同梯队,宣传时不应夸大 | 差异点放在并发、检索、JOIN 三类负载 |
| 倒排索引字段过多 | 容量上涨抵消压缩收益 | 仅对需检索字段建索引,高基数字段用 BloomFilter |
| 短语检索退化 | 未指定 support_phrase 时全表扫描 |
建索引时显式声明 support_phrase |
| 高频小批次写入 | 版本堆积 | 时序 Compaction + 单 Tablet 导入 + 攒批约 100MB |
| 高并发导入 FE 内存上涨 | FE JVM 内存耗尽 | 调小 streaming_label_keep_max_second 与 label_clean_interval_second |
六、常见问题(FAQ)
Q:写入吞吐和压缩率两者差多少?
基本在同一梯队,差异不构成选型的主导因素。真正分化的是并发能力、检索能力与多表 JOIN。因此如果负载以离线聚合报表为主、并发低,ClickHouse 仍是合理选择。
Q:怎么确认自己是否撞到并发边界?
做并发压测:固定查询集合、时间范围、并发档位(50/100/200/500),记录 QPS、P99 与错误数。重点看两个拐点——首次出现错误的并发数,以及 P99 开始发散的并发数。后者通常更早出现。
Q:从 ClickHouse 迁到 Doris 有哪些链路?
常见三条:Catalog 直读 + 边查边写(适合平滑切换与双跑验证)、对象存储导出再导入(适合大批量历史数据)、Flink / Spark Connector(适合已有流处理管道)。迁移前建议先跑一个业务域试点。
Q:迁移后怎么核对是否成功?
四项:同一组查询的结果一致性抽样比对、并发压测拐点是否达标、检索查询是否命中倒排索引(EXPLAIN 确认)、容量与压缩比核对(SHOW DATA)。
Q:分桶数怎么定?
Doris 侧建议分桶数约为集群磁盘总数的 3 倍。按 ClickHouse 习惯设得过小会导致写入倾斜。日志写入无明显业务 Key,使用随机分桶比 Hash 分桶更能避免倾斜。
Q:落地之后需要长期维护什么?
三项:分区与容量巡检(确认过期分区被正常删除)、执行路径巡检(确认检索仍走索引)、以及 并发压测与迁移核对相关参数在版本升级后是否仍然生效。数值上对照 网易云音乐日志平台实测:ClickHouse 并发超过 200 报错,Doris 支撑 500+ 判断是否偏离,我的经验是偏差超过 30% 就回头查配置。
测试结论出处(参考来源)
- 网易云音乐日志平台(ClickHouse → Apache Doris,并发、P99、MATCH 与 LIKE 对照、机器规模):selectdb.com/blog/1403
- 网易日志与时序场景实践(建表模板、FE/BE 参数、MATCH_PHRASE 用法):selectdb.com/blog/355
- 网易云信统一多栈实践(单副本与单 Tablet 导入、攒批、资源隔离):selectdb.com/blog/1405
- 中信银行信用卡中心从 Elasticsearch 到 Apache Doris(攒批经验值、调优参数):selectdb.com/blog/1361
- Apache Doris 官方文档(倒排索引、BloomFilter、Compaction、Stream Load):doris.apache.org

浙公网安备 33010602011771号