ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Kettle实战指南:多表合并与日期批量抽取的ETL自动化方案

Kettle实战指南:多表合并与日期批量抽取的ETL自动化方案 前阵子有个朋友问我“手上十几张表每天都要合并抽到一张总表里还要按日期批量跑Excel复制粘贴到天亮有没有靠谱的工具”我当时第一反应就是Kettle。这个老牌开源ETL工具虽然界面不算时髦但胜在“所见即所得”不写代码也能把数据流搭起来而且它在国内数据整合项目里的存在感一直很强。这篇文章不打算复述官方文档而是站在实际干活的角度把Kettle从下载安装到多表合并、按日期批量抽取、时间参数传递、JNDI配置这些高频场景串一遍顺便把我踩过的坑也一并交代了。适合刚接触ETL的开发者也适合准备在公司里搭数据抽取流程的同学。1. 为什么是Kettle从“把一个Excel数据导入数据库”说起1.1 ETL到底是什么你每天都在做的“搬砖”工作ETL是Extract、Transform、Load三个单词的缩写翻译过来就是“抽取、转换、加载”。很多人一听到这三个词就觉得高深其实它就是一套数据搬运流程。你可以把数据源想象成几个供应商仓库目标数据库想象成你自己的库房ETL就是安排人手把货从供应商仓库搬回来清点、检验、重新打包再按统一规则放进库房。抽取出数据、转换格式或清洗脏数据、加载到目标表三个动作串起来就是ETL。实际工作中最常见的例子业务部门给你一个Excel名单要求导入到用户表里同时把手机号里的空格去掉、把日期列统一成标准格式再把重复的用户剔除。这件事如果用人工处理每来一次就折腾一次而用Kettle把流程画出来之后以后只需要双击运行数据和结果就自动搞定。1.2 Kettle在ETL工具里的位置开源、可视化、不用写代码Kettle的官方名字叫Pentaho Data Integration缩写是PDI社区里习惯了叫Kettle。同类产品有Informatica、DataStage、Talend、NiFi等但Kettle有几个非常明显的特征开源免费、跨平台、图形化拖拽、上手门槛低。在不需要写代码的前提下它能连接几乎所有主流数据库、Excel、文本文件、接口数据通过把“步骤”像积木一样搭起来完成一套完整的数据流动线。和写Java、Python脚本做数据抽取相比Kettle最大的优势是可视化。你看到的是一条条从“表输入”指向“表输出”的连线中间可以随时插入“过滤记录”“字段选择”“排序记录”等步骤每一步处理什么、流到哪一眼就明白。对于被人他们经常说“文档不如一张画布”这句话在Kettle身上特别明显。1.3 Kettle的优势与短板什么场景适合用它我用了好几年Kettle最大的感受是它擅长“中小规模数据的规范化抽取加载”。你说它能为大数据平台做几亿行级别的离线清洗能但需要调优和更复杂的工程能力你说它比商业ETL工具功能全那倒不一定。维度优势短板成本社区版免费无授权压力官方商业支持需要收费上手难度图形化拖拽无代码基础也能学会概念较多需理解转换、作业、变量等数据源支持数据库、文件、HTTP、接口等覆盖面广一些专有协议需要自己写插件性能合理配置批量数后性能不错默认配置不调优时跑大批量会慢集群与调度配合操作系统定时任务或调度平台可使用本身不带强大的分布式调度监控能力什么场景适合用Kettle如果你只是要把多张表按天/按月合并抽到一张宽表或者每天从Oracle里导出报表数据或者把几个Excel清洗后入库这种场景Kettle能帮你省掉大量重复劳动。如果你要构建实时流式处理链路那Kettle不是对的工具它本质上是批处理工具别拿它当实时计算引擎用。2. 装好它JDK版本、下载渠道与第一次启动2.1 环境准备JDK和内存设置Kettle是Java写的安装第一件事就是确保机器上有合适的JDK。我见过太多人下载后双击没反应查了一圈发现是JDK版本不对。社区版不同版本的Kettle对Java版本要求不一样新版本的PDI比如9.x/10.x要求Java 11或17老版本8.x用Java 8更稳。建议安装之前先看一下解压目录里的README或官方文档里面明确写了依赖的Java版本。装好JDK后需要设置JAVA_HOME环境变量并在PATH里加入%JAVA_HOME%\bin。这一步很基础但容易埋坑如果电脑里同时装了多个Java版本命令行执行java -version显示的版本可能和Kettle要求的不一致启动就会莫名其妙失败。Windows下可以在Spoon.bat启动脚本里手动把JAVA_HOME指向你希望使用的JDK路径。内存设置同样重要。默认脚本给的堆内存可能只有256M或512M数据量稍一大就报OutOfMemoryError。Windows下编辑Spoon.batLinux下编辑spoon.sh找类似这一行PENTAHO_DI_JAVA_OPTIONS-Xmx2048m -Xms512m根据自己的机器内存调整我一般开发机设置-Xmx2048m跑大批量任务的服务器设置-Xmx4096m或更高。注意32位JVM最大只能用到约1.5G堆如果条件允许尽量用64位JDK。2.2 下载Kettle官方渠道和版本选择Kettle的下载渠道主要是官网社区版常见的是通过SourceForge或者Pentaho官网下载。文件名一般是pdi-ce-版本号.zip之类的集成包解压即用。社区版没有授权限制日常学习和公司内部使用完全没问题。版本策略上我的建议是“别追最新也别死守最老”。新版本一般会优化驱动兼容和功能修复但刚发布的版本也可能引入新问题。如果是生产环境我会选择发布了一段时间、社区反馈比较稳定的版本如果是个人折腾直接用最新版问题也不大。下载后解压到不含中文和空格的目录避免脚本解析路径时出幺蛾子。2.3 启动KettleWindows、Linux下的命令与常见问题Windows环境直接双击Spoon.bat就能打开图形界面。Linux有桌面环境的话进入目录执行./spoon.sh第一次启动会比较慢因为Kettle要初始化插件和配置别以为卡死了多等一会儿。如果服务器是无图形界面也别慌Kettle不依赖Spoon也能跑任务。它提供了两个命令行工具pan用来执行转换.ktr文件kitchen用来执行作业.kjb文件。比如在Linux服务器上手动跑一个抽取转换./pan.sh -file:/data/kettle/load_order.ktr -level:Basic这个特性很重要生产环境的调度往往是靠crontab或调度平台调pan/kitchen这一点很多新手不知道以为必须在有桌面的Windows机器上跑Kettle。启动常见问题里闪退排在第一位。原因大多是JAVA_HOME没配好或JDK版本不对。其次是字符集问题Windows下如果路径里有中文可能导致加载异常Linux下如果缺中文字体会导致Spoon界面乱码可以安装相关字体或者统一使用英文环境。还有一点不要用sudo直接跑避免文件权限混乱建议用普通用户创建任务并执行。2.4 第一次打开Spoon资源库还是文件模式Spoon启动后会弹出一个欢迎窗口问要不要连接资源库Repository。新手可以先选择“No repository”或者“文件模式”直接使用本地文件保存转换和作业。文件模式没什么不好我很多小项目都是直接维护.ktr和.kjb文件拷贝到服务器上就能跑。资源库模式则适合团队协作它把转换、作业、数据库连接统一存在一个数据库里多人共享一份定义方便版本回溯和权限管理。如果你只是个人开发或学习资源库反而增加复杂度文件更简单直接。第一次进入Spoon主界面可能会觉得有点乱密密麻麻的树形菜单和选项卡。不用慌核心就几个区域左侧“主对象树”里可以管理转换、作业和数据库连接中间大画布用来搭步骤右侧“核心对象”是步骤工具箱按输入、输出、转换、流程、查询等分类。记住这几个区域后面所有操作都在这里进行。3. 必须搞懂的核心概念转换、作业、步骤与数据库连接3.1 转换Transformation和作业Job怎么分工很多新手分不清转换和作业经常把一堆逻辑全塞在转换里结果跑批顺序没法控制。我习惯用一个比喻转换是“流水线”数据从源头进来经过一道道工序最终流向目标作业是“车间主任”安排什么时候开哪条流水线工序之间串行还是并行跑完还要不要发邮件通知都由作业来管。转换的保存后缀是.ktr它内部是并行的数据流。只要几个步骤之间通过“跳”连接它们就会尽量并行执行上游出数据下游就能处理。作业的保存后缀是.kjb它下面的条目大多数是按顺序执行的比如先执行转换A再执行转换B转换A失败时还可以走“false”分支发告警或结束。所以要控制“先后顺序”“循环”“失败重试”必须靠作业要处理具体的数据清洗、转换、加载必须靠转换。两者配合使用才能搭出完整的跑批流程。3.2 步骤Step和跳Hop数据流动的管道转换画布上的每一个小方块就是“步骤”比如“表输入”“文本文件输入”“字段选择”“排序记录”“表输出”。步骤之间用箭头连接这个箭头叫做“跳”Hop。数据就是沿着跳的方向从上游步骤流向下游步骤。步骤按功能大致分三类输入类负责把数据拉进来比如“表输入”“Excel输入”“获取系统信息”转换类处理数据变化比如“过滤记录”“字符串替换”“计算器”“排序记录”“去除重复记录”输出类把数据写出去比如“表输出”“插入/更新”“文本文件输出”。需要特别注意的是跳不只能传输数据还能带条件。比如用“过滤记录”步骤可以拉出两条跳一条标记为“true”一条标记为“false”满足条件的走一条路不满足的走另一条。这个机制在处理异常数据和分流向时非常实用。3.3 数据库连接驱动、URL和时区这些坑Kettle要连接数据库需要在左侧“主对象树”的“数据库连接”里新建连接。选择类型、填主机和端口等但真正容易出问题的往往不是这些基础项。第一坑是驱动。Kettle自带了很多驱动但版本不一定够用。例如新版MySQL 8需要com.mysql.cj.jdbc.Driver旧驱动是com.mysql.jdbc.Driver如果报ClassNotFoundException去官网或Maven仓库下载对应jar包放到Kettle的lib目录重启Spoon即可。第二坑是URL参数。连接MySQL时我基本都会加上这几个参数jdbc:mysql://localhost:3306/order_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8useSSLfalse关闭SSL告警serverTimezoneAsia/Shanghai避免时区误差characterEncodingutf8保证中文不乱码。连接Oracle时也要注意是用SID还是服务名URL写法完全不同比如jdbc:oracle:thin://localhost:1521/ORCLPDB1第三坑是字段大小写。Oracle默认会把人写的表名、字段名转成大写如果你的SQL里写了小写字段且质量不好会报“ORA-00904: 标识符无效”。解决办法是用双引号强制指定大小写但更建议在Kettle里统一用小写或大写的命名规则从源头避免问题。3.4 变量与参数让你的转换活起来如果做一个固定的抽取转换SQL里写死日期、表名用起来很简单换个日期就得改文件重新跑非常痛苦。Kettle提供了变量和参数机制把“每次变化的信息”抽出来这样同一个转换可以反复用。在转换的“属性”里可以定义命名参数比如startDate、endDate、sourceTable同时可以填默认值。在“表输入”步骤的SQL里用${参数名}引用SELECT * FROM ${sourceTable} WHERE create_date ${startDate} AND create_date ${endDate}注意${参数名}是字符串替换所以日期值一定要在SQL里用单引号包起来否则数据库会当成列名导致SQL语法错误。参数可以在运行转换时手动填也可以由作业传进来还可以通过命令行传入。比如用pan跑转换时./pan.sh -file:/data/kettle/load_order.ktr -param:startDate2024-01-01 -param:endDate2024-01-31参数化是Kettle工程化最重要的习惯没有之一。凡是可能变化的“阈值”“表名”“日期”都应该设计成参数而不是写死在步骤里。4. 高频实战一多表合并抽到一个表4.1 先搞清楚“合并”的几种语义“多表合并抽到一个表”这句话有两种完全不同的理解。一种是“纵向合并”即表结构相似的几张分表把数据从上到下堆在一起凑成一张大表比如把order_202401、order_202402、order_202403合并成order_all。另一种是“横向关联”即多个表通过主键进行join输出一张更宽的明细表比如订单表关联用户表得到“订单用户”宽表。我见过有人把这两种场景混在一起结果做出一个逻辑混乱的转换。Kettle里处理两者的方式完全不同纵向合并用“多个输入汇一个输出”横向关联用“记录集连接”或“数据库连接”等步骤。这篇主要讲最常见的纵向分表合并场景。4.2 用“多个表输入指向同一个表输出”实现纵向合并最简单直接的做法是在转换画布上放多个“表输入”步骤每一个负责读取一张分表再把它们全部连接到同一个“表输出”或“插入/更新”步骤上。数据会从多个输入步骤并行流入同一个输出步骤最终全部写入目标表。具体配置步骤新建一个转换拖入三个“表输入”和一个“表输出”。分别编辑表输入填写分表查询SQL。例如SELECT order_id, user_id, amount, create_date FROM order_202401;把三个表输入都连到“表输出”上。配置表输出选择目标表连接输入目标表名order_all。运行转换观察每个输入步骤各读取了多少行、最终写入了多少行。这里有个关键点如果几张分表的字段顺序不完全一致Kettle按字段名匹配写入但如果字段名不同比如一列叫orderDate、另一列叫create_date就需要在输出前加一个“字段选择”步骤把字段名统一成目标表需要的名字再进表输出。4.3 用“表输入SQL Union”的另一种做法如果分表数量不多且结构完全一致直接在“表输入”步骤里写一条UNION ALL也可以SELECT order_id, user_id, amount, create_date FROM order_202401 UNION ALL SELECT order_id, user_id, amount, create_date FROM order_202402 UNION ALL SELECT order_id, user_id, amount, create_date FROM order_202403这种方式的好处是配置简单只要一个输入步骤Kettle只需要执行一条SQL就能完成合并数据库层面压力集中但逻辑清晰。坏处是分表字段结构变化时需要手动维护SQL而且分表很多、SQL非常长时可读性和维护性会下降。我个人的习惯是分表数量在5张以内用SQL Union分表超过5张或者未来会动态增加用多个表输入汇一个输出的方式。动态表名配合变量可以做到改参数即可切换表比如SELECT * FROM ${table_prefix}${month}4.4 字段类型不一致、主键冲突怎么处理分表合并最常见的两个异常字段类型不一致和主键冲突。字段类型不一致典型表现是A表amount是DECIMAL(10,2)B表amount是VARCHAR(20)直接写入目标表时会报类型转换错误。解决办法是在输入步骤和输出步骤之间加“字段选择”或“类型转换”步骤把所有来源字段统一成目标表需要的类型。比如在“字段选择”的“元数据”页签里修改字段类型、长度、精度。主键冲突比如两张分表都包含order_id1001目标表的主键又是order_id直接插入就会报重复。解决思路有两种一种是“先排序去重再插入”。在写入前加“排序记录”步骤按order_id排序再加“去除重复记录”步骤按order_id判断重复保留第一条。数据量大时排序很吃内存需要在排序记录里调整“排序缓存大小”。另一种是改用“插入/更新”步骤而不是“表输出”设置关键字段为order_id查不到记录就插入查到就更新。这样即使重复执行转换也不会产生重复记录是更稳妥的幂等做法。我个人做合并任务时只要目标表允许都会优先用“插入/更新”宁可多跑一遍也不想因为漏跑或重跑把数据搞乱。5. 高频实战二批量遍历日期查数5.1 按天抽取数据的常见需求日常跑批里按天抽取是非常高频的需求。业务上常见的两种场景一是每天定时跑一次抽取“昨天”或“过去N天”的数据这是增量同步的常见姿势。二是补数比如某段时间的数据漏跑了需要从2024-01-01补到2024-01-31要求程序依次处理每一天而不是手动改31次参数。难点在于让转换“跑多次”并且每次使用不同的日期。如果只是在转换里把日期写死那补数时会想死。Kettle里最常用的解法是在“作业”层面做循环把日期作为变量传给转换。5.2 用“结果行”机制实现日期循环我推荐的方案是用“生成日期列表转换 作业对结果逐行执行”的模式这套方案可视化程度高不需要写复杂的JavaScript循环。第一步先建一个“生成日期列表”转换负责根据起始日期和结束日期生成每天的日期字符串。在画布上放“生成行”“增加序列”“JavaScript代码”“复制行到结果”。使用“生成行”步骤生成一行数据里面可以定义total_days。用“计算器”算出结束日期和起始日期之间的天数。用“增加序列”步骤生成从0到总天数的序列。用“JavaScript代码”或“公式”把起始日期 序列天数计算成每天的日期输出字段current_date。最后用“复制行到结果”步骤把行集写入“结果”中供作业读取。第二步在作业里放置这个转换后再接一个真正用来抽取数据的“按天抽取数据”转换条目并在该条目的属性中勾选“结果中的每一行都执行一次”。这样作业就会把上一步生成的日期列表一行一行的作为参数传给“按天抽取数据”转换执行。关键点来了“按天抽取数据”转换需要定义一个命名参数比如currentDate并且参数名要和结果行里的字段名一致。这样当作业逐行循环时结果行的字段值就会自动映射为转换的命名参数值。然后在表输入SQL中引用SELECT order_id, user_id, amount, create_date FROM orders WHERE create_date ${currentDate}运行作业后就能看到它一天一天地把数据查出来直到遍历完所有日期。5.3 简单场景作业里做变量递增如果只是每天定时跑一次不需要遍历一段日期区间也可以不用结果行循环直接在作业里做变量递增。具体思路是作业开始用“设置变量”初始化startDate、endDate、currentDate。加一个“简单的评估”作业项判断currentDate是否小于等于endDate。如果为真执行“按天抽取数据”转换传入currentDate参数。转换执行完成后用“JavaScript代码”作业项对currentDate加一天并调用job.setVariable写回。然后通过“跳”返回评估步骤继续判断。JavaScript代码作业项里可以写类似这样的逻辑var fmt new java.text.SimpleDateFormat(yyyy-MM-dd); var cur fmt.parse(job.getVariable(currentDate)); var cal java.util.Calendar.getInstance(); cal.setTime(cur); cal.add(java.util.Calendar.DAY_OF_MONTH, 1); job.setVariable(currentDate, fmt.format(cal.getTime()));这个方案适合“从某个日期开始每天都跑”的长周期任务配合操作系统定时任务能一直循环到结束日期。但相比“结果行”方式它对新手没那么友好我更推荐把日期列表生成放在转换里用结果行逐行驱动。调试起来也更直观。5.4 循环中如何避免重复数据和漏数据无论用哪种循环方式批量任务最怕的就是“重复跑”和“漏跑”。重复跑多半是因为作业失败后重新执行日期没有向前推进漏跑多半是因为某一天数据抽取失败但作业没有告警或者目标表没有主键约束导致插入失败被吞掉。我建议从三个层面防呆第一目标表设计唯一键或用“插入/更新”做幂等。如果一张表天然没有唯一键可以增加一个“业务日期业务编号”的联合唯一索引重复执行不会产生两条数据。第二在作业里记录日志。每次循环执行完写入一个“抽取日志表”记录日期、开始时间、结束时间、抽取行数。之后对比日志表和实际数据就能快速定位哪天漏跑。第三转换要设置合理的错误处理。比如表输入SQL报错时让作业走失败分支发送告警邮件而不是继续无脑执行。Kettle里每个步骤都可以配置“错误处理”跳配合作业的“发送邮件”条目可以在第一时间发现问题。6. 高频实战三转换里的时间参数与动态SQL6.1 Kettle中的时间参数从哪里来实际使用Kettle时时间参数很少是写死的。来源主要有四种第一种是运行转换时手动填写Spoon弹窗让你输入参数。第二种是作业里用“设置变量”或“JavaScript代码”计算好当前日期、昨天、月初等传给转换。第三种是命令行调用pan或kitchen时用-param:参数名值传进来。第四种是转换内部用“获取系统信息”步骤自动取得当前时间再配合dateAdd、dateDiff等函数计算时间范围。比如要取“昨天”的日期常见做法是在作业里用“JavaScript代码”或“计算器”也可以在转换中使用“获取系统信息”得到系统当前日期然后用“计算器”或“公式”减一天。但要注意“获取系统信息”拿到的是服务器的本地时间如果服务器时区和业务库时区不一致会直接导致数据多抽或少抽。6.2 在表输入里引用参数${VAR} 和 ? 占位符在Kettle的“表输入”步骤里写SQL最常用的是${参数名}变量替换。使用时需要保证两点参数已经在转换属性里定义过了以及“表输入”步骤中勾选了“替换SQL中的变量”选项否则变量名会被当成普通字符串传给数据库。示例SELECT * FROM orders WHERE create_date ${startDate} AND create_date ${endDate}注意${startDate}替换出来的是字符串所以必须用单引号包裹。这一点很多人栽过跟头把${startDate}直接写成2014-01-01又不加引号数据库直接报“ORA-00904: 标识符无效”或MySQL里的列不存在。还有一类是?占位符来自JDBC预编译参数但在Kettle的“表输入”中不太推荐使用因为Kettle作为一个可视化ETL工具变量替换更直观也更符合“参数化转换”的思维。当SQL需要动态表名时只能靠变量替换SELECT * FROM ${tableName} WHERE create_date ${startDate}动态表名虽然灵活但要注意控制SQL注入风险尤其是参数来源不可信时不要在参数里拼接危险SQL。Kettle本身是内部工具但安全习惯还是要养成。6.3 在作业里通过“设置变量”步骤传递时间参数作业向转换传参的标准姿势是“设置变量 转换条目参数映射”。具体操作在作业画布上添加“设置变量”作业项点击“获得变量”可以设置变量名、值、有效范围。有效范围建议选“在作业中有效”这样后续所有作业项都能读取。比如定义startDate值为2024-01-01endDate值为2024-01-31然后添加一个“转换”作业项指向你要执行的转换文件在“参数”页签里做映射参数名startDate变量startDate参数名endDate变量endDate这样转换运行时${startDate}就会被替换为作业里的变量值。注意转换的命名参数必须已经定义否则映射时找不到参数名。如果作业里还有“JavaScript代码”对日期做了运算例如把currentDate加一天后写回也需要保证修改后的变量在作业后续仍然有效。使用job.setVariable时作用域选择同样要注意写完以后直接继续执行下一个作业项即可。6.4 时间参数的格式与时区陷阱时间参数最隐蔽的坑是“格式不一致”。MySQL的DATETIME字段字符串比较时写的2024-01-01和2024-01-01 00:00:00含义就不同。比如你查当天数据WHERE create_date 2024-01-01 AND create_date 2024-01-02如果create_date是DATETIME类型和的边界位置就很清晰。但如果日期参数在Kettle里被格式化成20240101直接拼接进SQL就没法正确匹配。所以我习惯统一时间参数的展示格式为yyyy-MM-dd HH:mm:ss和yyyy-MM-dd两种按场景选用。时区问题主要出现在跨数据库、跨服务器。最简单的规避方式是在JDBC连接URL里显式指定时区比如MySQL的serverTimezoneAsia/Shanghai以及设置应用服务器和数据库服务器为同一时区。否则你早上跑批系统时间和数据库时间差了好几个小时抽取出来的数据就多了或少了整段。这个坑非常隐蔽排查时要先看服务器date和数据库select now()是否一致。7. JNDI配置从开发环境到生产环境的连接管理7.1 为什么要把数据库连接改成JNDI团队开发时每个人本地的数据库IP、账号可能都不一样。如果转换文件里的数据库连接是每个人自己配置的那么这个文件传到别人电脑上运行前就得改连接稍不注意就误操作到生产库风险很高。JNDI方案相当于把“连接名称”和“真实连接信息”分离。转换文件里只记住一个逻辑名字比如ORDER_DB而ORDER_DB到底连哪台机器、什么账号、什么密码全部写在Kettle服务器的jdbc.properties配置文件里。不同环境各配各的转换文件本身不用改。用点菜的比喻来说转换文件是菜单上的菜名后厨根据实际情况准备食材。开发环境下ORDER_DB指向开发库生产环境下同一份转换文件里的ORDER_DB自动指向生产库。这样发布时只需要替换配置文件不再需要逐个检查转换里的连接。7.2 JNDI配置文件写法jdbc.properties和simple-jndiKettle社区版通常使用simple-jndi这个轻量级JNDI实现。配置文件在Kettle目录下的simple-jndi文件夹里文件名叫jdbc.properties。打开以后每增加一个连接就写一段配置ORDER_DB/typejavax.sql.DataSource ORDER_DB/drivercom.mysql.cj.jdbc.Driver ORDER_DB/urljdbc:mysql://localhost:3306/order_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 ORDER_DB/userroot ORDER_DB/password123456这里有个容易出错的点连接名称里必须有/格式是连接名/type、连接名/driver、连接名/url、连接名/user、连接名/password缺一不可。连接名建议用大写加下划线方便在Spoon里识别。修改完jdbc.properties后需要重启Spoon才能生效。生产环境中这个文件里保存了真实的数据库账号密码要注意文件权限尽量让运行Kettle的系统和运维人员才能读到避免密码泄露。7.3 在Spoon中使用JNDI连接在Spoon中新建一个数据库连接时选择对应的数据库类型比如MySQL然后在“选项”或“连接方式”中切换为“JNDI”填写JNDI名称这里填的名称必须和jdbc.properties里的连接名一致比如ORDER_DB。有的版本在数据库连接窗口中会直接显示“使用JNDI名称”复选框勾选后输入JNDI名称即可。有的版本需要在“连接类型”中选择Generic database后再手动填写连接参数但这个方式不推荐我建议优先选明确支持的数据库类型再切JNDI。配置完成后在“表输入”步骤里选数据库连接时直接选择这个连接名。转换运行时会通过JNDI找到jdbc.properties里的真实URL和账号密码从而建立连接。如果你是在有应用服务器的场景中使用Kettle集成包也可能用到应用服务器提供的JNDI数据源。但社区版最常见、最简单的方式还是simple-jndi。这一点只要能区分清楚后续部署就不会卡。7.4 部署到服务端转换文件里保留JNDI名即可生产环境部署时把整个Kettle目录复制到服务器替换simple-jndi/jdbc.properties为生产环境的连接配置同时确认生产环境Kettle的lib目录下也有对应数据库驱动jar包。启动pan或kitchen执行作业时转换文件用到的数据库连接是JNDI名不需要打开文件去改IP和密码。这比“每个转换都写死直连”要安全、可维护得多。部署时我还遇到过另外两个问题。第一开发机器Windows下JNDI配置生效但Linux服务器上同一个文件读取不到原因多半是文件权限或路径不对。检查Kettle工作目录是否和配置文件处于同一层级以及是否用了只读权限。第二JNDI名称在不同环境含义不一致导致开发环境连接生产库的误操作。建议在开发库和生产库配置中使用完全不同的连接名比如ORDER_DB_DEV和ORDER_DB_PROD从名称上杜绝混淆。8. 常见问题排查与性能优化来自实际项目的经验8.1 跑得慢批量提交量、索引、分区Kettle默认配置下能跑但数据量一大就会出现“不算错就是慢”的情况。慢的根源往往不在Kettle本身而在写入方式上。表输出步骤里的“提交记录数量”默认可能是1000这个值偏保守。我一般会调大到5000或10000同时让JDBC底层支持批量写入。MySQL连接URL可以加上rewriteBatchedStatementstrue这个参数能让驱动真正把多条INSERT合并成一条批量执行实测插入性能能提升数倍。目标表索引也很关键。如果目标表有大量二级索引每一批插入都要同步维护索引数据量越大越慢。我的做法是大批量加载前先停掉或删除非必要索引加载完成后重建索引。如果目标是时间分区表最好按日期分区查询和清理历史数据都方便。并行也是优化手段。转换里多个互不依赖的数据流可以并行跑但要注意数据库连接池的并发限制。Kettle转换运行设置里可以调“批大小”“线程数”等参数新手不需要一开始就全部调整先观察究竟是读取慢还是写入慢再对症下药。8.2 报错驱动类找不到、时区、字符集、内存溢出Kettle报错信息有时候比较隐晦我整理了几个高频报错和排查思路ClassNotFoundException: com.mysql.cj.jdbc.Driver基本就是驱动没有放到lib目录或者驱动版本太老。去官网下载对应驱动放到lib目录重启Spoon。The server time zone value这是MySQL连接时区报错。在URL后面加serverTimezoneAsia/Shanghai即可。如果报错提示无法识别Asia/Shanghai可以尝试serverTimezoneGMT%2B8但更推荐升级驱动。中文乱码数据库层面按UTF-8存储Kettle连接URL里加characterEncodingutf8文本文件输入步骤里要看“编码”是不是UTF-8CSV经常遇到乱码就是这个原因。OutOfMemoryError: Java heap space堆内存不够。先调大启动脚本里的-Xmx如果数据量实在太大考虑在转换里增加“限制行数”做分批测试或者把插入提交量调小一点避免积压太多数据在内存中。还有一个思路是减少不必要的排序和去重排序会消耗大量堆内存。8.3 调试技巧预览数据、日志级别、Step Metrics我调试Kettle时没有写过多少代码主要是靠三个功能。第一是“预览”。任何输入步骤上右键都可以“预览”能先看看这一步到底读出了什么数据、有多少行、字段类型对不对。这个功能在做文件导入和SQL较复杂时特别有用能快速发现SQL写错了还是字段类型不匹配不用白白运行整个转换等半天。第二是日志级别。Spoon右下角可以切换日志级别或者命令行执行时加-level:Debug。如果是排查“为什么某一步没数据”或“某一步突然中断”Detailed或Debug级别的日志会打印每一步的行数和耗时很有价值。第三是Step Metrics即“步骤度量”。转换执行结束后在“执行结果”面板里能看到每个步骤的输入、输出、读、写、错误行数和耗时。哪一步停留时间最长哪一步进出行数对不上一目了然。比如表输入读了10万行但表输出只写了2万行那问题大概率出现在中间转换步骤的过滤或去重逻辑上。8.4 让Kettle项目可持续维护命名、版本管理、参数化最后一个部分想聊点工程经验。Kettle项目如果只是自己一个人用命名随意点没关系但要多人协作或长期维护命名和目录结构就非常重要。我建议所有转换、作业文件统一用有意义的英文名例如load_order_daily.ktr、extract_customer.kjb不要叫新建转换1、最终版2这种。目录上按业务模块分例如/etl/jobs、/etl/trans、/etl/ddl。数据库连接也统一命名能共用就共用连测试库还是生产库看名字就清楚。版本管理方面.ktr和.kjb本质是XML文件可以放进Git仓库。但要注意两点一是文件中可能包含绝对路径不同机器上路径不同会导致冲突所以尽量使用相对路径和参数二是数据库连接中的密码如果直接可见考虑使用Kettle的密码加密功能或者严格限制仓库权限。参数化这件事前面反复强调过这里再补充一点不只是日期和表名连日志表名、目标表字段映射、文件路径都可以参数化。一个转换如果能做到“同一个文件换参数就能跑不同场景”那它的复用价值就非常高。我在实际项目中经常是同一套转换被多个作业调用只不过每次传的日期范围和目标表不同而已。最后再分享一点个人体会。Kettle这个工具学了基本操作后剩下的就是“数据思维”先把目标拆成“从哪来、怎么变、到哪去”再在画布上一步步搭流程。多用“预览”慢就调提交批量怕出错就插日志多跑一次也没关系。按这套思路做下来Kettle能扛住大多数日常ETL需求。
返回列表