
简介data-integration.zip 是一份面向数据集成场景的 Kettle 开发资源包适合需要搭建 ETL 环境的数据工程师、BI 分析人员与运维人员用于解决多源数据抽取、清洗、转换及加载落地问题。压缩包共 965 个文件核心类型包括 172 个 jar 运行库、147 个 ktr 转换文件、15 个 kjb 作业文件另有 XML 配置、TXT 脚本说明、PNG 截图等整体约 106.65MB目录划分清楚便于按需检索其中 ktr/kjb 定义了具体的数据流与调度流程jar 提供了运行依赖截图和脚本说明则有助于理解界面操作与参数设置。包内提供开发环境所需的启动脚本、环境配置与数据库连接文件可快速启动图形化设计器并连接常见数据源大量转换与作业示例覆盖步骤编排、作业调度、错误处理等核心操作参考价值直接。目前已有 1178 人学习下载适合希望系统掌握 Kettle 工具的中级数据工程人员。1. 拿到># Linux 下最小启动流程 unzip># 先定位这行不同版本写法略有差异 # PENTAHO_DI_JAVA_OPTIONS-Xmx1024m -XX:MaxPermSize256m # 改成适合生产的值8G 内存的机器建议堆给 4G PENTAHO_DI_JAVA_OPTIONS-Xmx4096m -Xms1024m -Dfile.encodingutf-8-Xmx 是最大堆内存-Xms 是初始堆内存-Dfile.encodingutf-8 解决 Linux 下的中文乱码。Kettle 在读取 CSV 或非 UTF-8 数据库时如果 JVM 默认字符集跟系统不一致中文会变成问号。这个参数 Windows 下经常被忽略因为 Windows 中文版默认字符集就是 GBK日志看起来正常但同一个转换放到 Linux 服务器上跑就乱了。所以建议所有环境统一显式指定 UTF-8。还有个容易被忽略的点-XX:MaxPermSize 在 Java 8 以后已经废弃如果你的 JDK 比较新保留这个参数不影响启动但会打出 warning真正调大堆内存还是要靠 -Xmx。同样的参数要分别在 spoon.sh、pan.sh、kitchen.sh 里改因为三个脚本默认值独立只改 spoon.sh 本地测试没问题一上调度就还是老配置。3.3 数据库驱动放置MySQL、Accessucanaccess、国产库怎么加Kettle 自带部分驱动但 MySQL、PostgreSQL、Access 这些经常要自己补。MySQL 8 官方推荐 mysql-connector-java 8.x 的驱动把这个 jar 放进 lib 目录再重启 Spoon。# 以 MySQL 8 为例把驱动放到 lib 目录 cp mysql-connector-java-8.0.33.jar /opt/etl/data-integration/lib/放置后重启 Spoon新建数据库连接时驱动类选 com.mysql.cj.jdbc.DriverURL 写成 jdbc:mysql://host:3306/dbname?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai。这个 serverTimezone 参数很关键MySQL 8 驱动默认要求指定时区不写会报 CST 相关错误useSSLfalse 和 allowPublicKeyRetrievaltrue 是为了避免本机连接时 SSL 握手和密钥检索失败。Access 数据库比较特殊Kettle 老版本没有自带 Access 驱动需要下载 ucanaccess 驱动包连同它依赖的几个 jarjackcess、commons-lang 等一起放进 lib 目录。ucanaccess 的驱动类是 net.ucanaccess.jdbc.UcanaccessDriverURL 写法是 jdbc:ucanaccess://文件绝对路径配好之后就能像读普通数据库一样读 Access。国产数据库比如达梦、人大金仓做法相同拿到对应版本的 JDBC 驱动 jar 放进 lib在 Spoon 用通用数据库连接填驱动类和 URL难点基本只在驱动版本匹配上。3.4 日志级别与基础排错第一次启动失败看哪里Kettle 的日志分为 No logging、Basic、Detailed、Debug、Rowlevel 五级。生产环境用 Basic 就够它输出每个步骤的开始结束和错误汇总调试阶段用 Debug 能看到 SQL 实际执行语句Rowlevel 会把每行数据都打出来数据量稍大就刷屏只在单步调试时用。如果启动时报无法创建主目录或者 Spoon 界面打不开先确认>-- 表输入里写的 SQL先做全量抽取后续再改成增量 SELECT id, order_no, user_id, amount, status, create_time FROM t_order配置好源库连接和这条 SQL 之后双击表输出选择目标库连接然后点获取字段按钮让 Kettle 自动读取目标表结构并映射字段。这一步是新手最容易漏的如果不点获取字段表输出字段列表为空运行时会报无法找到字段映射错误这个错误信息本身就说明问题出在映射没生成。配置完成后点击工具栏运行选择本地执行执行引擎选普通。跑完看右下角步骤 Metrics输入行数、输出行数、错误数。如果两个数字一致这次抽取就成了。不一致时点错误标签页看具体报错行数据Kettle 会把错误行单独缓存不会吞掉数据这一点比直接写脚本要安全得多。4.2 用过滤记录和 JavaScript 代码做清洗转换实际业务里很少能把源表字段直接怼进目标表通常要过滤、改名、格式转换。Kettle 最常用的清洗手段是过滤记录步骤和JavaScript 代码步骤前者做分流转发后者做复杂值计算。过滤记录步骤需要配置一个条件表达式比如 status 不等于 1 的记录走false出口等于的走true出口。两个出口接不同的表输出就能实现正常数据导入正式表、异常数据进异常表的效果。这个步骤的匹配逻辑是可视化点选配置的不用写语法只要搞清字段类型和比较符即可。// JavaScript 代码步骤里做简单清洗 // 把金额由分转为元同时处理空值 var amount getInputRowValue(amount); var status getInputRowValue(status); if (amount null || amount ) { putRowData(OUT, amount, new java.math.BigDecimal(0)); } else { putRowData(OUT, amount, new java.math.BigDecimal(amount).movePointLeft(2)); }这段 JavaScript 代码在 Kettle 里运行在 Rhino 引擎上写法不是纯前端那种。getInputRowValue 取的是当前输入行的列值putRowData 把结果写入输出行OUT 是你在步骤里定义的输出流名称。字段名大小写要看实际列名Oracle 抽取出来的列名默认大写MySQL 默认小写这块非常容易踩坑。需要注意JavaScript 步骤的调试比较痛苦Kettle 不会帮你断点调试我一般习惯先输出到数据流用表输出临时落一个检查表看看结果确认没问题后再接正式链路。生产环境不建议放复杂 JS 逻辑性能瓶颈容易集中到这一步能用字段选择和计算器步骤解决的就不要写 JS。4.3 增量同步时间戳水位的实现与边界跨库同步最大的坑是第二次跑怎么不重复。最稳妥的增量方式是时间戳增量源表有 create_time 或 update_time每次同步记录上一次同步的截止时间点下次只取这个时间点之后的数据。Kettle 里时间戳增量用设置变量和获取变量配合实现也可以把上次同步时间存到一张控制表里。控制表的方式更符合生产习惯因为 Kettle 变量只在单个转换生命周期内有效跨天调度时还要依赖作业传参控制表则天然持久化。-- 用控制表保存同步水位每次同步前查询 SELECT COALESCE(MAX(sync_time), 1970-01-01 00:00:00) FROM etl_control WHERE table_name t_order拿到水位之后表输入的 SQL 里用占位符 ${last_sync_time} 过滤源表。如果控制表里没有记录首次执行会是全量这时需要判断Kettle 没有直接 if 步骤常见做法是在作业里加一个比较流程或者用过滤记录步骤对行数做判断第一次手动跑全量之后再把水位写进去。时间戳增量有个边界问题如果源表有个老数据被更新了 update_time而水位只按 create_time 过滤这条更新就同步不过来。实际业务里要把 query 条件写成 create_time 水位 OR update_time 水位目标表做 upsert 操作。Kettle 的表输出本身不支持 upsert需要换成表输入 字段检查 插入/更新步骤组合这是实现幂等同步的重点。4.4 定时调度Kitchen 命令行接入 crontab 的正确姿势转换做完要按天跑就需要把它包成作业再用 Kitchen 命令行执行。新建作业把刚才的转换作为作业条目拖进去保存为 .kjb 文件然后写 crontab。# 每天凌晨 2 点跑作业日志落盘到独立文件 0 2 * * * /opt/etl/data-integration/kitchen.sh -file/opt/etl/jobs/sync_order.kjb -levelBasic -logfile/opt/etl/logs/sync_order.log 21-logfile 参数把输出写到独立文件这样调 crontab 时不会因为标准输出没定义而丢失日志。crontab 环境变量比命令行少很多如果发现 Kitchen 在手动执行时正常、crontab 里执行时找不到 Java基本可以确定是 crontab 的 PATH 没包含 JAVA_HOME。解决办法是在 crontab 文件顶部手动 export JAVA_HOME 和 PATH。调度稳定性上还有两个细节第一crontab 里可以加 flock 锁文件防止作业重叠执行避免上一个还没跑完下一个又触发了第二Kitchen 失败时返回码非零crontab 会发邮件通知我一般让作业成功后把控制表的水位更新也放在作业里而不是放转换里这样调度层面能保证先成功、后记录水位的顺序。5. Kettle 避坑指南五个生产环境最常见的翻车现场5.1 中文乱码源库字符集和 JVM 不对齐现象从 Oracle 抽到 MySQL 的数据中文全部变成问号英文数字正常。原因Oracle 服务端字符集是 ZHS16GBKKettle 连接 URL 里没有指定字符集JVM 默认 UTF-8两边解码不一致。Kettle 不会自动做字符集转换它把字节流直接读出再直接写入乱码就发生在这一来一回。解决在数据库连接的选项里把 connection properties 设置对。Oracle 的 URL 追加 oracle.net.ssl_server_dn_matchfalse 先排除证书干扰再确认 NLS_LANG 跟服务端一致MySQL 在 URL 里加 characterEncodingutf8SQL Server 可以靠 JVM 参数 -Dfile.encoding 兜底。字符集问题排查起来耗时长建议新环境第一次建连接就把编码参数写全别等跑出来的数据不对再返工。5.2 内存溢出大表抽取先调参再改策略现象转换跑到一半报 java.lang.OutOfMemoryError: GC overhead limit exceeded 或 Java heap space重跑几次有时能过有时过不了。原因表输入步骤一次性把 ResultSet 读进来的默认行数没有限制加上后续的排序、分组步骤需要把数据缓冲在内存里堆就顶不住了。Kettle 默认堆偏小生产环境几十万行数据配合宽表很容易踩线。解决先调大 JVM 堆再把表输入的行集大小设为 1000~5000让数据分块流入后续步骤。如果数据量过亿用并发复制或分区把查询拆成多片并行处理。还有一个更简单的判断方法看日志里哪个步骤内存涨得最快基本就是那个步骤在持有大量中间数据优先给它加限制。5.3 驱动冲突lib 目录里新老 jar 共存是定时炸弹现象连接测试成功作业跑一段时间后突然报 ClassNotFoundException 或 NoSuchMethodError重启后又恢复随机性很强。原因lib 目录里同一个类存在多个版本 jar。Kettle 类加载顺序不稳定不同版本的驱动类名相同方法签名不同加载到旧版本就会出现 NoSuchMethodError加载到新版本而连接参数还是老写法又会报另外的错误。解决把 lib 目录里不需要的老驱动全部删掉只保留一个版本。特别注意不要同时放 MySQL 5.x 和 8.x 的 jar两个 jar 文件名不同但类名一样加载到哪个看运气。检查方法跑一个只连接数据库的转换Debug 级别日志会打出实际加载的驱动类来源 jar。线上环境修改 lib 后必须重启所有 Kitchen 进程否则类加载器还是旧的改完等于没改。5.4 增量重复水位记录的是作业开始时间而不是数据时间现象每天跑批后目标表记录数比源表还多多出来的几条是重复的边界数据。原因增量 SQL 用了 create_time ${last_sync_time}但 last_sync_time 记录的是作业开始时间不是源表数据的实际写入时间。跨天边界时凌晨 00:00:10 写入的源数据它的 create_time 比水位晚一秒但水位记录的是作业启动的 00:00:45于是这条数据这次没被同步下次又因为 create_time 小于水位被过滤掉永久丢失。解决水位改成源表中本次抽取到的最大 create_time而不是作业运行时间。实现方法是在同一个作业里加一个转换专门执行SELECT MAX(create_time) FROM t_order WHERE create_time ${last_sync_time}把结果更新进控制表。这一步必须是作业的最后一个条目而且要在作业成功后才执行否则抽取失败也会把错误水位写进去造成数据断层。5.5 并发锁表目标表写入竞争怎么解现象两个作业同时向同一张目标表写入数据库报表被锁定或死锁重试后偶尔能过。原因Kettle 的表输出默认单条 INSERT遇到并发会话时事务互相等待。尤其在按天跑批和补数作业同时执行时两个 Kitchen 进程竞争同一张表锁冲突几乎必然发生。解决两种做法。一是调度层面加串行crontab 里用 flock 对特定作业加锁确保同一时间只有同一个作业在跑二是在表输出步骤勾选使用批量插入批量大小设 500~1000 条减少锁竞争频率。使用批量插入后目标表的自增主键和唯一索引冲突会更明显设计目标表时要先想好去重键是业务主键就用插入/更新步骤是代理主键就要先查重再插入。6. 验证 Kettle 方案是否可靠行数对账是最低成本的手段Kettle 没有自动化测试机制跑批正确性全凭验证手段。我最常用的做法是每次跑批后自动做行数对齐源表 count 和目标表 count 差值要在误差范围内对账逻辑做成作业里的一个转换也可以写成独立脚本巡检两者各有利弊。先说明确的账怎么对订单表这类强主键表对账按 count 会导致漏 10 条又多了 10 条的假相等。我一般再加一层加工按天分区 count 源表和目标表两边分区值一致说明当天数据齐了。如果只做全表 count永远没法发现问题在哪一天。Kettle 里可以用 SQL 步骤分别去两个库查分桶 count然后用一个合并记录步骤做差集对比输出不一致的日期和差值这个转换本身就是一次很好的 Kettle 练兵。另一个实用技巧是仔细读日志尾部每个步骤的处理序列Kettle 每个转换结束会打印每个步骤的 read/written 行数和耗时。如果发现表输出耗时是表输入好几倍优先检查是不是默认单条 INSERT或者目标表索引太多导致写入慢。批量插入打开后耗时通常能降到原来的五分之一这个优化对每天几百万行的同步任务立竿见影。我曾经交付一个数据共享项目上线第二天客户反馈地址字段全是问号查了半天是 JVM 字符集没指定。自那以后我拿到每台新服务器的 style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />