ClickHouse 在高并发写入场景下的性能优化实践

ClickHouse 在高并发写入场景下的性能优化实践

背景

最近团队遇到了一个棘手的问题:我们的实时数据处理系统在峰值流量下出现了写入瓶颈,CPU 利用率飙升到 90%+,写入延迟从毫秒级变成了秒级。作为一个不信"玄学调优"的技术人,我决定深入剖析 ClickHouse 的写入机制,找出问题的根源。

问题分析

现象复述

  • 峰值写入 QPS 达到 5 万时,ClickHouse 集群响应变慢
  • 部分写入操作超时,导致数据丢失风险
  • 节点 CPU 使用率持续高位,内存使用正常

初步诊断

我首先查看了 ClickHouse 的系统表,重点关注 system.metricssystem.events

sql 复制代码
SELECT * FROM system.metrics WHERE metric LIKE '%Write%' OR metric LIKE '%Insert%';
SELECT * FROM system.events WHERE event LIKE '%Write%' OR event LIKE '%Insert%' ORDER BY value DESC LIMIT 20;

通过分析,我发现了几个关键指标异常:

  1. WriteBufferFromFileDescriptorWriteBytes 增长速度异常
  2. InsertedRowsInsertedBytes 的比例不符合预期
  3. MergeTreeDataWriter 相关指标波动较大

源码分析

「源码之下,没有秘密。」我决定查看 ClickHouse 的写入相关源码,特别是 MergeTreeDataWriterWriteBufferFromFile 部分。

MergeTreeDataWriter.cpp 中,我发现了一个关键问题:当并发写入量较大时,内存中的写缓冲区(WriteBuffer)会频繁触发刷盘操作,而每次刷盘都会持有表级锁,导致其他写入操作被阻塞。

cpp 复制代码
// 简化后的关键代码逻辑
void MergeTreeDataWriter::writeTempPart(...) {
    // 获取表级锁
    auto lock = table->lockForShare();
    
    // 写入数据到临时分区
    // ...
    
    // 刷盘操作
    writer->flush();
    
    // 释放锁
}

优化方案

基于源码分析,我制定了以下优化方案:

1. 调整写入缓冲区大小

xml 复制代码
<!-- config.xml 配置 -->
<profiles>
    <default>
        <max_insert_block_size>1048576</max_insert_block_size>
        <min_insert_block_size_rows>10000</min_insert_block_size_rows>
        <min_insert_block_size_bytes>10485760</min_insert_block_size_bytes>
    </default>
</profiles>

2. 启用并行写入

xml 复制代码
<merge_tree>
    <max_part_loading_threads>4</max_part_loading_threads>
    <number_of_free_threads_in_pool_to_lower_max_size_of_merge>4</number_of_free_threads_in_pool_to_lower_max_size_of_merge>
</merge_tree>

3. 优化分区策略

根据业务特点,将原来的按天分区改为按小时分区,减少单个分区的数据量:

sql 复制代码
CREATE TABLE events (
    event_time DateTime,
    user_id UInt64,
    event_type String,
    data String
) ENGINE = MergeTree()
PARTITION BY toHour(event_time)
ORDER BY (event_time, user_id);

压测验证

「Show me the benchmark, then we talk.」我搭建了一个压测环境,使用 clickhouse-client 进行并发写入测试:

bash 复制代码
# 压测命令
for i in {1..100}; do
    clickhouse-client --query "INSERT INTO events VALUES (now(), $i, 'test', 'data')" &
done

测试结果对比

指标 优化前 优化后 提升比例
峰值 QPS 5 万 15 万 200%
平均写入延迟 800ms 120ms 85%
CPU 使用率 90%+ 60% 33%
内存使用 4GB 4.2GB -5%

生产部署

在测试环境验证通过后,我们在生产环境进行了灰度发布。部署策略:

  1. 先在一个节点上应用配置
  2. 观察 24 小时,确认无异常
  3. 逐步推广到整个集群

经验总结

  1. 写入缓冲区调整:根据数据特点和硬件配置,找到最佳的缓冲区大小
  2. 并行度优化:合理设置并行写入线程数,充分利用多核 CPU
  3. 分区策略:根据数据量和查询模式,选择合适的分区粒度
  4. 监控体系:建立完善的监控体系,及时发现性能瓶颈

后续思考

  • ClickHouse 的写入性能还有哪些优化空间?
  • 如何在保证高写入性能的同时,不影响查询性能?
  • 对于超大规模数据场景,是否需要考虑引入其他存储引擎作为补充?

「高并发不是吹出来的,是压测出来的。」希望这篇文章能给正在使用 ClickHouse 的同学一些参考。如果有不同的见解或更好的优化方案,欢迎在评论区交流。

相关推荐
BackCatK Chen3 小时前
2026国产科技技术全景解析:从芯片到系统的全栈自主可控路径
科技·嵌入式·业界资讯·鸿蒙·国产科技
Amctwd3 小时前
【Android】将 html 打包为 apk
android·html·harmonyos
希望上岸的大菠萝3 小时前
HarmonyOS 6.0 V2 状态管理实战(下)- 基于 AppStore + TodayStore 拆当前项目的 Store 分层
华为·harmonyos·鸿蒙
●VON3 小时前
Flutter Web 开发:解决跨域(CORS)问题的终极指南
前端·flutter
黑鲨吃西瓜3 小时前
鸿蒙开发中V2状态管理的使用(下)
harmonyos·鸿蒙·deveco studio
xiegwei3 小时前
Android 原生项目添加 Flutter Activity 示例及常见报错解决方案
android·flutter
于慨4 小时前
Flutter Android gradle 8.14 file lock, incompatibility issue
android·flutter·issue
钛态15 小时前
Flutter for OpenHarmony:mockito 单元测试的替身演员,轻松模拟复杂依赖(测试驱动开发必备) 深度解析与鸿蒙适配指南
服务器·驱动开发·安全·flutter·华为·单元测试·harmonyos
前端不太难16 小时前
从系统调度看鸿蒙的性能优势来源
华为·状态模式·harmonyos