集群数据迁移(原生 SQL 方案)
本方案是「ClickHouse 集群数据迁移」系列方案之一,不依赖任何额外工具,仅使用 ClickHouse 原生的 remote() 跨集群读取与 Distributed 分布式表能力,通过纯 SQL 完成源集群到目标集群的数据迁移。方案以逐表、逐步执行并即时校验的方式推进,适用于数据表数量有限、临时或一次性的迁移场景;同时支持源与目标集群拓扑不一致(如分片数不同)时的自动重分片,也可用于拓扑相同的场景。
本文以一次真实迁移案例贯穿说明——源集群为 4 分片、目标集群为 3 分片,涵盖大表、小表、空表、未分布式化表等生产环境常见的表类型,可参照案例中的步骤与校验方法适配到自己的迁移场景。若数据量较大、数据表较多且可接受部署专用工具以换取高并行与断点续传能力,可参考集群数据迁移(迁移工具方案)。
迁移场景描述
- 源集群 SRC(集群 1)拓扑 = 4 分片 × 2 副本,版本 26.xx.xx.xx。
- 目标集群 DST(集群 2)拓扑 = 3 分片 × 2 副本,版本 26.xx.xx.xx。
- clickhouse-copier 在高版本(>=v26.2)已不随发行包提供(两集群 core 上均无该二进制,已被官方移除)。因此本方案采用官方推荐的等价替代:在目标端建 Distributed 表 + 通过 remote() 跨集群 INSERT ... SELECT,由目标 Distributed 表按 cityHash64(run_id) 自动把数据从 4 分片重分片到 3 分片,效果等同 copier。
- 连接账号:admin / 密码见集群 config(下文命令用 $PW 占位,执行时替换)。
- 已验证:DST→SRC 的 remote() 跨集群读取连通(
SELECT count() FROM remote('{src-core-ip}', default.run_metrics, ...)正常返回)。
拓扑与入口
- SRC 读入口(任一 core 均可,Distributed 会扇出全部 4 分片)。
- DST 写入口(任一 core 均可)。
- SRC 和 DST 的 Keeper:已确认健康。
待迁移对象(SRC 需迁移 default 库)
生产环境常见的迁移场景中,假设 default 库下存在以下几类表:
- 大表
- 小表
- 无数据表
- 未分区表
- 未分布式化表
每种类型表都有各自的处理策略,可以涵盖大多数线上生产场景。本案例假设待迁移的表如下:
| 表 | 本地表引擎 | 分布表 sharding key | 规模(逻辑) |
|---|---|---|---|
| metrics / metrics_local | ReplicatedReplacingMergeTree(version) | cityHash64(run_id) | 116 亿行 / ~139 GiB,全部在单分区 202608 |
| run_metrics / run_metrics_local | ReplicatedReplacingMergeTree(version) | cityHash64(run_id) | 116 万行 |
| metric_histograms / metric_histograms_local | ReplicatedReplacingMergeTree(version) | cityHash64(run_id) | 0 行(仅建表) |
| mut_test_local | ReplicatedMergeTree(无分布表) | 需新建临时分布表 | ~9.9 万行 |
说明:metrics_local 全部数据集中在单一分区 202608,无法按分区切分,采用 cityHash64(run_id) % N 哈希分桶切分。
步骤总览
每步做完后必须执行"验证"再进入下一步:
- S1 迁移前置检查
- S2 在 DST 建表(本地表 + 分布表,ON CLUSTER)
- S3 迁移空表 metric_histograms(验证建表链路)
- S4 迁移小表 run_metrics
- S5 迁移 mut_test(含临时分布表)
- S6 迁移大表 metrics(哈希分桶,逐桶验证)
- S7 全量一致性校验
- S8 清理临时对象
S1. 迁移前置检查
在 SRC 与 DST 各任一 core 执行:
1SELECT version(); -- 两端大版本应一致(26.xx.xx)
2SELECT shard_num,replica_num,host_name FROM system.clusters WHERE cluster='default' ORDER BY shard_num,replica_num;
3SELECT count() FROM system.zookeeper WHERE path='/'; -- Keeper 可读
DST 上验证跨集群连通:
1SELECT * FROM remote('{src-core-ip}', system.one, 'admin', '$PW');
验证通过标准:两端版本一致;DST=3 分片、SRC=4 分片;remote() 返回 0(无异常)。
S2. 在 DST 建表(ON CLUSTER 'default')
说明:本地表 ZK 路径含 {shard}/{replica} 宏,ON CLUSTER 会在各节点用正确宏创建;分布表用 DST 的 default 集群。
1-- metrics
2CREATE TABLE default.metrics_local ON CLUSTER 'default' (<与源同列定义>)
3ENGINE=ReplicatedReplacingMergeTree('/clickhouse/tables/{shard}/default/metrics_local','{replica}',version)
4PARTITION BY toYYYYMM(ts) ORDER BY (tenant_id,entity,project_id,run_id,metric_name,step) SETTINGS index_granularity=8192;
5CREATE TABLE default.metrics ON CLUSTER 'default' AS default.metrics_local
6ENGINE=Distributed('default','default','metrics_local',cityHash64(run_id));
7-- run_metrics / metric_histograms 同理(列定义与源 SHOW CREATE 完全一致)
验证:SELECT count() FROM system.tables WHERE database='default' 在每个 core 上应见到对应本地表 + 分布表;SELECT count() FROM default.<dist> 返回 0。
S3. 迁移空表 metric_histograms(先验链路)
1INSERT INTO default.metric_histograms
2SELECT * FROM remote('{src-core-ip}', default.metric_histograms, 'admin', '$PW');
验证:DST/SRC count() 均为 0。
S4. 迁移小表 run_metrics
1INSERT INTO default.run_metrics
2SELECT * FROM remote('{src-core-ip}', default.run_metrics, 'admin', '$PW');
验证:SELECT count() FROM default.run_metrics,DST 应 = SRC(1160050)。再比对聚合校验:SELECT count(), uniqExact(run_id), max(updated_at) FROM default.run_metrics 两端一致。
S5. 迁移 mut_test(源无分布表,建临时分布表中转)
在 SRC 建临时分布表:
1CREATE TABLE default.mut_test_dist ON CLUSTER 'default' AS default.mut_test_local
2ENGINE=Distributed('default','default','mut_test_local', cityHash64(id));
在 DST 建本地表 + 分布表:
1CREATE TABLE default.mut_test_local ON CLUSTER 'default' (`id` UInt64,`v` UInt64)
2ENGINE=ReplicatedMergeTree('/clickhouse/tables/{shard}/default/mut_test_local','{replica}') ORDER BY id SETTINGS index_granularity=8192;
3CREATE TABLE default.mut_test ON CLUSTER 'default' AS default.mut_test_local
4ENGINE=Distributed('default','default','mut_test_local', cityHash64(id));
5INSERT INTO default.mut_test
6SELECT * FROM remote('{src-core-ip}', default.mut_test_dist, 'admin', '$PW');
验证:SELECT count(), sum(id), sum(v) FROM default.mut_test 两端一致(源用 mut_test_dist 读)。
S6. 迁移大表 metrics(哈希分桶,逐桶迁移 + 验证)
分 N=16 桶(可调),逐桶执行并核对:
1-- 对 k = 0..15:
2INSERT INTO default.metrics
3SELECT * FROM remote('{src-core-ip}', default.metrics, 'admin', '$PW')
4WHERE cityHash64(run_id) % 16 = k
5SETTINGS max_execution_time=0, max_insert_threads=4, network_compression_method='lz4';
注意:向 Distributed 表 INSERT 默认是异步转发(distributed_foreground_insert=0),插入返回后数据可能仍在分发缓冲区。校验计数前必须执行
SYSTEM FLUSH DISTRIBUTED default.<dist>,或在插入时设置SETTINGS distributed_foreground_insert=1(本次 mut_test 验证已踩此坑并确认)。 逐桶验证:迁移每个桶后比对该桶行数。
1-- DST
2SELECT count() FROM default.metrics WHERE cityHash64(run_id)%16=k;
3-- SRC(经 remote 或直接在源执行)
4SELECT count() FROM default.metrics WHERE cityHash64(run_id)%16=k;
说明:ReplacingMergeTree 后台 merge 可能对重复主键去重导致 DST 计数偏小。若源无重复主键则计数应严格相等;如出现偏差,用 ... FINAL 或按主键聚合复核,确认是去重而非丢数。
S7. 全量一致性校验(对每张表)
1SELECT count() AS c, uniqExact(run_id) AS u FROM default.<表>; -- DST 与 SRC 比对
2-- metrics 额外核对数值:SELECT count(), sum(value), min(ts), max(ts) FROM default.metrics;
验证通过标准:各表 count()/uniqExact/聚合值两端一致(metrics 计数偏差需经 FINAL 复核解释为去重)。
S8. 清理临时对象
1-- SRC
2DROP TABLE IF EXISTS default.mut_test_dist ON CLUSTER 'default';
回滚 / 安全
- 全过程只对 DST(目标空集群) 写入,源集群只读;任何步骤出错可在 DST
TRUNCATE TABLE default.<local> ON CLUSTER 'default'或 DROP TABLE 重来。 - 密码属敏感信息,命令中以 $PW 占位,勿在日志/记录中回显明文。
附录:本次实际验证结果(已在两真实集群实跑)
- S1 前置:SRC=4 分片、DST=3 分片;DST 已开机(6 core 全部 up);Keeper 健康;跨集群 remote() 连通 OK。
- S2 建表:6 张表(metrics/metric_histograms/run_metrics 各含 local+dist)ON CLUSTER 全节点成功,初始 count=0。
- S3 metric_histograms:DST=0 = SRC=0 ✓
- S4 run_metrics:DST count=1160050 / uniqExact(run_id)=145001 / max(updated_at)=2026-08-06,与 SRC 完全一致 ✓;已确认重分片到 3 分片(388656/388186/383208)。
- S5 mut_test:DST count=99000 / sum(id)=4950000000 / sum(v)=4950082000,与 SRC 完全一致 ✓(踩坑:需 SYSTEM FLUSH DISTRIBUTED 后计数才对)。
- S6 metrics:按 cityHash64(run_id)%16 分 16 桶迁移,16:45→17:21 约 36 分钟完成;DST count=11601000000,与 SRC 完全一致 ✓;3 分片分布均衡(3.887B / 3.882B / 3.832B,各 ×2 副本)。
- S7 一致性:各表行数/去重/聚合两端一致。
- S8 清理:SRC 临时表 default.mut_test_dist 已 DROP ON CLUSTER;core 上临时脚本已删除。
结论:本方案有效,集群 1 全部业务数据已成功迁移并重分片到集群 2(4 分片 → 3 分片),校验一致。
评价此篇文章
