数据迁移与扩缩容方案
数据迁移的难点
分库分表实施过程中,数据迁移是最容易出问题的环节。核心难点在于:
- 数据一致性:迁移过程中的增量数据不能丢失
- 停机窗口:业务对迁移时间敏感
- 回滚方案:迁移失败后快速切回
- 数据校验:迁移完成后验证数据完整性
停机迁移方案
适用场景
业务允许短时间停机(一般在凌晨低峰期),数据量不大(百 GB 级别内)。
实施步骤
第 1 步:通知业务方,确认停机窗口(如凌晨 2:00-5:00)
第 2 步:停止应用写入(或切换为只读模式)
第 3 步:全量数据导出 + 导入到新表
第 4 步:数据校验(数量 + 关键字段 checksum)
第 5 步:切换数据源指向新库
第 6 步:应用启动,验证功能
第 7 步:确认无误后清理旧库
数据导出导入
# 从旧库导出
mysqldump -h old_host -u root -p db_name \
--where="user_id % 4 = 0" \
--no-create-info \
--complete-insert \
user_order > user_order_0.sql
# 导入到新分片
mysql -h new_host_0 -u root -p db_0 < user_order_0.sql
优缺点
优点:实现简单,数据一致性容易保证
缺点:需要停机,数据量大时迁移时间长
在线平滑迁移(双写方案)
适用场景
业务不能长时间停机,需要在线迁移大表数据。
实施步骤
第 1 阶段:双写搭建
// 应用层修改:写入时同时写入旧库和新库
public void saveOrder(Order order) {
oldOrderDao.save(order); // 旧库
newOrderDao.save(order); // 新分片(根据分片规则路由)
}
查询仍然使用旧库,避免查询性能受影响。
第 2 阶段:历史数据迁移
-- 分批迁移旧数据
-- 使用 LIMIT OFFSET 分批迁移,每批 10000 行
INSERT INTO new_db_0.order SELECT * FROM old_db.order
WHERE id BETWEEN 1 AND 10000;
// 每批迁移完成后校验
long oldCount = oldDao.countByIdRange(start, end);
long newCount = newDao.countByOldIdRange(start, end);
if (oldCount != newCount) {
// 重新迁移本批
}
第 3 阶段:增量追平与切换
-- 再次迁移双写期间的增量数据
-- 使用时间戳或自增 ID 定位
INSERT INTO new_db_0.order SELECT * FROM old_db.order
WHERE update_time > lastSyncTime;
当新旧库数据差距缩小到可以接受的停机窗口内:
- 停止双写(停写旧库输出缓冲区或短暂暂停写入)
- 最后一次增量同步
- 全量校验
- 切换查询到新库
第 4 阶段:旧库下线
确认新库运行稳定后,关闭旧库的双写逻辑,彻底移除旧库。
扩容方案
场景
从 4 个分片扩容到 8 个分片。
方案一:取模变化 + 全量迁移(简单但代价大)
// 旧规则:order_id % 4
// 新规则:order_id % 8
// 需要重新计算所有数据的分片归属并迁移
// 约 50% 的数据需要搬迁
方案二:虚拟节点 + 一致性哈希
通过一致性哈希算法,扩容时只迁移部分数据:
// 旧分片节点:A, B, C, D
// 新分片节点:A, B, C, D, E, F, G, H
// 一致性哈希保证只有这部分数据需要迁移:
// 某些属于 A 的数据需要迁移到 E
// 某些属于 B 的数据需要迁移到 F
// ...
// 迁移量约 50%,但实现复杂
方案三:级联扩容(大数据量常用)
不直接扩容,而是增加一层路由:
// 级联路由:先取模旧规则,再取模新规则
class CascadeRouter {
int getShard(long id) {
int oldShard = id % 4;
if (oldShard == 0) return id % 2; // old0 拆为 new0,new1
if (oldShard == 1) return id % 2 + 2; // old1 拆为 new2,new3
// ...
}
}
这样每个原有分片只需要拆分为 2 个新分片,每次只迁移 50% 的数据。
方案四:提前预留(推荐)
设计时采用”二分法”预留分片空间:
// 初始分配 256 个分片(但在 only 4 台机器上运行)
// 将来扩展时,将 256 个分片分散到更多机器
// 数据完全不用迁移,只需将表文件移动到新机器
# 预留足够多的分片数
tables:
order:
# 一张表对应逻辑上的 256 个分片
actual-data-nodes:
# 初期 4 台机器,每台 64 个分片
- db0.t_order_$->{0..63}
- db1.t_order_$->{64..127}
- db2.t_order_$->{128..191}
- db3.t_order_$->{192..255}
# 扩容时加机器,把部分分片移到新机器即可
# 不会改变分片算法
缩容方案
缩容(减少分片)比扩容更少见,通常发生在业务萎缩或资源整合时。过程就是扩容的逆过程:
- 数据从被移除的分片迁到保留的分片
- 重新配置分片规则
- 验证一致性
- 下线节点
关键工具
- canal:阿里巴巴开源的 binlog 订阅组件,用于增量数据同步
- DataX:阿里巴巴的异构数据同步工具,适合全量迁移
- pt-online-schema-change:Percona 的在线 DDL 工具,可辅助迁移
- ShardingSphere-Scaling:ShardingSphere 自带的弹性迁移组件
面试要点
- 停机迁移简单可靠但影响业务,在线迁移复杂但无感知
- 双写 + binlog 追赶是平滑迁移的经典模式
- 扩容的最佳策略是”提前预留足够多的分片”
- 能说清楚:取模分片为什么扩容复杂,一致性哈希为什么好
- 数据校验是迁移中不可忽略的环节
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容