ITPUB

人麻了,领导让我写个脚本,把700T数据库,全部迁移到别的机房,带宽不超过2Mbps

说真的,领导这口一开,“你写个脚本把700T数据库迁一下”,我当场差点没直接过去了。

Image

700T,朋友们,这不是700MB,不是700GB,是700T!

脑子里第一个反应是:您老在开玩笑吧?700T,还是跨机房的迁移,还给我限速——2Mbps?这速度怕不是想让我搬到下辈子?

Image

不过嘴上不能说,心里也只能默默咽下这口老血,毕竟“程序员不能说不行,只能说安排”。

“你就写个脚本嘛”,咋写?

先说说这事的背景,两个机房,分别部署了不同类型的国产数据库,老机房用的是 A 数据库,新机房上的是 B 数据库。问题来了,字段类型都不完全兼容,就像你拿 Excel 想导入 Word,那肯定要先把格式整理一遍。

然后领导画了个饼:不能影响骨干网络,传输速度别超过 2Mbps。

我听完脑子里三个字:物理迁移。

网友说“硬盘快递”,真不是开玩笑

700T 数据,2Mbps 的速度,咱们算一笔账:

2Mbps 也就是每秒 0.25MB,一天 24 小时、3600 秒,差不多就是每天 21GB。你迁一整年也迁不完好吧?这不扯淡嘛。

所以网友说的“把硬盘拔下来,坐高铁送过去”,是真·工程师智慧。现在还有个专业名词:“Sneakernet”——意思就是物理拷贝比网络快。

Image

不过呢,现实中硬盘不是你想拔就拔,你得考虑权限、安全、数据库是否在运行中、冷热数据如何区分……但方向没错,大数据量物理迁移是主流。

假如你非要写“一个脚本”?

咱说点正经的,真要写脚本,也得结合这个场景:

  • 数据源:A 国产数据库
  • 目标库:B 国产数据库(字段类型不完全兼容)
  • 限速传输(2Mbps)
  • 数据总量 700TB

假设这个场景是你非写不可,那至少你得按这个流程来想:

  1. 数据导出(ETL 过程)
  2. 字段映射与转换
  3. 压缩与分片
  4. 限速网络传输
  5. 目标库导入

是不是听着就想辞职?我来详细说说怎么搞。

第一步:数据导出

你得从 A 数据库中把数据“导”出来,这时候不是一口气全导,是分批来,一张张表按顺序导。写个 Java 脚本用 JDBC 连接数据库,按分页导出,比如这样:

Connection conn = DriverManager.getConnection(sourceUrl, user, password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM huge_table LIMIT 0, 10000");
// 把结果写到本地文件,可能用 JSON、CSV、Parquet,根据实际情况来

别忘了加断点续传机制,你不可能一口气跑完,写个状态文件存每张表的进度。

第二步:字段转换

两个数据库字段不兼容?这就涉及到数据类型映射了。

比如 A 库里的 VARCHAR(255) 到 B 库里可能是 STRING,或者你遇到 CLOB、BLOB 这种老祖宗类型,还得转换成文件、再 base64 编码。

这部分没捷径,只能提前列清楚所有字段和类型的映射关系,写个转换器,类似下面:

public String convertType(String sourceType){
switch (sourceType.toUpperCase()) {
case"VARCHAR":
return"STRING";
case"CLOB":
return"TEXT";
case"NUMBER":
return"DECIMAL";
default:
return"STRING";
    }
}

这个步骤也可以借助 Apache NiFi 或者 DataX 这类工具,但你得自己配置字段映射。

第三步:压缩 + 限速传输

带宽有限制?那你必须压缩啊。搞个压缩率高点的,比如 LZ4、Zstd,压完还要控制传输速度,不能上来就“满血输出”,那会被网管拍死。

Java 里实现限速传输,可以用 RateLimiter 或者搞个 Thread.sleep 控制节奏,比如用 Guava 的:

RateLimiter limiter = RateLimiter.create(0.25); // 每秒 0.25MB
while ((bytesRead = input.read(buffer)) != -1) {
    limiter.acquire(bytesRead);
    output.write(buffer, 0, bytesRead);
}

当然,也可以压完先“冷藏”到移动硬盘里,晚上下班后再通过 VPN 挂上慢慢传。你以为程序员都是敲键盘的?不,咱还得会掐点偷跑。

第四步:导入目标库

这时候得看 B 数据库怎么支持批量导入,假设支持 LOAD DATA 或者你写个批量 insert 脚本:

PreparedStatement ps = conn.prepareStatement("INSERT INTO target_table (a, b) VALUES (?, ?)");
ps.setString(1, "xxx");
ps.setInt(2, 123);
ps.addBatch();

批处理注意设置 commit 间隔,不然内存爆炸,还得控制导入速度,防止目标库崩了。

“同步”可以搞,但得砸钱

有网友说他买了阿里云 DTS 同步服务,花几十块钱就搞定——这要看你量多不多。

700T 用阿里云 DTS?兄弟,你这是要让阿里云给你搬家啊……几十块钱估计也就跑个“你昨天改过的三行配置”。

商业同步服务确实省事,但前提是你得把钱花在刀刃上,还要保证云上有直连,不然传输还是慢。

写脚本?可以,别全靠

700T 数据迁移,不是你写个 for 循环就能解决的事。真要写,脚本得做到:

  • 能导出、导入;
  • 能压缩、限速;
  • 能容错、断点续传;
  • 能字段映射、兼容目标库格式;
  • 能分批执行,不能搞一波流。

但说实话,这种场景下“写脚本”就像你爸让你修房子给你一把锤子——是能干,但不现实。

更靠谱的方案,是脚本负责处理部分数据迁移+字段转换的逻辑,大头交给物理移动 + 专业工具。

毕竟,写代码不是为了让自己秃头,是为了让事情更简单。700T 的数据不怕多,就怕你轻敌。眼里没活、手里全是 bug 的,搞不好最后写脚本的是你,扛硬盘的也是你。

 直播预告 
4月18日,ITPUB携手行业专家围绕“DeepSeek一体机核心技术:看似‘取巧’实则‘破局’?”这一主题开展线上直播分享,下方扫码预约直播:
Image

Image