571 words
3 minutes
亿级数据迁移实战:MySQL 到 ClickHouse

1.5亿条数据从 MySQL 迁移到 ClickHouse,踩过的坑和经验总结

背景#

业务需求:将订单历史数据从 MySQL 迁移到 ClickHouse,用于实时分析。

  • 数据量:1.5 亿+
  • 每日增量:50 万条
  • 查询需求:多维度聚合分析

方案对比#

方案优点缺点
阿里巴巴 DataX成熟稳定需要额外服务
Canal + Flink实时同步复杂度高
定时导出脚本简单可控需要停机窗口
ClickHouse JDBC实现简单大数据量慢
TIP

最终选择:先全量迁移,再通过 Binlog 实时同步增量

1. 表结构设计#

-- MySQL 表结构
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
amount DECIMAL(10,2),
status VARCHAR(20),
created_at DATETIME,
updated_at DATETIME
);
-- ClickHouse 表结构(物化视图 + MergeTree)
CREATE TABLE orders (
id UInt64,
user_id UInt64,
amount Decimal(10,2),
status String,
created_at DateTime,
updated_at DateTime,
_sign Int8 DEFAULT 1
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(created_at)
ORDER BY (user_id, created_at);

2. 全量迁移#

public void fullMigration() {
// 1. 导出 MySQL 数据
List<Orders> batch;
while ((batch = queryOrders(offset, batchSize)) != null) {
// 2. 写入 ClickHouse
clickHouseClient.insert("orders", batch);
offset += batchSize;
// 3. 每 10 万条提交一次
if (offset % 100000 == 0) {
log.info("已迁移 {} 条", offset);
}
}
}

3. 增量同步#

// 使用 Canal 监听 Binlog
public class BinlogListener implements EventListener {
@Override
public void onEvent(Event event) {
EventType type = event.getHeader().getEventType();
if (type == EventType.WRITE || type == EventType.UPDATE) {
// 写入 ClickHouse
Order order = parseOrder(event);
clickHouseClient.insert("orders", order);
} else if (type == EventType.DELETE) {
// 软删除(ClickHouse 不支持真正删除)
// 通过 _sign = -1 标记
}
}
}

4. 踩过的坑#

坑1:写入速度太慢#

// ❌ 单条写入,1秒只能写入几百条
for (Order order : orders) {
clickHouseClient.insert("orders", order);
}
// ✅ 批量写入,速度提升 100 倍
clickHouseClient.insertBatch("orders", orders);

坑2:分区规划不合理#

-- ❌ 按 ID 分区,数据不均匀
PARTITION BY id % 100
-- ✅ 按时间分区,查询友好
PARTITION BY toYYYYMM(created_at)

坑3:数据重复#

-- ✅ 使用 ReplacingMergeTree 自动去重
CREATE TABLE orders (
id UInt64,
...
) ENGINE = ReplacingMergeTree(updated_at)
ORDER BY id;
-- ⚠️ 定期手动去重
OPTIMIZE TABLE orders FINAL;

性能对比#

指标MySQLClickHouse
单表数据量1.5 亿1.5 亿
COUNT 查询30s0.5s
聚合查询5-10s<1s
存储空间200GB40GB

总结#

  1. 提前规划分区 - 按时间分区对分析查询友好
  2. 批量写入 - 减少网络开销
  3. 做好监控 - 同步延迟、数据完整性
  4. 灰度迁移 - 先迁移冷数据,再迁移热数据