集群扩容与升级:分片重平衡、平滑升级与滚动重启

ClickHouse 集群在数据量增长与业务迭代中必然面临扩容与升级两大运维命题。本文从分片与副本两个维度拆解扩容决策,详解 weight 调整、数据重平衡与新分片回填的完整流程,并给出从 24.3 LTS 升级到 25.3 的路径规划、向后不兼容变更的应对、滚动重启的副本切换细节,以及升级窗口内的查询写入影响评估、回滚方案与故障演练清单。

前置:/clickhouse-distributed-cluster/(分布式集群架构)、/clickhouse-replicated-tables-disaster-recovery/(复制表与容灾)、/clickhouse-monitoring-maintenance/(监控与日常维护)。

目录

1. 集群拓扑与扩容决策

扩容的第一步不是加机器,而是判断当前瓶颈究竟在存储、CPU 还是写入吞吐。下面这份 remote_servers 配置描述了一套 12 分片 3 副本的典型集群,扩容决策要围绕它的拓扑展开,可参考 /clickhouse-production-case/ 中的真实扩容案例。

<clickhouse>
    <remote_servers>
        <analytics_cluster>
            <shard>
                <weight>1</weight>
                <internal_replication>true</internal_replication>
                <replica><host>ch01-01</host><port>9000</port></replica>
                <replica><host>ch01-02</host><port>9000</port></replica>
                <replica><host>ch01-03</host><port>9000</port></replica>
            </shard>
            <shard>
                <weight>1</weight>
                <internal_replication>true</internal_replication>
                <replica><host>ch02-01</host><port>9000</port></replica>
                <replica><host>ch02-02</host><port>9000</port></replica>
                <replica><host>ch02-03</host><port>9000</port></replica>
            </shard>
            <!-- shard03 至 shard12 同构,合计 12 分片 3 副本 -->
        </analytics_cluster>
    </remote_servers>
</clickhouse>

判断扩容方向的三个核心信号:

  • 单分片磁盘使用率持续高于 70% 且压缩后仍增长,说明需要扩分片而非扩副本。
  • 写入延迟 P99 随并发上升而恶化,且 Merge 队列积压,优先考虑增加分片分摊写入。
  • 查询延迟高但 CPU 空闲,通常是副本不足或查询未并行化,扩副本收益更大。

容量水位可以直接用 SQL 量化,避免凭感觉决策:

SELECT
    hostName() AS host,
    formatReadableSize(sum(bytes_on_disk)) AS disk_used,
    round(100 * sum(bytes_on_disk) / 2000000000000, 1) AS pct_of_2tb
FROM system.parts
WHERE active AND database = 'analytics'
GROUP BY host
ORDER BY disk_used DESC;

工程要点:扩容决策要基于 system.clusters 与磁盘监控的量化数据,先确认瓶颈类型再决定扩分片还是扩副本;分片数变化必然伴随数据重分布,副本数变化只需补齐数据,成本相差一个数量级。

2. 分片与副本:扩容的两个维度

分片解决的是容量与写入吞吐的水平切分,副本解决的是可用性与读吞吐的冗余。二者在 ClickHouse 里是正交的:分片由 Distributed 表的路由逻辑决定,副本由 ReplicatedMergeTree 的 ZooKeeper 协调决定,扩容时必须分清自己动的是哪一维,可结合 /clickhouse-table-engines/ 理解引擎选型。

-- Distributed 表按分片键把写入路由到某个 shard 的某个 replica
CREATE TABLE analytics.events_dist ON CLUSTER analytics_cluster
(
    event_date Date,
    user_id UInt64,
    event_type LowCardinality(String),
    amount Decimal(18, 4)
)
ENGINE = Distributed('analytics_cluster', 'analytics', 'events_local', cityHash64(user_id));
-- 查看集群当前的分片副本布局,is_local 标记本地节点
SELECT cluster, shard_num, replica_num, host_name, is_local, weight
FROM system.clusters
WHERE cluster = 'analytics_cluster'
ORDER BY shard_num, replica_num;

两种扩容路径的对比:

  • 扩副本:只需把新节点加入 remote_servers 并启动 ReplicatedMergeTree,数据由复制队列自动补齐,不涉及数据重分布,风险低。
  • 扩分片:分片键的哈希空间被重新划分,老数据留在原分片,需要显式重平衡或重建表,风险高但容量收益直接。

两种路径在操作成本上的差异:

  • 扩副本通常只需改配置加节点,一小时内可完成,且可在线进行。
  • 扩分片涉及数据搬迁与业务切流,需要数小时到数天,且要安排维护窗口。

工程要点:扩副本是低风险的容量与读性能补充,扩分片才真正提升写入与存储上限;判断依据是单分片的磁盘水位与写入延迟,而不是整集群的平均值。

3. 数据重平衡与 weight 调整

修改 shard 的 weight 只改变 Distributed 表后续写入的路由概率,历史数据不会自动迁移。因此 weight 是止血手段,真正把老数据搬到新分片仍要靠 resharding 或分区级迁移,分片键的选取可参考 /clickhouse-schema-modeling-best-practices/。

<shard>
    <weight>2</weight>
    <internal_replication>true</internal_replication>
    <replica><host>ch01-01</host><port>9000</port></replica>
    <replica><host>ch01-02</host><port>9000</port></replica>
    <replica><host>ch01-03</host><port>9000</port></replica>
</shard>
<shard>
    <weight>2</weight>
    <internal_replication>true</internal_replication>
    <replica><host>ch13-01</host><port>9000</port></replica>
    <replica><host>ch13-02</host><port>9000</port></replica>
    <replica><host>ch13-03</host><port>9000</port></replica>
</shard>

配置改动后需要让节点重新加载:

SYSTEM RELOAD CONFIG;
SELECT shard_num, weight FROM system.clusters
WHERE cluster = 'analytics_cluster' ORDER BY shard_num;

weight 调整的三个约束:

  • 只影响写入路由,已写入数据仍留在原分片,读放大问题不会自动缓解。
  • 各 shard weight 之和决定概率分布,通常保持整数倍关系便于推算。
  • 副本之间不设 weight,同一分片内的副本数据必须完全一致。

工程要点:weight 是临时疏导写入热点的手段,不解决存储倾斜;生产上更稳妥的做法是配合分区迁移把热点分片的历史分区逐批搬到新分片,再回收 weight 差异。

4. 新分片接入与数据回填

把 12 分片扩到 18 分片,标准做法是新建一套 18 分片的集群配置,先建好空表,再用 INSERT SELECT 从老集群拉数据回填,校验行数与校验和后切换写入。单分片 2TB 的数据在 3 副本、万兆内网下通常需要数小时到十几小时,回填链路可参考 /clickhouse-data-ingestion/。

-- 1. 在新集群上建好本地表与分布式表
CREATE TABLE analytics.events_local ON CLUSTER analytics_cluster_new
AS analytics.events_local
ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/analytics/events', '{replica}')
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_date, user_id);
-- 2. 逐分片回填:按新分片键取模过滤后写入新集群
INSERT INTO analytics.events_local
SELECT * FROM cluster('analytics_cluster_old', 'analytics', 'events_local')
WHERE cityHash64(user_id) % 18 = 3;

-- 3. 校验行数与校验和,两侧必须完全一致
SELECT count(), sum(cityHash64(user_id, event_type)) AS chk
FROM cluster('analytics_cluster_old', 'analytics', 'events_local');
SELECT count(), sum(cityHash64(user_id, event_type)) AS chk
FROM analytics.events_local;

回填期间的四个要点:

  • 回填必须按新分片键取模过滤,否则数据会落错分片。
  • 老集群持续写入,回填完成后要补一轮增量,或短暂停写。
  • 单分片 2TB 回填建议限速,避免打满网络影响在线查询。
  • 切换写入前用 system.replicas 确认所有副本 queue_size 归零。

工程要点:resharding 的核心是新集群建表、按新分片键回填、行数与校验和双重比对、确认复制队列归零后切写;回填窗口内的增量数据要单独补齐,不能假设一次全量即可收工。

5. 版本升级路径与兼容性

ClickHouse 的升级必须逐 LTS 递进,24.3 LTS 不能直接跳到 25.3,中间需要经过 24.8。跨大版本升级前要逐条核对向后不兼容变更,尤其是设置项默认值的变化与表引擎行为的调整,性能回归可对照 /clickhouse-production-performance-tuning/ 的基线。

<clickhouse>
    <!-- 升级过渡期把兼容级别钉在旧版本,降低行为差异风险 -->
    <compatibility>24.3</compatibility>
    <!-- 去重窗口过小会导致重启后重复插入被误判为不重复 -->
    <replicated_deduplication_window>1000</replicated_deduplication_window>
    <replicated_deduplication_window_seconds>604800</replicated_deduplication_window_seconds>
</clickhouse>
SELECT version() AS current_version;
SELECT name, value, changed FROM system.settings WHERE name = 'compatibility';
SELECT name, value FROM system.merge_tree_settings
WHERE name LIKE 'replicated_deduplication%';

升级前必须核对的清单:

  • 24.3 到 24.8 到 25.3 的 LTS 递进路径,禁止跨版本直达。
  • compatibility 设置用于过渡期锁定旧行为,观察稳定后再解除。
  • 表引擎与数据类型是否命中废弃列表,如旧式语法与部分引擎参数。
  • 复制去重窗口在新版本中的默认值变化,避免重启后重复数据。

工程要点:升级路径严格遵循 LTS 递进,升级前用 system.settings 与 system.merge_tree_settings 对照官方不兼容清单逐项确认;compatibility 是过渡期安全带,但不能长期依赖。

6. 滚动重启流程与副本切换

滚动重启的本质是让同一分片内的副本轮流离线,任何时刻至少保留一个健康副本对外服务。重启前先确认复制延迟,重启后必须等待副本追平再动下一个节点,复制机制细节见 /clickhouse-replicated-tables-disaster-recovery/。

-- 重启前确认复制健康度,absolute_delay 是副本落后主库的秒数
SELECT database, table, is_leader, absolute_delay, queue_size, inserts_in_queue
FROM system.replicas
WHERE database = 'analytics'
ORDER BY absolute_delay DESC;
-- 单节点操作:重启前先让副本追平
SYSTEM SYNC REPLICA analytics.events_local;
SYSTEM RESTART REPLICA analytics.events_local;
#!/usr/bin/env bash
set -euo pipefail
for host in ch01-01 ch01-02 ch01-03; do
    ssh "$host" 'sudo systemctl restart clickhouse-server'
    until ssh "$host" "clickhouse-client -q \"SELECT count() FROM system.replicas WHERE absolute_delay > 60\" | grep -q '^0$'"; do
        sleep 10
    done
    echo "node $host replica caught up"
done

滚动重启的四个约束:

  • 同一分片内绝不允许同时重启两个副本,否则查询会大面积失败。
  • 重启前把 distributed_ddl_queue 清空,避免 DDL 在节点离线时丢失。
  • 重启后用 SYSTEM SYNC REPLICA 主动追平,不要只依赖后台调度。
  • 节点重新加入后检查 system.replicas 的 is_readonly 是否恢复为 0。

工程要点:滚动重启的节奏由复制延迟而非固定 sleep 决定,每重启一个节点都要等到副本追平、queue_size 归零再继续;分片内至少保留一个在线副本是不可逾越的底线。

7. 升级窗口的查询与写入影响

升级窗口内最容易被忽略的是分布式 DDL 与后台 Merge 的中断。DDL 在集群上是异步传播的,节点离线会导致任务卡在队列里;而重启期间正在进行的 Merge 会重新排队,短时间放大磁盘压力,相关指标见 /clickhouse-monitoring-maintenance/。

-- 升级前确认没有未完成的分布式 DDL
SELECT entry, host, status, query, exception_text
FROM system.distributed_ddl_queue
WHERE status != 'Finished'
ORDER BY query_create_time DESC
LIMIT 10;
-- 升级窗口内暂停重 Merge,降低磁盘与 CPU 竞争
SYSTEM STOP MERGES analytics.events_local;
-- 维护完成后恢复
SYSTEM START MERGES analytics.events_local;
SELECT database, table, elapsed, progress, num_parts
FROM system.merges WHERE database = 'analytics';

升级窗口对查询与写入的实际影响:

  • Distributed 表查询在副本离线期间会重试其他副本,max_replica_delay_for_distributed_queries 决定容忍的最大延迟。
  • 写入侧依赖 insert_deduplicate,去重窗口内的重试会被识别为重复块,窗口过小则可能写重。
  • 后台 Merge 暂停会累积未合并分区,恢复瞬间可能触发大量合并。
  • 物化视图的写入链路会随底层表的重启而短暂阻塞。

窗口内建议的观测指标:

  • system.replicas 的 absolute_delay 是否在容忍范围内。
  • system.distributed_ddl_queue 是否有长期处于排队状态的任务。
  • 客户端侧的错误率与 P99 写入延迟是否出现台阶式上升。

工程要点:升级窗口要避开业务高峰,提前排空 distributed_ddl_queue、暂停不必要的 Merge,并确认 insert_deduplicate 与副本延迟参数能覆盖重启期间的抖动。

8. 回滚方案与故障演练

回滚的前提是升级前保留了旧版本二进制与可用的备份。ClickHouse 不支持跨大版本的原路降级,一旦新版本写入了旧版本无法解析的数据格式,只能从备份恢复,因此回滚方案必须在升级前就演练通过,备份策略见 /clickhouse-backup-dr/。

clickhouse-server --version
cp /usr/bin/clickhouse /opt/clickhouse/clickhouse-24.3
tar czf /backup/clickhouse-data-$(date +%F).tar.gz /var/lib/clickhouse
sudo systemctl stop clickhouse-server
cp /opt/clickhouse/clickhouse-24.3 /usr/bin/clickhouse
sudo systemctl start clickhouse-server
-- 回滚前确认没有残留的 mutation 与复制任务
SELECT database, table, mutation_id, is_done, latest_fail_reason
FROM system.mutations WHERE is_done = 0;
SELECT database, table, type, num_tries, last_exception
FROM system.replication_queue WHERE num_tries > 3;

故障演练至少要覆盖三类场景:

  • 单副本重启失败:验证分片内其余副本能否独立支撑查询与写入。
  • 复制队列卡死:人为制造 ZooKeeper 会话超时,观察恢复流程。
  • 回滚到旧版本:在预发集群完整走一遍换二进制与数据恢复。

工程要点:回滚必须建立在升级前的二进制与备份双保险之上,且要在预发环境完整演练;发现异常时优先考虑先回滚再排查,不要在生产上边跑边修。

9. 生产实践与检查清单

把扩容与升级做成可重复的标准流程,关键在于把每一步的判断依据固化成可执行的检查项。下面这段监控 SQL 可以作为升级前后的一致性基线,合并行为可参考 /clickhouse-merge-tree-tuning/。

SELECT
    hostName() AS host,
    formatReadableSize(sum(bytes_on_disk)) AS disk_used,
    (SELECT count() FROM system.replicas WHERE absolute_delay > 300) AS lagging,
    (SELECT count() FROM system.merges) AS active_merges
FROM system.parts
WHERE active
GROUP BY host;
-- 集群维度的健康基线
SELECT cluster, count() AS nodes,
       countIf(is_local) AS local_nodes
FROM system.clusters
WHERE cluster LIKE 'analytics%'
GROUP BY cluster;

扩容与升级的统一检查清单:

  • 扩容前确认单分片磁盘水位与写入延迟,明确扩分片还是扩副本。
  • resharding 完成后行数与校验和双重比对,复制队列归零再切写。
  • 升级路径按 LTS 递进,逐项核对不兼容变更与设置默认值。
  • 滚动重启以复制延迟为节拍,分片内始终保留健康副本。
  • 升级前保留旧二进制与备份,回滚方案在预发演练通过。
  • 每次操作后留存 system.clusters 与 system.replicas 的快照,便于事后复盘。

工程要点:把每次扩容与升级的判断依据固化为监控基线与检查清单,让高风险操作变成有据可依的标准流程,而不是依赖个人经验的临场发挥。

10. 速查表与一句话记忆

下表汇总扩容与升级过程中最常用的命令与参数,可作为操作时的快速对照。

场景命令或参数注意事项
查看集群布局system.clusters关注 shard_num、replica_num 与 weight
调整写入分布remote_servers 的 weight只影响新写入,历史数据不迁移
回填新分片INSERT SELECT 配合 cluster 表函数按新分片键取模过滤,双重校验行数
复制健康检查system.replicas关注 absolute_delay 与 queue_size
追平副本SYSTEM SYNC REPLICA重启后主动执行,不等后台调度
暂停合并SYSTEM STOP MERGES维护窗口内使用,结束后及时恢复
锁定兼容行为compatibility 设置过渡期使用,稳定后解除
回滚准备保留旧二进制与备份跨大版本不支持原路降级

一句话记忆:扩分片重分布数据、扩副本只补数据,升级按 LTS 递进、重启以复制延迟为节拍,回滚靠二进制与备份双保险。

延伸阅读

  • /clickhouse-distributed-cluster/ — 分片副本与 ZooKeeper 协调的集群基础
  • /clickhouse-replicated-tables-disaster-recovery/ — 复制表原理与容灾切换
  • /clickhouse-monitoring-maintenance/ — 监控指标与日常维护操作
  • /clickhouse-backup-dr/ — 备份恢复与灾难恢复演练
  • /clickhouse-merge-tree-tuning/ — MergeTree 合并行为与参数调优
  • /clickhouse-production-case/ — 生产环境真实运维案例
  • /clickhouse-data-ingestion/ — 数据写入链路与批量导入
  • 数据库专题

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「数据库」更多文章

  1. 用户自定义函数:executable UDF、SQL UDF 与性能边界
  2. 内存管理与落盘:查询内存、spill to disk 与 OOM 防护
  3. 日志与指标存储:可观测性后端、Grafana 与成本治理