1. 为什么我们需要自研Upsert插件
在ETL数据同步的场景中,我们经常会遇到这样的需求:需要将源表的数据同步到目标表,如果目标表中已经存在相同主键的记录,则更新该记录;如果不存在,则插入新记录。这就是典型的"插入/更新"(Upsert)操作。
Kettle(现称Pentaho Data Integration)自带的"插入/更新"组件虽然能实现这个功能,但在实际使用中我发现它的性能实在让人头疼。记得有一次处理一个百万级数据的同步任务,用了原生组件后整整跑了6个小时还没完成,这让我不得不开始寻找更好的解决方案。
原生组件性能低下的主要原因在于它的实现机制:对于每一条数据,它都需要先查询目标表确认是否存在,然后再决定执行插入还是更新操作。这意味着每条数据都需要与数据库交互两次,当数据量大时,这种频繁的交互会带来巨大的性能开销。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 自研Upsert插件的核心设计思路
2.1 减少数据库交互次数
自研Upsert插件的第一个优化点就是减少数据库交互次数。我们采用了"批量处理+条件更新"的策略。具体来说,插件会先将待处理的数据批量加载到内存中,然后通过一条SQL语句完成所有记录的插入或更新操作。
这里的关键是使用了MySQL的INSERT ... ON DUPLICATE KEY UPDATE语法。这个语法允许我们在一条SQL语句中完成插入或更新操作,数据库会自动判断记录是否存在(基于主键或唯一索引),如果存在就执行更新,不存在就执行插入。
sql复制INSERT INTO target_table (id, name, createtime)
VALUES (1, '张三', '2023-01-01'), (2, '李四', '2023-01-02')
ON DUPLICATE KEY UPDATE
name = VALUES(name), createtime = VALUES(createtime);
2.2 批量提交优化
第二个优化点是批量提交。原生组件通常是逐条提交,而我们实现了批量提交机制。通过调整批量提交的大小,可以在内存消耗和处理效率之间找到最佳平衡点。
在我的测试中,当批量大小设置为5000时,性能达到最佳状态。这个值可以根据实际环境调整,一般建议在1000-10000之间。太小
