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 监听 Binlogpublic 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;性能对比
| 指标 | MySQL | ClickHouse |
|---|---|---|
| 单表数据量 | 1.5 亿 | 1.5 亿 |
| COUNT 查询 | 30s | 0.5s |
| 聚合查询 | 5-10s | <1s |
| 存储空间 | 200GB | 40GB |
总结
- 提前规划分区 - 按时间分区对分析查询友好
- 批量写入 - 减少网络开销
- 做好监控 - 同步延迟、数据完整性
- 灰度迁移 - 先迁移冷数据,再迁移热数据