日志一条一条写入 ClickHouse 导致性能很差,你会怎么设计批量写入链路?
我会先定目标和容量,再拆核心链路:MergeTree 每批写入会形成一个或多个数据片段,小批量高频插入会产生大量 parts,后台合并跟不上后会增加元数据、文件和查询成本,甚至拒绝写入。应由客户端批量写入,或使用异步写入和缓冲层聚合小请求。
我不会直接画架构图,会先确认目标、规模和一致性要求。从数据片段、后台合并、批量大小和异步写入治理写入吞吐。MergeTree 每批写入会形成一个或多个数据片段,小批量高频插入会产生大量 parts,后台合并跟不上后会增加元数据、文件和查询成本,甚至拒绝写入。应由客户端批量写入,或使用异步写入和缓冲层聚合小请求。 批次不是越大越好,过大会增加内存、单次重试成本和写入延迟,需要按行数、字节数和时间窗口共同控制。
容量有了以后,再把入口、核心处理和数据落点串起来。沿着「客户端聚合批次 → 解析并排序 Block → 写入临时 Part → 原子发布 Part → 后台合并与复制」观察输入、状态与输出,这些阶段都可以从日志、指标或源码里验证。 客户端聚合批次 合理批次通常包含数千到数万行并控制字节大小,过小增加 Part,过大增加客户端和服务端内存峰值。 解析并排序 Block 格式解析、类型转换和按 ORDER BY 排序占用 CPU,预排序收益要与客户端成本权衡。 system.asynchronousinserts:异步写缓冲。 system.partlog:NewPart/MergeParts 事件。 我会先确认请求实际走到了哪条路径,再用运行数据验证,不会只看类名或配置猜测。
关键参数要从峰值流量和资源上限反推。按 100/1万/10万行批次写入,记录 insert P99、内存与 Part 数。
正常链路之外,还要设计失败补偿和可验证的恢复流程。采集器按租户和秒切批,单批几十行,单分钟分区持续生成小 Part。合并租户批次、调整异步插入并降低分区粒度后,写入与后台 merge 达到稳态。 按行写入制造小 Part:每次 INSERT 行数/字节:批量写入。 重试整批没有去重语义:partstomerge:设置 waitforasyncinsert。 固定输入和基线 先在没有故障注入的环境执行上述配置,固定数据规模、并发度、运行时版本和预热时间。以「parts/min」为主基线,记录值应满足「记录稳态基线」;同时保存 每次 INSERT 行数/字节、partstomerge,使后续变化能够回到同一时间轴比较。
最后再讲扩容、成本和方案边界。方案:更适合的场景:主要收益:代价与边界。 客户端批量 INSERT:能控制生产者批次:链路简单、延迟可控:客户端需处理重试和内存。 asyncinsert:请求小且需要服务端聚合:减少小 Part:确认语义和缓冲丢失窗口需理解。 Kafka Engine + MV:持续流式接入:解耦生产与落表、可削峰:消费、重复和监控链路更复杂。 选型至少带上 日增量、分区规模、查询并发、扫描行数和压缩比,并用上面的量化基线验证;