上个月接到一个活儿,帮朋友把公司的一套老系统从SQL Server迁到MySQL。对方说“数据量不大,也就几十张表”。我心想这不小菜一碟?结果一上手就发现,事情远没那么简单。表确实不多,但字段类型、编码、主键策略全都不一样,光是挨个手工对账就折腾了两天。后来我决定用Kettle,也就是Pentaho Data Integration,直接从零开始搭一条迁移流水线。这东西我之前只是听说过,没想到真用起来,居然像搭积木一样顺手。

先说Kettle是什么。它是个开源的ETL工具,ETL就是抽取、转换、加载。说白了,就是帮你把数据从A系统搬出来,中间做点清洗和格式转换,塞进B系统。市面上类似的工具有很多,比如Apache NiFi、Talend,甚至简单的Python脚本也能干,但Kettle最大的优势是图形化界面。你不用写一行代码,拖拽几个组件就能搭出一条完整的迁移流程。这点对很多非技术出身的运维人员特别友好。我第一次打开它的Spoon界面时,看到一排排的“输入”、“输出”、“转换”图标,第一反应是“这不就是画流程图吗?”事实上,它比画流程图还直观。
我接手的具体场景是这样的:源库是SQL Server 2008,目标库是MySQL 5.7。表结构不完全一致,比如源库有个字段叫“createdat”,类型是datetime,目标库要求是timestamp;源库里有些表有自增主键,目标库要求用UUID。更要命的是,有几张表的数据量超过500万行,直接复制肯定超时。这些问题要是用手工处理,要么写一堆SQL脚本,要么写Python脚本,都得调试半天。而Kettle里,你只需要在“表输入”组件里写一条SELECT语句,然后在“字段选择”组件里指定映射关系,再在“表输出”组件里设置目标表名和字段类型,一条线就串起来了。
具体操作时,我踩了几个坑。第一个是驱动问题。Kettle连接SQL Server需要JTDS或者微软的JDBC驱动,连接MySQL需要Connector/J。如果你用的Kettle版本比较老,自带的驱动可能不支持新版数据库。我一开始没注意,直接建连接,结果报错“找不到合适的驱动”。后来去官网下载了对应版本的jar包,扔到Kettle的lib目录下,重启Spoon才解决。第二个坑是字符编码。源库里有些字段存的是中文,默认是GBK,目标库是UTF-8。如果不做转换,导进去的数据全是乱码。Kettle里有个“字符串替换”组件,可以指定源编码和目标编码,但更省事的做法是在“表输入”组件的SQL语句里直接加一句“CONVERT(varchar, field)”,把字段转成UTF-8再输出。
但真正的考验是数据量大的表。500万行数据,直接“表输入”到“表输出”,Kettle默认是一次性读取所有数据,然后一次性写入。结果内存直接爆了,Kettle卡死,我只好强制关掉。后来查文档才发现,Kettle支持“批量提交”和“分页读取”。具体做法是:在“表输出”组件里设置“批量插入大小”,比如每次提交1000条;在“表输入”组件的SQL里加“ORDER BY”和“OFFSET FETCH”来实现分页。这样数据是一批一批读、一批一批写,内存压力小很多。我试了试,速度从原来的几分钟变到几十分钟,但至少不会卡死了。后来我还发现,Kettle有个“并行”选项,可以同时启动多个线程来跑不同的表,效率能提升好几倍。不过要注意,如果目标库有外键约束,得先禁用约束,否则并行写入时会报错。
迁移过程中,最让我头疼的是数据校验。数据搬完了,怎么保证没丢、没多、没错?手工对账显然不现实。Kettle里有个“比较”组件,可以对比两个表之间的差异。我把源库和目标库分别作为两个输入,然后指定主键字段,组件就会输出“新增”、“删除”、“修改”三种结果。但实际用起来,我发现如果表有自增主键,源库和目标库的主键值可能不一样,因为MySQL的自增策略和SQL Server不同。这种情况下,你得用业务逻辑上的唯一键来对比,比如订单号、用户ID。我折腾了半天,还是写了个简单的SQL脚本,分别统计源和目标的总行数、总金额(对金额字段求和),一对比数字一致,才松了一口气。
说到Kettle的局限性,也得提一句。它毕竟是个基于Java的桌面工具,启动慢、吃内存,处理大数据量时性能不如Spark或Flink这类分布式框架。而且它的调度功能很弱,虽然可以用Kitchen命令行工具配合Cron做定时任务,但远不如Airflow或Oozie灵活。另外,它的社区版功能有限,企业版才支持一些高级特性,比如集群部署和实时流处理。不过对于小规模的数据迁移,一次性的或者定期的批处理任务,Kettle完全够用。我这次迁移完,朋友说后续每个月还要同步一次增量数据,我直接搭了个定时任务,用Kitchen跑Kettle的作业,再配合一个增量抽取的SQL条件,比如“WHERE updatetime > lastruntime”,就搞定了。
总结一下我的体会。Kettle最核心的价值不是它的功能有多强,而是它把数据迁移这件事从“写代码”降维成了“画流程图”。你不需要懂Java、Python,甚至不需要精通SQL,只要理解数据流转的逻辑,就能快速搭出一条可用的管道。当然,它也有自己的坑,比如驱动、编码、内存、并行策略,但这些问题网上都有现成的解决方案。对我而言,这次从零到一的经历最大的收获是:以后再遇到数据库迁移,我不再想着写几百行脚本了,而是先打开Spoon,拖几个组件试试。如果你也在琢磨怎么把数据从一个库搬到另一个库,不妨试试Kettle,说不定也会跟我一样,觉得这事没那么难。


