这是《ClickHouse 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 故障排查要先确定故障域:客户端、网络、ClickHouse 服务、存储、Keeper、上游写入还是查询本身。本章给出一套可复用的排查框架。
28.1 排查框架
1. 确认影响面:单查询、单表、单节点还是集群
2. 确认时间线:开始时间、变更、流量峰值
3. 检查服务存活和端口
4. 检查资源:CPU、内存、磁盘、网络
5. 检查 system.processes
6. 检查 query_log / part_log / error log
7. 检查副本和 Keeper
8. 采取止血动作
9. 事后修复根因
28.2 服务不可用
检查:
curl -sS 'http://ch-01:8123/ping'
systemctl status clickhouse-server
ss -lntp | grep -E '8123|9000'
常见原因:
| 原因 | 特征 |
|---|---|
| 磁盘满 | 启动或写入失败 |
| 配置错误 | 启动直接退出 |
| 内存不足 | 进程退出或系统 OOM |
| 端口冲突 | 监听失败 |
| 依赖 Keeper 不可达 | 副本只读或 DDL 失败 |
| DNS 解析失败 | 远程节点连接异常 |
28.3 查询超时
运行中查询:
SELECT
query_id,
elapsed,
read_rows,
read_bytes,
memory_usage,
query
FROM system.processes
ORDER BY elapsed DESC;
止血:
KILL QUERY WHERE query_id = 'target-query-id';
随后检查:
- 是否分区未过滤;
- 是否扫描超大列;
- 是否高基数聚合;
- 是否大 JOIN;
- 是否被合并或写入争抢 IO;
- 是否分布式某分片慢。
28.4 内存不足
错误常见为 MEMORY_LIMIT_EXCEEDED。
排查:
SELECT query_id, query, memory_usage, read_bytes
FROM system.query_log
WHERE exception LIKE '%MEMORY_LIMIT%'
ORDER BY event_time DESC
LIMIT 20;
处理:
- 增加过滤条件;
- 减少读取列;
- 降低分组基数;
- 使用近似聚合;
- 预聚合;
- 调整 max_memory_usage;
- 增加机器内存;
- 检查并发队列。
28.5 Too many parts
查看:
SELECT table, partition, count() AS parts, sum(rows) AS rows
FROM system.parts
WHERE active
GROUP BY table, partition
ORDER BY parts DESC
LIMIT 20;
止血:
- 暂停非关键写入;
- 降低补数并发;
- 增大批次间隔;
- 处理磁盘 IO 瓶颈;
- 必要时限制查询流量。
根因修复是调整写入频率、分区粒度或容量,而不是频繁执行 OPTIMIZE。
28.6 磁盘满
SELECT name, path, total_space, free_space
FROM system.disks
ORDER BY free_space;
处理顺序:
- 停止或限流写入;
- 检查异常大表和临时文件;
- 确认 TTL 是否生效;
- 删除可安全删除的过期分区;
- 扩容磁盘;
- 恢复写入并观察合并。
不要手工删除数据目录中的 part,除非有明确恢复方案。
28.7 副本异常
SELECT
database,
table,
replica_name,
is_readonly,
queue_size,
absolute_delay,
last_queue_exception
FROM system.replicas;
处理:
| 现象 | 方向 |
|---|---|
| readonly | Keeper 连接和元数据 |
| queue 增长 | 网络、磁盘、源副本 |
| delay 高 | 同步落后 |
| part missing | 从健康副本拉取或恢复 |
| metadata mismatch | 表结构一致性 |
28.8 Kafka 无数据
排查:
SELECT database, table, consumer_id, assignments.topic
FROM system.kafka_consumers;
检查:
- topic 是否正确;
- group offset 是否越界;
- 消息格式是否匹配;
- 物化视图是否存在;
- 目标表是否有异常;
- 是否坏消息被跳过;
- 消费者是否停止。
28.9 数据不一致
对账:
SELECT count(), sum(amount)
FROM analytics.events_local
WHERE event_date = '2026-08-25';
SELECT sum(pv), sum(gmv)
FROM analytics.city_metric_local
WHERE metric_date = '2026-08-25';
差异来源:
- 物化视图创建时间;
- 历史回填重复;
- 过滤条件不同;
- 类型转换;
- 时区口径;
- 延迟数据;
- 消息丢失或重复。
28.10 应急预案
必备预案:
| 故障 | 动作 |
|---|---|
| 恶意大查询 | kill、限流、降并发 |
| 磁盘满 | 停写、清理、扩容 |
| Too many parts | 限流写入、降合并压力 |
| Keeper 多数派失效 | 停拓扑变更、恢复 Quorum |
| 单节点故障 | 摘除流量、副本恢复 |
| 数据误删 | 停写、备份恢复 |
| Kafka 积压 | 降级、重放、扩消费者 |
本章小结
故障排查要先收敛影响面,再用系统表和日志定位资源或语义问题。止血动作要有限制和记录,根因修复要回到写入模型、表设计、容量规划和监控告警。
思考题
- 查询超时的第一排查动作是什么?
- MEMORY_LIMIT_EXCEEDED 如何处理?
- 为什么不能频繁 OPTIMIZE 解决 Too many parts?
- 副本 readonly 常见原因有哪些?
- 数据不一致应从哪些口径入手?