
ClickHouse 千万级 TPS 实时写入下的 Part 治理彻底告别Too many parts写入挂起在大促实时监控大盘、千亿级 APM 全链路日志与流式风控场景中ClickHouse 凭借其强悍的列存写入吞吐成为了事实上的标准选型。然而几乎每一个初次将 ClickHouse 推上千万级实时写入高压的架构师都会在某天夜里被同一个臭名昭著的崩溃异常所击溃DB::Exception: Too many parts in all data parts in table t_order_log (300). Merges are processing significantly slower than inserts, parts are being generated at a rate of 120.00 parts per second. (version 24.8.3)伴随着这个异常ClickHouse 会无情地启动写入反压与挂起Write Stall——直接拒绝任何新的INSERT请求导致上游 Flink 流处理任务瞬间发生背压雪崩Kafka 消息大量积压。为什么 ClickHouse 如此惧怕“频繁小写入”在千万级 TPS 的持续高压下我们如何从物理微架构出发彻底根治 Part 碎片膨胀实现丝滑平稳的极限实时写入[ClickHouse 频繁小写入引发 Too Many Parts 写入挂起机制] 上游应用高频单行 / 小批写入 (例如每秒 1000 次, 每次 50 行) │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 每次 INSERT 都在磁盘生成一个独立的物理 Part 目录! │ │ (每秒产生 1000 个小 Part, 单表累积 Part 数量直奔 300!) │ └──────────────────────────────┬──────────────────────────────┘ │ 远超后台 Compaction 合并算力极限! ▼ ┌─────────────────────────────────────────────────────────────┐ │ 触碰 parts_to_throw_insert 300 熔断红线! │ │ ──▶ 【系统强制拒绝所有新写入! 抛出 Too many parts 异常!】 │ └─────────────────────────────────────────────────────────────┘内核机制为什么每一次INSERT都会生成一个物理 PartClickHouse 的底层存储引擎是基于LSM-Tree 变体——MergeTree设计的。ClickHouse 内核中有一个绝对的物理铁律客户端发起的每一次INSERT INTO ...事务无论你写入的是 100 万行还是仅仅 1 行ClickHouse 都会在磁盘上为你创建一个全新的、独立的 Part 目录Part Folder如果上游应用像对待传统 MySQL 一样每个线程单条写入数据每秒 1000 次写入就会在磁盘上生成1000 个独立的物理小 Part 目录每个 Part 都包含所有列的压缩文件.bin和稀疏索引标记.mrk3后台后台合并工作池BackgroundProcessingPool哪怕拼尽全力进行多路归并也无法在物理 IO 上追赶如此恐怖的 Part 产生速率当单表活跃 Part 数量超过parts_to_delay_insert 150时ClickHouse 开始人为增加写入延迟超过parts_to_throw_insert 300时直接抛出异常拒绝写入[工业级千万 TPS 写入三阶缓冲治理流水线] 千万级实时事件流 (Kafka 流水) │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 第一阶段: Flink / 消费端大微批攒批 (Micro-Batch Buffering) │ │ - 严格约束: 单批次 50,000 行 或 等待时间 2.0 秒 │ │ - 将写入频率从每秒数万次骤降至每秒 10~20 次大块落盘 │ └──────────────────────────────┬──────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 第二阶段: ClickHouse 内存 Buffer 引擎前置平滑缓冲 │ │ - 写入先落内存 Buffer Table每隔 5 秒异步大块 Flush 实体表│ └──────────────────────────────┬──────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 第三阶段: MergeTree 后台异步合并算力深度调优 │ │ - 增大 background_pool_size 32 (释放多核合并算力) │ │ - 调优 max_bytes_to_merge 避免大 Part 阻塞小 Part 快速合并│ └─────────────────────────────────────────────────────────────┘生产级根治方案一上游必须执行“大微批攒批Micro-Batching”解决 Part 膨胀的绝对第一法则是在客户端或流计算引擎Flink / Spark Streaming中完成数据攒批// Flink ClickHouse Sink 标准生产攒批配置范式 ClickHouseSink sink ClickHouseSink.builder() .setBatchSize(50000) // 核心参数: 单批次累积达到 50,000 行才执行一次写入 .setFlushInterval(2000) // 核心参数: 最长等待时间 2000 毫秒 .setMaxRetries(3) .build();物理效果原本每秒 5 万次零散写入被规整为每秒仅发起 1 次包含 50,000 行的大 Block 写入磁盘每秒仅产生 1 个高质量的大 Part后台合并线程可以在不到 0.05 秒内轻松完成异步 Compaction活跃 Part 数量永远稳定在10 到 20 个的极健康水位生产级根治方案二内核合并调度与 Buffer 引擎调优在 ClickHouse 配置文件config.xml和users.xml中必须针对高并发写入实例进行内核调优yandex !-- 1. 扩大后台数据合并线程池 (释放多核 CPU 算力) -- background_pool_size32/background_pool_size background_merges_mutations_concurrency_ratio2/background_merges_mutations_concurrency_ratio profiles default !-- 2. 适当放宽大促写入熔断阈值避免瞬时毛刺直接抛异常 -- parts_to_delay_insert300/parts_to_delay_insert parts_to_throw_insert600/parts_to_throw_insert max_delay_to_insert1/max_delay_to_insert /default /profiles /yandex生产实操收益通过将全站上游写入全面重构为大微批模式并调优后台合并线程池在大促全链路压测中ClickHouse 集群成功抗住了单机每秒 85 万行、全集群超 1500 万 TPS的极限实时写入洪峰单表活跃 Part 数量长期稳定在18 个左右Too many parts异常彻底归零实现了大促指挥大盘“千万写入不卡顿、秒级查询毫秒出”的卓越架构表现。