简介:ETL 工具 Kettle 9.4 已更名为 Pentaho Data Integration(简称 PDI),该版本资源面向数据集成工程师、ETL 初学者以及需要迁移旧项目的团队,可完整覆盖数据抽取、清洗、转换和加载场景,无论日常开发还是生产环境中的任务调度都适用,帮助快速搭建可复用的数据处理流程。资源压缩包采用 rar 格式,共包含了 1082 个文件,其中 630 个 jar 文件提供核心运行库,196 个 ktr 转换文件和 19 个 kjb 作业文件可直接在 Spoon 中加载,另有 xml 配置、properties 参数、bat/sh 启动脚本以及 xlsx/xls 示例数据等辅助内容,整体大小约 358MB,解压后即可离线安装使用。已有超过 7595 人学习下载,适合希望从 Kettle 旧版平稳迁移或系统学习并上手 PDI 9.4 新特性的开发者。通过这份资料能拿到完整的程序主体、常用转换与作业模板示例、跨平台启动脚本,既可用 Spoon 图形化完成 ETL 设计,也可通过 Kitchen、Pan 等命令行工具调度执行,便于对比新旧版本差异、理清各组件调用关系并快速融入实际项目开发。
1. Kettle 9.4 值得下吗:先跑通一个转换再谈其他
做数据清洗和同步的人,早晚会碰到一个尴尬场景:甲方库里是 Oracle 11g,自己本地是 MySQL 8,数据口径还对不上,领导只丢一句“你弄个工具导一下”。Kettle(Pentaho Data Integration,简称 PDI)9.4 就是干这个的——它不算新,但胜在稳定,社区版免费,图形化拖拽就能搭出“读数据 → 清洗 → 写目标库”的完整链路,不需要写一行业务代码。这篇笔记不聊官方文档里那些套话,只讲我从下载到跑通全流程的实际经验:JDK 版本怎么配、驱动放哪、Spoon 怎么启动、第一个转换怎么做、以及 9.4 里最容易踩的五个坑。如果你是刚接触 ETL 的运维或数据分析师,跟着走一遍大概半小时能把环境跑起来;如果你是老手,可以直接跳到第 5 章看避坑部分。
2. 装好就能跑:JDK 版本、驱动放置与 Spoon 启动参数
2.1 JDK 版本选择:9.4 对 Java 8 的依赖与兼容边界
PDI 9.4 官方要求 JDK 8,这不是随口一说。社区版底层依赖了大量老牌库(比如 Apache Commons、旧的 JDBC 桥接逻辑),这些库在 JDK 11 以上会出现两类问题:一是模块化限制导致某些类加载不到,二是反射访问被限制,典型报错是java.lang.IllegalAccessError或NoClassDefFoundError。我见过有人图省事装了 JDK 17,结果 Spoon 界面能开,但一跑转换就崩,最后乖乖换回 JDK 8。
建议做法是装一个 JDK 8 的较新小版本,比如 8u202 或更高,然后通过环境变量JAVA_HOME指向它。注意 Windows 上如果同时装了多个 JDK,Spoon 启动脚本里优先读JAVA_HOME,所以先确认这个变量没问题再谈别的。
# Windows 临时验证 JAVA_HOME echo %JAVA_HOME% java -version # Linux/macOS echo $JAVA_HOME java -version确认版本为 1.8.x 即可。如果你机器上没有 JDK 8,宁可单独解压一个绿色版 JDK 到本地目录,也不要动系统默认的 JDK 版本。绿色版 JDK 在好多环境里反而更省心,不用碰注册表和系统变量。
2.2 启动脚本与内存参数:Spoon.bat 里藏着什么
下载好 PDI 9.4 的压缩包后,解压目录里最显眼的是Spoon.bat(Windows)和spoon.sh(Linux/macOS)。很多人双击 Spoon.bat 后发现界面迟迟不出来,或者出来不到十分钟直接卡死,多半是默认内存参数太低。Kettle 的图形界面本身是 Swing 写的,内存占用不比 IntelliJ 小多少,默认 1G 堆内存跑大转换很容易抛OutOfMemoryError。
打开Spoon.bat能找到类似这样的段落:
if "%PENTAHO_DI_JAVA_OPTIONS%"=="" set PENTAHO_DI_JAVA_OPTIONS="-Xms1g" "-Xmx4g" "-XX:MaxPermSize=256m"我一般会改成:
set PENTAHO_DI_JAVA_OPTIONS="-Xms2g" "-Xmx6g" "-XX:MaxMetaspaceSize=512m"改动说明:-Xms2g表示 JVM 启动时直接分配 2G 堆,减少运行中频繁扩容;-Xmx6g是堆上限,如果你机器只有 8G 内存,这个值别超过 5g,不然操作系统会开始换页,反而更慢;MaxPermSize在 JDK 8 里已经废弃,改成MaxMetaspaceSize才是正解。改完保存,再启动 Spoon。
如果你的转换要处理几千万行的数据,还有个更稳妥的做法:不在图形界面里跑大规模作业,而是用命令行工具 Pan 或 Kitchen(见第 6 章),这样图形界面的内存压力和业务执行的资源完全隔离。
2.3 驱动放置:MySQL 与 Oracle 连接器到底放哪
这一步是新手最容易翻车的地方,也是老手偶尔“卡壳”的点。PDI 9.4 解压后,lib目录里自带了一部分驱动,但绝对没有 MySQL 8 和 Oracle 11g 的官方 JDBC 驱动。连接数据库报错Driver class not found的原因十有八九是驱动 jar 放错了位置。
正确的放置位置是 PDI 根目录下的lib文件夹。注意不是libswt,不是plugins,也不是某个转换文件所在目录。
| 数据库 | 驱动包 | 放置路径 | 说明 |
|---|---|---|---|
| MySQL 5.x | mysql-connector-java-5.1.48.jar | {PDI_HOME}/lib | 老项目常用 |
| MySQL 8.x | mysql-connector-java-8.0.30.jar | {PDI_HOME}/lib | 必须配serverTimezone |
| Oracle 11g | ojdbc6.jar (11.2.0.4) | {PDI_HOME}/lib | 11g 用 6 版本驱动 |
| Oracle 12c+ | ojdbc8.jar | {PDI_HOME}/lib | 12c 以上用 8 版本 |
# Windows 下确认驱动已被加载(在 Spoon 里也能看) cd {PDI_HOME}\lib dir mysql*.jar dir ojdbc*.jar补充一条血泪经验:驱动放好后,必须完全关闭并重启 Spoon,而不是在菜单里“刷新连接”。Kettle 的类加载器在启动时就把lib目录扫了一遍,运行时不会动态加载新放入的 jar。你要是复制完驱动后只是断开重连数据库,大概率还是报 ClassNotFound,这时候别怀疑人生,老老实实重启。
3. 核心流程拆解:从表输入到表输出,数据是怎么流起来的
3.1 转换与作业:Kettle 原理里最基础的两类文件
Kettle 的原理可以一句话概括:转换(Transformation)处理行流,作业(Job)调度执行顺序。转换里是“步骤”(Step)和“跳”(Hop)组成的流水线,数据从源步骤一行一行流经每个处理步骤,最终到达目标;作业则是更高层的编排,可以顺序执行多个转换、判断结果、发送邮件或触发定时任务。这个区分是所有 Kettle 使用教程的地基——在新建文件那一刻就要想清楚:现在做的是单条数据流处理,还是多步骤任务编排。
# 两个常用命令行工具对应的文件类型 Pan.bat -file:xxx.ktr # 运行转换(.ktr) Kitchen.bat -file:xxx.kjb # 运行作业(.kjb)选型建议:如果只是“从 A 表抽到 B 表,中间做几个字段处理”,用转换就够了;如果要做“先抽 A,成功后再抽 B,失败发邮件”,就用作业。把作业套在转换外面,是 Kettle 项目里最常见的架构。
3.2 构建第一个转换:表输入到表输出的五个步骤
打开 Spoon,新建一个转换。左侧面板拖出“表输入”到画布,双击配置数据库连接和 SQL;再拖出“表输出”,同样配置目标库连接和目标表。两个步骤之间拖一根线连起来,这个过程是图形化的,但背后的逻辑你要清楚:
-- 一个最简单的表输入 SQL SELECT id , user_name , create_time FROM src_user WHERE create_time >= '2024-01-01'这里要注意:SQL 里写了具体日期,意味着这是一个“一次性同步”。如果下次要跑增量,你就得用变量替换日期,这个到第 6 章专门讲。
表输出设置里有个“Commit size”参数,默认是 1000,意思是每攒够 1000 行才提交一次。很多新手导入几百万行时发现目标表一直没数据,其实就是 commit 没到阈值,这不是出错,是机制如此。把小事务提交改成 5000~20000 能明显改善大批量写入速度,但注意如果中途失败,回滚代价也会变大,一般建议 5000 以内比较稳。
跑完看右下角的“步骤度量”面板,能看到每个步骤处理的行数。核对“输入行数”和“输出行数”是否一致,是检查转换正确性的第一道关卡,也是所有 Kettle 教程里我一定会强调的习惯。不一致就说明中间丢了数据或产生了重复,趁早查。
3.3 变量与参数:让同一个转换适应不同日期
真实业务里没有“只跑一次”的转换,每天定时同步是最常见的场景。Kettle 支持字符串形式的变量引用,比如${YESTERDAY},在转换里所有输入框都可以引用。变量来源有几种:启动时用-param:传入、作业里从“设置变量”步骤写入、或者直接写死在kettle.properties文件里。
Pan.bat -file:sync.ktr -param:START_DATE=2024-06-01 -param:END_DATE=2024-06-30 -level:Basic参数说明:-param:后跟键值对,多个参数用空格隔开;-level:Basic是日志级别,日常跑批用 Basic 就够了,排查问题时再调到 Debug。转换里对应的表输入 SQL 写成:
SELECT * FROM src_order WHERE order_date >= '${START_DATE}' AND order_date < '${END_DATE}'变量替换是 Kettle 原理中“行流 + 元数据驱动”设计的一部分:步骤结构不变,只有数据边界伸缩。新手最容易犯的错误是变量名拼写错误——Kettle 不会在启动时立刻报变量不存在,而是把${WRONG_NAME}当成普通字符串拼进 SQL,等跑起来才发现查出来的数据不对。所以每次用变量,先-level:Debug跑一次,在日志里确认 SQL 渲染结果。
3.4 日志级别:从 Basic 到 Debug,报错时看哪里
Spoon 界面左下角有个日志窗口,默认级别是 Basic。报错时很多人直接拉日志窗口,看到一大片 INFO 就慌了,其实关键是“从下往上找第一个 ERROR 或 Caused by”。Kettle 的日志结构是从执行开始到当前位置的完整轨迹,真正的根因往往藏在最后的Caused by里,前面那些 Exception 只是外层包装。
# 命令行执行时指定日志级别 Pan.bat -file:sync.ktr -level:Detailed # 排查时用 Pan.bat -file:sync.ktr -level:Error # 正常跑批时用,减少输出我的习惯是:正常调度用 Error 级别,日志只出现错误时才有内容;开发调试用 Detailed,能看到每个步骤的行数和耗时;只有在定位驱动类加载、内存分配这类问题时才用到 Debug。日志级别不是越高越好,级别过高会让日志文件迅速膨胀,几十分钟写满几个 G 不是玩笑。
4. 三种常见数据源实战:Excel、JSON 接口与文本输出
4.1 Excel 输入:表头、空行与合并单元格的处理
Excel 输入是 Kettle 使用中最常见的场景,也最容易出现“数据对不上”的玄学问题。核心注意点有三个:表头行数、空行过滤、以及单元格格式。默认情况下,Kettle 把 Excel 第一行当作表头,但实际表格里经常有标题行、说明行混在前几行,这时在“Excel 输入”步骤的“工作表”选项卡里指定“起始行”即可。
| Excel 源情况 | 配置位置 | 推荐设置 |
|---|---|---|
| 第一行就是字段名 | 工作表 → 起始行 | 0 |
| 前三行是标题/说明 | 工作表 → 起始行 | 3 |
| 中间有空行 | 内容 → 过滤器 | 按“字段非空”过滤 |
| 合并单元格 | 不支持直接处理 | 在 Excel 里先拆列 |
# 一个典型的空行过滤表达式(在“文件或目录”步骤中) 字段:order_id 操作:IS NOT NULL逻辑说明:Excel 输入步骤把每一行映射为一行流数据,合并单元格在 Java POI 的读取模型里只有左上角那个单元格有值,其余全是 null。Kettle 没有自动“填充合并单元格”的功能,所以我的做法是在 Excel 源头就取消合并,或者导入后用“字段填充”步骤把 null 补成上一行的值。空行也是一样的道理——它不会报错,只会让目标表里多出一堆全 null 的脏数据,必须先过滤再入库。
4.2 JSON 输入:从一个 HTTP 接口拉数据到插入库
Kettle 9.4 里对接接口要用到两个步骤的组合:HTTP Client负责请求,JSON Input负责解析。很多人在“JSON Input”里写错路径,这背后是 JSONPath 的语法问题。比如接口返回:
{ "code": 0, "data": { "list": [ {"id": 1, "name": "张三"}, {"id": 2, "name": "李四"} ] } }要解析list数组里的字段,路径写法是:
$.data.list[*].id $.data.list[*].name在 JSON Input 步骤的“JSON 字段”区域,第一列填“jPath”,第二列填“字段名”,再指定数据类型。逻辑说明:[*]表示遍历数组中的每个对象,这一步转化成行流后会输出两行数据,每一行有id和name两个字段。如果你只填了$.data.list而没加[*].字段,解析出来是一个完整的 JSON 对象塞进一个字段里,后面的表输出会直接报类型不匹配。
HTTP Client 的另一个坑是“返回结果乱码”。常见做法是在请求步骤的“编码”参数里写UTF-8,但更保险的是用“网页采集”前的 HTTP 响应头来确认。乱码出现时先看接口返回的 Content-Type 是不是application/json; charset=utf-8,如果不是,Kettle 会按照默认 ISO-8859-1 解码,这时可以在 HTTP Client 的“接受编码”里强制指定。
4.3 文本文件输出:编码、分隔符与日期格式
把数据导出成 CSV 或固定宽度文本,是给下游业务系统交接的常见需求。文本文件输出步骤里的编码、分隔符、日期格式,每个参数都会影响下游是否能正常读取。
# 一个典型的 CSV 输出配置 文件编码:UTF-8(带 BOM 或不带 BOM) 分隔符:, 日期格式:yyyy-MM-dd HH:mm:ss 强制引号:N逻辑说明:编码选 UTF-8 时要注意,Windows 上用记事本打开、Excel 双击打开,如果发现中文乱码,是缺少 BOM 导致的。Kettle 的“编码”下拉框里有一项UTF-8BOM,给下游是 Excel 用户时选它;给 Linux 端解析的程序就选普通 UTF-8,因为带 BOM 的文本在 Java 的 BufferedReader 里会读到\uFEFF前缀,反而引起字段名不匹配。日期格式不指定的话,Kettle 会输出它内部的时间戳格式,比如2024/06/01 12:00:00.000,下游用 yyyy-MM-dd 解析必炸。
文件输出还有一个“追加或覆盖”的选项,对应Append和Truncate。每次跑批如果希望生成独立文件,文件名里可以带${RUN_DATE}变量,避免覆盖前一天的数据;如果目标系统只认固定文件名,那就选覆盖写。
5. 避坑指南:9.4 里最容易翻车的五个现场
5.1 时区报错:The server time zone value 乱码
现象:连接 MySQL 8 时,Spoon 弹错:The server time zone value '�й���ʱ��' is unrecognized,后边还跟着一串看不懂的字符。
原因:MySQL 8 的驱动要求显式指定时区,而服务器的默认时区是CST(中国标准时间),驱动在解析时把中文时区名称用错误的编码转换成了乱码。Kettle 的日志窗口里看到乱码不是中文环境问题,是驱动内部的 Locale 转换缺陷。
解决:在数据库连接的高级选项中增加一个连接参数,或者在 JDBC URL 中显式追加:
jdbc:mysql://127.0.0.1:3306/testdb?serverTimezone=Asia/Shanghai&useSSL=false&characterEncoding=utf8注意serverTimezone要写Asia/Shanghai,不要写GMT+8,某些老版本驱动不认GMT+8这种格式。从那以后我每次新建 MySQL 连接都习惯性地把连接 URL 完整写出来,而不是只填 IP 和库名。
5.2 ojdbc6.jar 明明放了还是 ClassNotFound
现象:Oracle 连接测试报Driver class not found: oracle.jdbc.OracleDriver,但lib目录里明明有 ojdbc6.jar。
原因:有两种可能。一是驱动 jar 版本与 JDK 不匹配,ojdbc6.jar 在 JDK 8 上没问题,但你本地如果装的是 JDK 11,ojdbc6 的某些类会加载失败;二是 jar 确实在刚放进去后没有重启 Spoon,Kettle 的类加载器不感知运行时新增的 jar。
解决:先确认lib目录里有 jar,然后看java -version是不是 1.8;最后一定重启 Spoon 再测连接。如果重启后还是不行,打开 jar 包确认类路径:
jar tf ojdbc6.jar | grep OracleDriver这一步能看到驱动完整类名。如果输出为空,说明你拿到的 jar 是损坏的或不完整的,重新下载。Oracle 的 ojdbc6.jar 在 11.2.0.4 之后有过一个版本,网上容易下到 10g 时代的旧包,类名存在但内部抛异常,这种最坑人。
5.3 中文乱码:数据库里看正常,导出的文件全乱
现象:Spoon 预览数据显示中文正常,但文本文件输出后在 Excel 里打开全是乱码。
原因:预览正常说明数据源头没问题,问题出在“文本文件输出”步骤的编码设置。Kettle 默认输出编码是 GBK 还是系统默认,取决于运行环境,Windows 中文系统下常常是 GBK,而你的文件写入用的 UTF-8,下游用 UTF-8 打开时自然乱码。
解决:输出步骤中“编码”一栏,统一勾选UTF-8BOM,这个带 BOM 的格式对 Excel 最友好。如果下游是 Java 程序解析,则选普通UTF-8,并提醒对方不要用 BufferedReader 默认编码读文件。排查乱码问题的口诀:先确认源头库的字符集,再确认 Kettle 连接 URL 里的characterEncoding,最后看输出文件的编码——三个环节必须一致,缺少一个环节的排查都是瞎猜。
5.4 内存溢出:跑到一半报 OutOfMemoryError
现象:转换处理几百万行时,日志里出现java.lang.OutOfMemoryError: Java heap space,Spoon 界面直接卡死或退出。
原因:默认堆内存只有 1G,而每个“表输入”步骤在读取数据时会把 ResultSet 缓存在 JVM 堆里,几百万行的数据量加上对象开销,很容易就顶爆。另一个常见原因是“行集”大小(Rowset size)设置过小,步骤间数据传递频繁阻塞。
解决:按第 2 章方法把-Xmx调到 4G 以上;在转换属性里把“行集大小”从默认的 10000 调大到 20000~50000。还要注意:如果表输出步骤里开着“批量插入”,对内存的消耗会比单行插入高不少。一种更彻底的方案是放弃图形界面,改用 Pan 命令行跑,界面本身不占资源,能省下 1G 左右的开销。
5.5 局部修改空字符串不转 NULL 的开关在哪
现象:明明在表输入里写了IFNULL(column, ''),可库里存进去的变成了空字符串,而不是 NULL;但另一个字段的空字符串又被正常转成了 NULL,行为不一致。
原因:Kettle 对空字符串和 NULL 的转换规则,取决于每个字段定义里的“允许空字符串”属性。它不在表输入里控制,而是在字段选择的“元数据”设置中:字段的Null if和Type属性共同决定转换行为。默认情况下,字符串字段的空字符串会被保留,不会自动转 NULL。
解决:在“字段选择”或“字段拆分”步骤的元数据区域,选中目标字段,在Null if一栏填入空值条件:
Null if: ""参数说明:Null if里的值表示“当源数据等于这个值时,转换成 NULL”。如果你希望""和"NULL"字符串都转成 null,可以写成:
"";NULL;注意分号是分隔符。这个“局部修改”之所以让很多人找不到,是因为它藏在字段属性里,而不是数据库连接或步骤配置里。从那以后我每次做数据清洗都会先列一张“空值映射表”,明确哪个字段要保留空字符串,哪个字段要转 NULL,再动手配字段元数据。
6. 让转换自动化:Kitchen 调度、主输出校验与增量核对
转换做出来只是第一步,能每天自动跑、跑完让人敢信,才算落地。我这里讲三个最实用的习惯。
第一,用主输出步骤校验数据质量。Kettle 里每个转换只能有一个“主输出步骤”,它会接收经过它之前所有处理的数据流。调试时在目标写入步骤前面加一个“空操作”步骤,右键设为“主输出步骤”,跑完后日志会单独输出该步骤的行数和前后字段快照。这一步能帮你快速定位“数据是在哪个环节被过滤掉或写坏的”。
第二,用作业加速增量同步的搭建。作业里可以把两个转换串起来:第一个转换取最大时间戳写入临时文件,第二个转换读取这个文件做增量抽取。启动命令用 Kitchen:
Kitchen.bat -file:sync_all.kjb -param:MAX_ID=100000 -level:Basic参数说明:-file指定作业文件;-param注入业务参数,比如从 100000 这个主键位继续增量拉取;-level设成 Basic 让日志只留关键信息。把这个命令交给 Windows 任务计划程序或 Linux cron,就能实现每日定时跑批。
第三,也是最容易忽略的一点:每次改动后,做一次字段级核对。我的习惯是跑完后在目标库执行一条对照 SQL:
SELECT COUNT(*), SUM(order_amount) FROM target_order_table WHERE stat_date = '2024-06-30';然后和源表的同口径数字对一遍。对不上就查转换,查字段映射,查过滤条件。对得上再收工。这套习惯养成后,踩坑概率至少降一半。
最后分享一个教训:有次我优化增量转换,把时间条件从接口参数改成了写死的SYSDATE-1,当时跑起来没问题,但第二天重新执行时没有清掉前一天遗留的临时表,导致同一批数据重复入库,下游报表连续三天对不上账。从那以后我每次跑批前都强制走一遍完整的“清临时表 → 跑主流程 → 核对行数”三部曲,宁可多花五分钟,也绝不省这一步。希望帮到你。
本文还有配套的精品资源,点击获取