文章总结: 本文提供ClickHouseCPU高负载排查指南,涵盖诊断、定位与优化。通过system.processes等系统表区分慢查询、并发冲突及后台任务抢占资源。优化策略包括限制查询并发、改用LZ4压缩、优化SQL及批量写入。文末附监控脚本实现长效预警。该方案实操性强,能帮助运维人员快速解决生产性能瓶颈。 综合评分: 91 文章分类: 安全运营,解决方案,实战经验
救命!ClickHouse CPU飙满了?运维手把手排查指南
原创
小柳实验室
小柳实验室
2025年12月31日 06:30 湖南
做大数据运维的同学,大概率都遇过这种崩溃时刻:ClickHouse突然CPU拉满,监控面板一片红,业务查询超时告警刷不停,手忙脚乱排查却找不到头绪。
其实不用慌!ClickHouse CPU飙高就那几个常见原因——要么是查询并发太多挤爆了,要么是慢查询在拖后腿,再不然就是后台合并任务抢资源、数据配置没弄对。
我把自己平时排查这类问题的经验,整理成了一套 「应急诊断→精准定位→优化解决→长效监控」 的完整流程,每一步都配了可直接复制的命令和脚本,就算是运维新手,跟着做也能快速搞定。
一、紧急止血!3步快速锁定问题方向
CPU一飙高,先别乱改配置、乱杀进程!第一步是快速缩小问题范围,这3步操作下来,10分钟内就能搞清楚大概是哪里出了问题,避免越操作越乱。
1. 先确认:CPU高负载是不是ClickHouse搞的鬼?
首先要分清,是ClickHouse本身占满了CPU,还是其他系统进程在捣乱。下面4种方法都能查,选自己顺手的来就行:
# 方法1:最常用,精准看ClickHouse进程的CPU占用
top -p $(pgrep clickhouse-server)
# 方法2:可视化更直观,需要先装htop
htop -p $(pgrep clickhouse-server)
# 方法3:看进程详细的资源占比情况
ps aux | grep clickhouse-server | grep -v grep
# 方法4:查所有CPU核心的使用情况,1秒采1次,共采5次
mpstat -P ALL 1 5
这里给大家说个行业通用的判断标准,一看就懂:
- • ✅ 正常:CPU使用率低于70%,服务稳得很
- • ⚠️ 警告:70%-90%,得盯紧了,准备优化
- • 🚨 危险:超过90%,业务随时可能崩,赶紧处理!
2. 再检查:系统是不是已经过载了?
这里要注意:CPU使用率高,不代表系统就过载了。但如果「系统负载」超过了CPU核心数,就说明进程开始排队了,响应肯定会变慢。
# 快速看系统负载
uptime
# 看详细负载数据(前3个数字分别是1、5、15分钟的负载)
cat /proc/loadavg
# 实时监控负载变化,每秒刷新1次
watch -n 1 'uptime'
给大家举个例子,比如服务器是16核CPU,判断标准就是这样:
-
• ✅ 正常:负载低于16,没有进程排队
-
• ⚠️ 警告:16-32之间,轻度排队,部分查询可能受影响
-
• 🚨 危险:超过32,严重排队,大量查询会超时
3. 初步定位:是查询还是后台任务在搞事?
搞清楚CPU高负载的来源后,用下面2条命令,快速分清是用户查询的问题,还是ClickHouse的后台任务在抢资源:
# 查看当前有多少个并发查询在跑
clickhouse-client --query "SELECT count() as concurrent_queries FROM system.processes WHERE query != ''"
# 查看活跃的后台任务数(主要是合并任务和Mutation任务)
clickhouse-client --query "
SELECT 'Merges' as task_type, count() as active_count FROM system.merges
UNION ALL
SELECT 'Mutations' as task_type, count() as active_count FROM system.mutations WHERE is_done = 0
"
看结果的时候可以这么判断:
- • 如果并发查询数超过了CPU核心数 → 大概率是查询太多挤爆了
- • 如果合并任务数超过15个,或者Mutation任务数超过3个 → 就是后台任务在抢CPU
二、精准破案!4个维度揪出真正元凶
通过上面的紧急排查锁定大致方向后,接下来就深入挖根源。下面这4个维度,基本能覆盖90%的ClickHouse CPU飙高场景。
维度1:查当前运行的查询——头号嫌疑犯
绝大多数CPU飙高,都是慢查询或者并发查询太多导致的。通过system.processes这个系统表,就能清楚看到所有正在跑的查询状态。
1. 查看所有正在执行的查询详情
clickhouse-client --query "
SELECT
query_id, # 查询的唯一ID,后面杀查询要用
user, # 谁执行的这个查询
address, # 客户端的地址
query, # 具体的查询SQL
elapsed, # 已经跑了多少秒
read_rows, # 已经读取了多少行数据
formatReadableSize(read_bytes) as read_bytes, # 读取的字节数,自动转成易读格式
formatReadableSize(memory_usage) as memory # 占用的内存
FROM system.processes
WHERE query != ''
ORDER BY elapsed DESC # 按运行时间倒序,先看跑了最久的查询
FORMAT Vertical # 垂直显示,看着更清楚
"
2. 筛选长时间运行的“问题查询”
如果觉得上面的命令输出太多,也可以直接过滤出跑了超过10秒的查询,精准定位拖慢系统的“元凶”:
clickhouse-client --query "
SELECT
query_id,
user,
query,
elapsed,
read_rows,
formatReadableSize(read_bytes) as read_bytes
FROM system.processes
WHERE query != '' AND elapsed > 10 # 只看跑了超过10秒的查询
ORDER BY elapsed DESC
"
3. 紧急处理:杀掉占满CPU的查询
如果查到某个查询跑了很久,而且CPU占比极高,别犹豫,先杀掉保住系统!用前面查到的query_id就行:
clickhouse-client --query "KILL QUERY WHERE query_id='你的查询ID'"
维度2:查历史慢查询——根治问题的关键
有时候当前跑的查询没问题,但CPU还是高,可能是历史慢查询留下的隐患。通过system.query_log系统表,能查到之前的慢查询记录,从根源上优化。
1. 查看最近100条慢查询
clickhouse-client --query "
SELECT
query_id,
user,
query,
query_start_time, # 查询开始的时间
query_duration_ms, # 执行了多少毫秒
read_rows,
formatReadableSize(read_bytes) as read_bytes
FROM system.query_log
WHERE type = 2 # type=2代表查询已经完成(1是开始,2是结束)
AND query_duration_ms > 1000 # 只看执行时间超过1秒的慢查询
ORDER BY query_duration_ms DESC
LIMIT 100
"
2. 找出高频慢查询模式
有时候不是单个查询慢,而是某类相似查询反复执行,累积起来拖垮CPU。用normalizeQuery函数能去掉SQL里的具体参数,找出最耗资源的查询模板:
clickhouse-client --query "
SELECT
normalizeQuery(query) as query_pattern, # 统一查询模板,去掉具体值
count() as query_count, # 这类查询执行了多少次
sum(query_duration_ms) as total_duration_ms, # 总耗时
sum(read_rows) as total_read_rows # 总读取行数
FROM system.query_log
WHERE type = 2
AND event_time > now() - INTERVAL 24 HOUR # 查最近24小时
AND query_duration_ms > 1000
GROUP BY query_pattern
ORDER BY total_duration_ms DESC # 按总耗时排序
LIMIT 20
"
找到这类高频慢查询模板后,针对性优化SQL,就能从根源解决问题。
维度3:查后台任务——容易被忽略的“隐形凶手”
很多同学排查时只关注用户查询,却忘了ClickHouse的后台任务也会抢CPU。最常见的就是合并任务(Merges)和Mutation任务,尤其是在数据导入高峰期。
1. 查看正在进行的合并任务
ClickHouse的MergeTree引擎会自动合并小分区,合并过程会消耗大量CPU和IO,合并任务太多就会拖慢整个系统:
clickhouse-client --query "
SELECT
database,
table,
elapsed, # 合并已经跑了多少秒
progress, # 合并进度(0-1,1代表完成)
merge_type, # 合并类型
formatReadableSize(total_bytes_to_merge) as total_size # 要合并的数据量
FROM system.merges
ORDER BY elapsed DESC
FORMAT Vertical
"
判断标准:
- • ✅ 正常:活跃合并任务数 < 5个
- • ⚠️ 警告:5-15个,会影响查询性能
- • 🚨 危险:> 15个,CPU基本会被合并占满
2. 查看卡住的Mutation任务
当执行ALTER TABLE … DELETE/UPDATE语句时,会生成Mutation任务。如果任务卡住,会一直占用CPU资源,而且还可能导致数据不一致:
clickhouse-client --query "
SELECT
database,
table,
mutation_id, # Mutation任务ID
command, # 执行的具体命令(比如DELETE/UPDATE)
create_time, # 任务创建时间
latest_fail_reason # 失败原因(如果卡住,看这里找问题)
FROM system.mutations
WHERE is_done = 0 # 只看未完成的任务
ORDER BY create_time DESC
FORMAT Vertical
"
判断标准:正常情况下,活跃的Mutation任务数应该是0,超过3个就必须警惕。
维度4:查数据和表配置——基础配置没弄对,再优化也白搭
如果前面查了都没问题,就看看是不是数据分区、表结构或者压缩算法配置不合理,这些基础问题也会导致CPU长期偏高。
1. 查看表的分区情况
分区太多、单个分区太大,都会增加查询时的CPU开销(需要扫描更多数据)。比如单个分区超过10GB,查询时就容易卡顿:
clickhouse-client --query "
SELECT
database,
table,
partition,
count() as parts_count, # 这个分区有多少个数据块
sum(rows) as total_rows, # 总行数
formatReadableSize(sum(bytes_on_disk)) as total_size # 分区大小
FROM system.parts
WHERE active = 1 # 只看活跃的分区
GROUP BY database, table, partition
ORDER BY sum(bytes_on_disk) DESC
LIMIT 50
"
2. 检查压缩算法是否合理
ClickHouse支持LZ4、ZSTD等压缩算法,不同算法对CPU的消耗差别很大:
clickhouse-client --query "
SELECT
database,
table,
format, # 压缩算法
round(sum(data_uncompressed_size) / sum(bytes_on_disk), 2) as compression_ratio # 压缩率
FROM system.parts
WHERE active = 1
GROUP BY database, table, format
ORDER BY sum(bytes_on_disk) DESC
LIMIT 30
"
给大家个选型建议,根据业务场景选:
- • ✅ 优先选LZ4:速度快,CPU消耗低,压缩率中等,适合查询频繁的场景
- • ⚠️ 选ZSTD要谨慎:压缩率高,但CPU消耗大,适合存储密集、查询较少的场景,建议用低级别(比如ZSTD(1))
三、对症下药!6个优化方案,CPU直接压回安全线
找到问题根源后,针对性优化就行。下面这6个方案,覆盖了大部分CPU飙高的场景,直接抄作业就行。
方案1:优化查询并发——解决“查询太多挤爆CPU”
如果是并发查询数超过CPU核心数导致的,直接限制并发数:
# 1. 临时调整(会话级别,重启后失效)
clickhouse-client --query "SET max_concurrent_queries=8" # 建议设为CPU核心数,比如16核就设16
# 2. 永久调整(修改config.xml配置文件,需要重启ClickHouse)<profiles>
<default>
<max_concurrent_queries</max_concurrent_queries>
<max_thread_pool_size></max_thread_pool_size> # 建议设为CPU核心数*</default></profiles>
另外,也可以给查询设置超时时间,避免慢查询一直占着资源:
clickhouse-client --query "SET max_execution_time=60" # 60秒超时,自动终止
方案2:优化慢查询——从根源减少CPU消耗
慢查询是CPU的“慢性毒药”,优化SQL比调配置更有效。分享几个实用的优化技巧:
- 1. 用EXPLAIN分析执行计划,看是否有全表扫描:
clickhouse-client --query "EXPLAIN SELECT * FROM 你的表 WHERE 你的条件" - 2. 给ORDER BY字段加索引(ClickHouse的主键就是索引),避免全表扫描
- 3. 用PREWHERE替代WHERE,减少早期数据读取(比如过滤大字段时)
- 4. 不要用SELECT *,只查需要的字段
- 5. 合理用LIMIT限制返回结果行数
方案3:优化合并任务——避开业务高峰期
如果合并任务太多,可通过配置调整合并策略,避免在业务高峰期抢占CPU:
# 修改config.xml中的merge_tree<merge_tree><max_bytes_to_merge_at_max_space_in_pool>16106127360</max_bytes_to_merge_at_max_space_in_pool> # 150GB,单次合并的最大<background_pool_size>16</background_pool_size> # 后台合并线程池大小</merge_tree>
注意:尽量不要在业务高峰期执行OPTIMIZE TABLE手动合并,会瞬间拉满CPU。
方案4:清理卡住的Mutation任务
如果发现Mutation任务卡住,先看latest_fail_reason找原因,解决后还没好,就果断终止(谨慎操作,可能导致数据不一致,建议先备份):
# 终止指定的Mutation任务
clickhouse-client --query "KILL MUTATION WHERE database='库名' AND table='表名' AND mutation_id='任务ID'"
另外,尽量避免在高峰期执行大量DELETE/UPDATE操作,可改用分区级别的删除(DROP PARTITION),效率更高、CPU消耗更低。
方案5:优化压缩算法——平衡性能和存储
如果当前用的是ZSTD压缩,且查询频繁导致CPU高,建议换成LZ4:
- 1. 新建表时指定LZ4压缩:
CREATE TABLE 新表名 ( 字段1 类型, 字段2 类型 ) ENGINE = MergeTree() ORDER BY 主键字段 SETTINGS codec = LZ4; # 指定LZ4压缩 - 2. 现有表修改压缩算法(需要重建表,数据量大时谨慎):
ALTER TABLE 旧表名 MODIFY COLUMN 字段名 类型 CODEC(LZ4);
方案6:优化数据导入——避免“小批量、高频次”写入
小批量、高频次的INSERT会生成大量小分区,导致合并任务激增,CPU飙升。优化方案:
- 1. 批量插入:把多条INSERT合并成一条,减少分区生成: `– 错误写法:多次小批量插入 INSERT INTO 表 VALUES (1); INSERT INTO 表 VALUES (2);
— 正确写法:一次批量插入
INSERT INTO 表 VALUES (1), (2), (3)…;
2. 降低插入线程数:clickhouse-client –query “SET max_insert_threads=4”
3. 用异步插入(适合高并发写入场景):INSERT INTO 表 ASYNC VALUES (…)
`
四、长效监控!一键运行的监控脚本
与其等CPU飙高后再排查,不如提前监控预警。下面这个脚本,整合了前面所有核心检查项,一键运行就能输出完整监控报告,建议加到定时任务里(比如每分钟跑一次)。
#!/bin/bash
# ClickHouse CPU监控脚本(自行适配生产环境)
# 若clickhouse-client不在PATH中,手动修改下面的路径,比如:CLICKHOUSE_CLIENT="/usr/bin/clickhouse-client"
CLICKHOUSE_CLIENT="clickhouse-client"
# 检查clickhouse-client是否可用
check_clickhouse_client() {
if ! command -v $CLICKHOUSE_CLIENT &> /dev/null; then
echo "⚠️ 警告:未找到 $CLICKHOUSE_CLIENT 命令,ClickHouse相关查询功能无法执行!"
return 1
fi
return 0
}
# 初始化检查
check_clickhouse_client
CLICKHOUSE_AVAILABLE=$?
# 输出监控报告
echo "=================================================="
echo "📊 ClickHouse CPU 监控报告 [$(date +%Y-%m-%d\ %H:%M:%S)]"
echo "=================================================="
echo ""
# 1. 系统CPU使用率
echo "1. 系统CPU使用率:"
top -bn1 | grep "Cpu(s)" | awk '{print " 用户空间: " $2 "% | 内核空间: " $4 "% | 空闲: " $8 "%"}'
echo ""
# 2. ClickHouse进程资源占用
echo "2. ClickHouse进程资源:"
CH_PID=$(pgrep clickhouse-server)
if [ -n "$CH_PID" ]; then
top -bn1 -p $CH_PID | tail -1 | awk '{print " PID: " $1 " | CPU使用率: " $9 "% | 内存使用率: " $10 "%"}'
else
echo " ❌ ClickHouse进程未运行!"
fi
echo ""
# 3. 当前并发查询数
echo "3. 当前并发查询数:"
if [ $CLICKHOUSE_AVAILABLE -eq 0 ]; then
$CLICKHOUSE_CLIENT --query "SELECT count() as concurrent_queries FROM system.processes WHERE query != ''"
else
echo " ❌ 无法获取:clickhouse-client命令不可用"
fi
echo ""
# 4. 活跃后台任务数
echo "4. 活跃后台任务数:"
if [ $CLICKHOUSE_AVAILABLE -eq 0 ]; then
$CLICKHOUSE_CLIENT --query "
SELECT '合并任务(Merges)' as task_type, count() as active_count FROM system.merges
UNION ALL
SELECT 'Mutation任务' as task_type, count() as active_count FROM system.mutations WHERE is_done=0
"
else
echo " ❌ 无法获取:clickhouse-client命令不可用"
fi
echo ""
# 5. 最近1小时慢查询数
echo "5. 最近1小时慢查询数(执行时间>1秒):"
if [ $CLICKHOUSE_AVAILABLE -eq 0 ]; then
$CLICKHOUSE_CLIENT --query "
SELECT count() as slow_queries FROM system.query_log
WHERE type=2 AND event_time > now() - INTERVAL 1 HOUR AND query_duration_ms > 1000
"
else
echo " ❌ 无法获取:clickhouse-client命令不可用"
fi
echo "=================================================="
echo "📋 监控结束"
echo "=================================================="
使用方法:
# 1. 保存脚本(比如叫clickhouse_cpu_monitor.sh)
# 2. 赋予执行权限
chmod +x clickhouse_cpu_monitor.sh
# 3. 直接运行
./clickhouse_cpu_monitor.sh
# 4. 添加定时任务(每分钟执行一次,输出到日志)
crontab -e
# 加入下面这行(日志路径可自行修改)
* * * * * /path/to/clickhouse_cpu_monitor.sh >> /var/log/clickhouse_cpu_monitor.log 2>&1
五、最后总结:运维必备的3个核心原则
- 1. CPU飙高先“止血”:优先杀慢查询、限制并发,再排查根源,避免业务崩溃
- 2. 90%的问题在查询:先优化SQL和并发,再调配置,不要本末倒置
- 3. 长效监控是关键:用上面的脚本定时监控,提前预警,比出问题后救火更省心
按照这套流程操作,大部分ClickHouse CPU飙高的问题都能在30分钟内解决。如果遇到特殊场景搞不定,也可以留言讨论~
最后,给大家整理了一份排查清单,收藏起来备用:
- • ✅ 检查系统CPU使用率(top/htop)
- • ✅ 检查当前运行的查询(system.processes)
- • ✅ 检查历史慢查询(system.query_log)
- • ✅ 检查后台任务(system.merges、system.mutations)
- • ✅ 检查表分区和压缩算法(system.parts)
- • ✅ 调整并发和查询超时配置
- • ✅ 部署监控脚本,定时预警
📬 关注我
推荐阅读
Redis主从复制深度解析:数据高可用与负载均衡的核心方案
运维必备|Zabbix 从 0 到 1 搭建企业级监控,告警自动喊你处理!
15分钟搞定业务宕机!运维必备排查指南(附实操命令)
SCP 与 rsync 到底怎么选?运维老鸟的文件传输避坑指南
效率拉满!Docker+Nginx 一站式部署 Java(JAR/WAR 通用),运维再也不加班
别再搞混Nginx和OpenResty!90%运维都踩过的坑,一文讲透核心差异
开发运维必备神器!HexHub 一站式搞定数据库、SSH、Docker 所有需求
网络排查神器!掌握 tcpdump,让网络故障无处遁形
MySQL 与 PostgreSQL:两个老对手的技术对决与选型指南
高性能存储刚需党必看!Docker 部署 RustFS,效率直接拉满
别再用第三方短链了!这个开源神器3分钟搭建专属短网址平台
Linux服务器重启后服务不自启?systemd实战指南 + 混沌演练验证
502 Bad Gateway 不是终点:一次生产事故背后的全链路复盘
备份做了,但能恢复吗?MySQL 数据恢复终极指南来了!
Firewalld 实战全攻略:从入门到精通,搭配 ipset 打造高效防护体系!
命令行也能玩转 WebSocket?别再用浏览器调了
MySQL 自动化备份脚本:安全、高效、免维护
Docker磁盘空间告急?3分钟教你彻底清理,释放大量空间!
Nginx 如何正确代理 SSE 与 WebSocket?一篇讲透长连接配置
【实战】打造超强Linux防火墙!10分钟提升服务器安全等级
一个不存在的用户,竟让MySQL 8.4当场崩溃?背后藏着甲骨文不敢明说的安全暗战!
无公网IP!NPS内网穿透终极指南,Docker一键部署
告别 Docker Hub 依赖!从零部署高可用 Harbor 私有镜像仓库
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:小柳实验室 小柳实验室《救命!ClickHouse CPU飙满了?运维手把手排查指南》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论