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');

示例仅说明语义,目标磁盘和权限需提前配置。生产恢复应在隔离库或隔离集群进行,验证行数、分区、采样聚合、权限、字典和查询性能后再切换。

副本并不是历史备份;对象存储上的共享数据也不意味着元数据、配置和删除事故自动可恢复。

十二、磁盘满与只读故障

处置顺序:

  1. 停止非必要写入、Mutation 和大查询;
  2. 识别占用来自 Active Part、Detached Part、临时文件、日志还是备份;
  3. 检查每块磁盘和 Storage Policy;
  4. 不要直接在数据目录手工删除未知文件;
  5. 通过表、分区和受支持命令释放空间;
  6. 恢复后观察 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
Copyright © yan-jian.com 2023 - 2026 · Powered by GitBook更新时间: 2026-11-03 16:23:43

results matching ""

    No results matching ""