ClickHouse 生产集群:副本、分片、备份恢复与性能治理
ClickHouse 擅长大规模分析查询,但“查询很快”不等于“集群天然可靠”。生产稳定性取决于表引擎、分区键、排序键、Shard/Replica 拓扑、Keeper、后台 Merge、数据写入批次、磁盘布局、备份和查询资源治理。本文给出从架构到排障的完整运维框架。
ClickHouse 的
DROP、TRUNCATE、分区操作、Mutation、强制副本恢复和 Keeper 操作都可能造成大范围影响。生产变更前必须确认表引擎、复制路径、分片范围、备份可恢复性和回滚方案。
一、先区分三个概念
- 分片 Shard:横向拆分数据,提高容量和并行度;
- 副本 Replica:保存同一分片的冗余副本,提高可用性;
- Distributed 表:查询或写入路由层,本身通常不存储业务数据。
客户端 / 负载均衡
↓
Distributed 表
├─ Shard 1:Replica A / Replica B
└─ Shard 2:Replica A / Replica B
↓
ClickHouse Keeper(复制协调元数据)
双副本不能替代备份。错误删除、错误 Mutation、应用写入脏数据可能同步到所有副本。
二、容量与拓扑规划
规划时同时计算原始数据、压缩后数据、主键索引、跳数索引、临时空间、Merge 放大、Mutation、备份和故障重建空间。磁盘长期超过 80% 后,Merge、复制和临时文件会互相挤压,容易出现连锁故障。
故障域建议:
- 同一分片的副本分布到不同宿主机、机架或可用区;
- Keeper 使用奇数节点并避免与所有数据副本共享同一故障域;
- 避免所有副本共用同一块远端存储而误以为具备节点级容灾;
- 负载均衡要具备健康检查,并了解读写一致性要求。
三、MergeTree 表设计
表设计的核心不是“字段能否存进去”,而是数据如何排序、裁剪和合并。
CREATE TABLE events_local
(
event_time DateTime,
tenant_id UInt32,
user_id UInt64,
event_type LowCardinality(String),
payload String
)
ENGINE = ReplicatedMergeTree(
'/clickhouse/tables/{shard}/events', '{replica}'
)
PARTITION BY toYYYYMM(event_time)
ORDER BY (tenant_id, event_type, event_time, user_id)
TTL event_time + INTERVAL 365 DAY DELETE;
设计原则:
ORDER BY决定物理排序和主键稀疏索引效果;- 常用等值过滤字段通常放在排序键前部,但要结合基数和查询模式;
- 分区主要用于数据生命周期和分区级操作,不宜按用户等高基数字段建立海量分区;
- TTL 会触发后台处理,不应在同一天集中删除巨量历史数据;
Nullable、过多 String 和高基数低收益索引会增加资源开销。
四、写入路径与小批次问题
ClickHouse 更适合批量插入。大量单行或很小批次会产生过多 Part,导致 Merge 压力、文件句柄和 Keeper 元数据增加。
观察:
SELECT database, table, count() AS active_parts,
sum(rows) AS rows, formatReadableSize(sum(bytes_on_disk)) AS size
FROM system.parts
WHERE active
GROUP BY database, table
ORDER BY active_parts DESC;
优化方式:应用批量写入、消息队列缓冲、异步插入或合理调整写入并发。不要通过无限提高 Part 相关阈值掩盖上游错误写入模式。
五、复制队列与副本健康
SELECT database, table, is_leader, is_readonly, is_session_expired,
queue_size, inserts_in_queue, merges_in_queue,
absolute_delay, total_replicas, active_replicas
FROM system.replicas
ORDER BY absolute_delay DESC;
SELECT database, table, replica_name, type, create_time,
num_tries, last_exception
FROM system.replication_queue
ORDER BY create_time;
queue_size 增长可能来自网络、磁盘、Keeper、Merge、源副本不可用或数据校验失败。不能只执行 SYSTEM RESTART REPLICA;应先判断队列项目类型和 last_exception。
六、ClickHouse Keeper 运维
Keeper 保存复制协调信息,不保存表的全部业务数据,但其不可用会影响复制表的写入和协调操作。生产需要:
- 奇数节点和独立故障域;
- 稳定低延迟磁盘与网络;
- 监控会话、延迟、选主、日志和快照;
- 限制非授权访问;
- 定期验证配置与恢复过程;
- 变更时逐节点进行,保留多数派。
时间漂移、长 GC/调度停顿、磁盘 fsync 抖动都可能导致会话过期。不要在多数派已经不稳时同时重启多个节点。
七、Distributed 表与写入一致性
Distributed 表可按分片键路由。分片键选择不当会造成热点或查询散射。
CREATE TABLE events_all AS events_local
ENGINE = Distributed(
production_cluster,
analytics,
events_local,
cityHash64(tenant_id)
);
需要理解:
- 写入本地表还是 Distributed 表;
- 是否启用内部异步队列;
- 目标分片不可用时数据放在哪里;
- 重试是否可能重复写入;
- 查询是否必须访问所有分片;
- 副本选择策略和延迟副本处理。
业务主键唯一并不是 MergeTree 默认提供的全局强约束。ReplacingMergeTree 等引擎也常体现“最终去重”,不能被误用成事务数据库唯一键。
八、Merge、Mutation 与资源竞争
后台 Merge 将小 Part 合并为大 Part。Mutation 修改历史数据,可能重写大量 Part。常见事故是白天执行大范围 ALTER TABLE ... UPDATE/DELETE,导致磁盘、CPU 和 IO 被长期占满。
SELECT database, table, mutation_id, command, create_time,
parts_to_do, is_done, latest_fail_reason
FROM system.mutations
WHERE NOT is_done;
SELECT database, table, elapsed, progress,
num_parts, total_size_bytes_compressed
FROM system.merges;
大规模修正优先考虑新表重算、分区替换、分批执行或业务层补偿。执行前估算受影响 Part、临时空间和完成时间。
九、查询性能分析
排查不要从“加机器”开始。先检查查询日志、读取行数、读取字节、内存、线程数和是否利用排序键。
SELECT event_time, query_duration_ms, read_rows,
formatReadableSize(read_bytes) AS read_size,
formatReadableSize(memory_usage) AS memory,
query
FROM system.query_log
WHERE type = 'QueryFinish'
ORDER BY query_duration_ms DESC
LIMIT 20;
重点问题:
- WHERE 条件没有命中排序键前缀;
- 使用
SELECT *读取大量无用列; - JOIN 右表过大或基数爆炸;
- 高基数
GROUP BY内存不足; - FINAL 被滥用;
- 大范围精确去重;
- 并发查询互相挤占内存和 CPU;
- 跳数索引选择性不足。
使用 EXPLAIN indexes = 1 检查裁剪情况,并结合 system.query_log 的真实读取量验证。
十、资源治理
生产应使用用户、角色、Profile 和 Quota 区分在线查询、报表、ETL、运维和备份。至少限制:
- 单查询内存;
- 最大执行时间;
- 最大读取行数/字节数;
- 并发查询数;
- 外部排序和聚合阈值;
- 只读账号的写权限;
- 运维 DDL 的审批与审计。
“全员共用 default 超级账号”会让误操作、审计和容量治理全部失效。
十一、备份与恢复
备份需要覆盖表结构、数据、字典、用户权限、配置、Keeper 关键配置和外部依赖。可以使用 ClickHouse 原生 BACKUP/RESTORE 对接支持的目标,也可采用经过验证的备份工具。
BACKUP DATABASE analytics
TO Disk('backups', 'analytics-2026-11-03.zip');
RESTORE DATABASE analytics AS analytics_restore
FROM Disk('backups', 'analytics-2026-11-03.zip');
示例仅说明语义,目标磁盘和权限需提前配置。生产恢复应在隔离库或隔离集群进行,验证行数、分区、采样聚合、权限、字典和查询性能后再切换。
副本并不是历史备份;对象存储上的共享数据也不意味着元数据、配置和删除事故自动可恢复。
十二、磁盘满与只读故障
处置顺序:
- 停止非必要写入、Mutation 和大查询;
- 识别占用来自 Active Part、Detached Part、临时文件、日志还是备份;
- 检查每块磁盘和 Storage Policy;
- 不要直接在数据目录手工删除未知文件;
- 通过表、分区和受支持命令释放空间;
- 恢复后观察 Merge、复制队列和磁盘增长。
若集群进入只读,先检查 Keeper 会话、磁盘和副本状态。盲目重启可能让问题从单副本扩散到整个分片。
十三、监控告警基线
至少监控:查询延迟与错误率、并发与排队、读取/写入吞吐、内存峰值、CPU steal、磁盘利用率与延迟、Active Part 数、Merge 和 Mutation、复制队列、延迟副本、Keeper 会话、分布式写队列、备份新鲜度和恢复演练结果。
告警必须能定位集群、分片、副本、表和查询 ID。建议保留慢查询样本和最近变更记录,避免只看到“CPU 100%”却不知道是哪类工作负载造成。
十四、上线检查清单
- 表的分区键、排序键和 TTL 经过真实数据验证;
- 同一分片副本跨故障域;
- Keeper 保持奇数多数派并已监控;
- 写入批次与重试语义明确;
- 用户、角色、限额和审计已配置;
- 磁盘保留 Merge、Mutation 和恢复余量;
- 备份完成过真实恢复;
- 故障切换、单副本重建和 Keeper 节点维护均有演练。
参考资料
- ClickHouse Documentation:
https://clickhouse.com/docs/ - Replicated table engines:
https://clickhouse.com/docs/engines/table-engines/mergetree-family/replication - System tables:
https://clickhouse.com/docs/operations/system-tables/ - Backup and restore:
https://clickhouse.com/docs/operations/backup