目录
Kettle是一款流行的ETL(Extract-Transform-Load,即数据抽取、转换、装载)工具,并可用来操作Hadoop上的数据。Kettle是用Java语言开发的。它最初的作者Matt Casters原是一名C语言程序员,在着手开发Kettle时还是一名Java小白,但是他仅用了一年时间就开发出了Kettle的第一个版本。虽然有很多不足,但这版毕竟是可用的。使用自己并不熟悉的语言,仅凭一己之力在很短的时间里就开发出了复杂的ETL系统工具,作者的开发能力和实践精神令人十分佩服。后来Pentaho公司获得了Kettle源代码的版权,Kettle也随之更名为Pentaho Data Integration,简称PDI。
Kettle工具在设计之初就考虑到了一些设计原则,这些原则也借鉴了以前使用过的其它一些ETL工具积累下的经验和教训。
Kettle认为,作为ETL的开发者,应该把时间用在创建应用解决方案上。任何用于软件安装、配置的时间都是一种浪费。例如,为了创建数据库连接,很多和Kettle类似的Java工具都要求用户手工输入数据驱动类名和JDBC URL连接串。尽管用户可以通过互联网都能搜索到这些信息,但这明显把用户的注意力转移到了技术方面而非业务方面。Kettle尽量避免这类问题的发生。
一般来说,ETL工具要使简单的事情更简单,使复杂的事情成为可能。ETL工具提供了标准化的构建组件来实现ETL开发人员不断重复的需求。当然可以通过手工编写Java代码或Java脚本来实现一些功能,但增加的每一行代码都给项目增加了复杂度和维护成本。所以Kettle尽量避免手工开发,尽量提供组件及其各种组合来完成任务。
Kettle直接把所有功能通过界面的方式提供给用户,节约开发人员或用户的时间。当然专家级的ETL用户还是要去学习隐藏在界面后的一些特性。在Kettle里,ETL元数据可以通过XML格式表现,或通过资源库,或通过使用Java API。无论ETL元数据以哪种形式提供,都可以百分之百通过图形用户界面来编辑。
ETL转换里有各种各样的名称,如数据库连接、转换、步骤、数据字段、作业等都要有一个名称。如果还要在命名时考虑一些限制(如长度、选择的字符等),就会给工作带来一定麻烦。Kettle具备足够的智能化来处理ETL开发人员设置的各种名称。最终ETL解决方案应该可以尽可能地自描述,这样可以部分减少文档的需求,减少项目维护成本。
Kettle不需要用户了解转换中某一部分工作是如何完成的,但允许用户看到ETL过程中各部分的运行状态。这样可以加快开发速度、降低维护成本。
Kettle从设计初始就在数据的发送、接收方式上尽可能灵活。Kettle可以在文本文件、关系数据库等不同目标之间复制和分发数据,从不同数据源合并数据也是内核引擎的一部分,也同样很简单。
在一些ETL工具里经常可以看到数百行的输入和输出映射,对于维护人员来说这是一个噩梦。在ETL开发过程中,字段要经常变动,这样的大量映射也会增加维护成本。Kettle的一个重要核心原则就是,在ETL流程中所有未指定的字段都自动被传递到下一个组件。也就是说输入中的字段会自动出现在输出中,除非中间过程特别设置了终止某个字段的传递。
Kettle可以被归类为可视化编程语言(Visual Programming Languages,VPL),因为Kettle可以使用图形化的方式定义复杂的ETL程序和工作流。Kettle里的图就是转换和作业。可视化编程一直是Kettle里的核心概念,它可以让用户快速构建复杂的ETL作业和降低维护工作量。Kettle中的设计开发工作几乎都可以通过简单的拖拽来完成。它通过隐藏很多技术细节,使IT领域更接近于商务领域。
转换(transformation)是Kettle ETL解决方案中最主要的部分,它处理抽取、转换、装载各阶段各种对数据行的操作。转换包括一个或多个步骤(step),如读取文件、过滤输出行、数据清洗或将数据装载到数据库等等。
转换里的步骤通过跳(hop)来连接,跳定义了一个单向通道,允许数据从一个步骤向另一个步骤步骤流动。在Kettle里,数据的单位是行,数据流就是数据行从一个步骤到另一个步骤的移动。
图1显示了一个转换例子,该转换从数据库读取数据并写入文本文件。除了步骤和跳,转换还包括了注释(note)。注释是一个文本框,可以放在转换流程图的任何位置。注释的主要目的是使转换文档化。
步骤是转换的基本组成部分,它以图标的方式图形化地展现,这里显示了两个步骤,“表输入”和“文本文件输出”。一个步骤有几个关键特性:
跳(hop)就是步骤间带箭头的连线,跳定义了步骤之间的数据通路。跳实际上是两个步骤之间的被称为行级(row set)的数据行缓存。行集的大小可以在转换的设置里定义,缺省为10000行。当行集满了,向行集写数据的步骤将停止写入,直到行集里又有了空间。当行集空了,从行集读取数据的步骤停止读取,直到行集里又有可读的数据行。注意,跳在转换里不能循环,因为在转换里每个步骤都依赖于前一个步骤获取字段。
跳的这种基于行集缓存的规则允许每个步骤都由一个独立的线程运行,这样并发程度最高。这一规则也允许以最小消耗内存的数据流的方式来处理。在数据分析中,我们经常要处理大量数据,所以这种并发低耗内存的方式也是ETL工具的核心需求。
对于Kettle转换,不可能定义一个步骤在另一个步骤之后执行,因为所有步骤都以并发方式执行:当转换启动后,所有步骤都同时启动,从它们的输入跳中读取数据,并把处理过的数据写到输出跳,直到输入跳不再有数据,就中止步骤的运行。当所有的步骤都中止了,整个转换就中止了。从功能的角度看,转换具有明确的起点和终点。这里显示的转换起点是“表输入”步骤,因为这个步骤生成数据行。终点是“文本文件输出”步骤,因为这个步骤将数据写到文件,而且后面不再有其它节点。
一方面,可以想象数据沿着转换里的步骤移动,形成一条行头到尾的数据通路。而另一方面,转换里的步骤几乎是同时启动的,所以不可能判断出哪个步骤是第一个启动的步骤。如果想要一个任务沿着指定的顺序执行,那么就要使用后面介绍的“作业”了。
数据以数据行的形式沿着步骤移动。一个数据行是零到多个字段的集合,字段包括这里所列的几种数据类型。
每个步骤在输出数据行时都有对字段的描述,这种描述就是数据行的元数据,通常包括下面一些信息:
当设计转换时有几个数据类型的规则需要注意:
既可以显式地转换数据类型,如在“字段选择”步骤中直接选择要转换的数据类型,也可以隐式地转换数据类型,如将数值数据写入数据库的varchar类型字段。这两种形式的数据转换实际上是完全一样的,都是使用了数据和对数据的描述。
Kettle内部的Date类型里包含了足够的信息,可以用这些信息来表现任何毫秒精度的日期、时间值。如果要在String和Date类型之间转换,唯一要指定的就是日期格式掩码。这里显示的是几个日期转换例子。
转换掩码(格式) |
结果 |
yyyy/MM/dd’T’HH:mm:ss.SSS |
2019/12/06T21:06:54.321 |
h:mm a |
9:06 PM |
HH:mm:ss |
21:06:54 |
M-d-yy |
12-6-19 |
Numeric数据(包括Number、Integer、BigNumber)和String类型之间的转换用到的几个字段元数据是:转换掩码、小数点符号、分组符号和货币符号。这些转换掩码只是决定了一个文本格式的字符串如何转换为一个数值,而与数值本身的实际精度和舍入无关。这里显示了几个常用的例子。
值 |
转换掩码 |
小数点符号 |
分组符号 |
结果 |
1234.5678 |
#,###.### |
. |
, |
1,234.57 |
1234.5678 |
000,000.00000 |
, |
. |
001.234,56780 |
-1.9 |
#.00;-#.00 |
. |
, |
-1.9 |
1.9 |
#.00;-#.00 |
. |
, |
1.9 |
12 |
00000;-00000 |
|
|
00012 |
最后一个表格提供了Boolean和String之间、整型与日期类型之间数据类型转换的列表。
从 |
到 |
描述 |
Boolean |
String |
转换为Y或N,如果设置长度大于等于3,转换为true或false |
String |
Boolean |
字符串Y、True、Yes、1都转换为true,其它字符串转换为false(不区分大小写) |
Integer |
Date |
整型和日期型之间转换时,整型就是从1970-01-01 00:00:00 GMT开始计算的毫秒值。 |
Date |
Integer |
大多数ETL项目都需要完成各种各样的维护任务。例如,当运行中发生错误,要做哪些操作;如何传送文件;验证数据库表是否存在,等等。而这些操作要按照一定顺序完成。因为转换以并行方式执行,就需要一个可以串行执行的作业来处理这些操作。
一个作业包括一个或多个作业项,这些作业项以某种顺序来执行。作业执行顺序由作业项之间的跳(job hop)和每个作业项的执行结果来决定。图2显示了一个典型的装载数据仓库的作业。
作业项是作业的基本构成部分。如同转换的步骤,作业项也可以使用图标的方式图形化展示。但是,如果再仔细观察,还是会发现作业有一些地方不同于步骤:
因为作业顺序执行作业项,所以必须定义一个起点,如图中的“start”作业项,就定义了一个起点。一个作业只能定义一个开始作业项。
作业的跳是作业项之间的连接线,它定义了作业的执行路径。作业里每个作业项的不同运行结果决定了作业的不同执行路径。对作业项的运行结果的判断如下:
在作业项连接(跳)的右键菜单上,或跳的小图标里都可以设置以上这三种判断方式。
Kettle使用一种回溯算法来执行作业里的所有作业项,而且作业项的运行结果(真或假)也决定执行路径。回溯算法就是:假设执行到了图里的一条路径的某个节点时,要依次执行这个节点的所有子路径,直到没有再可以执行的子路径,就返回该节点的上一节点,再反复这个过程。
例如,图3里的A、B、C三个作业项的执行顺序为:
因为没有定义执行顺序,所以这个例子的执行顺序除了ABC,还可以有CAB。这种回溯算法有两个重要特征:
有时候需要将作业项并行执行。这种并行执行也是可以的。一个作业项能以并发的方式执行它后面的作业项,如图4中上面的作业所示。在这个例子里,作业项A和C几乎同时启动。
需要注意的是,如果A和C是顺序的多个作业项,那么这两组作业项也是并行执行的,如图5所示。
在这个例子中,作业项[A、B、写日志]和[C、D、清空表]是在两个线程里并行执行的。通常设计者也是希望以这样的方式执行。但有时候,设计者希望一部分作业项并行执行,然后再串行执行其它作业项。这就需要把并行的作业项放到一个新的作业里,然后作为另一个作业的作业项,如图6所示。
作业执行结果不仅决定了作业的执行路径,而且还向下一个作业项传递了一个结果对象。结果对象包括了这里所示的一些信息。
JavaScript作业项是一个功能强大的作业项,可以实现更高级的流程处理功能。在JavaScript作业项里,可以设置一些条件,这些条件的结果,可以决定最终执行哪条作业路径。
转换和作业是Kettle的核心组成部分。在介绍Kettle设计原则时曾经讨论过,它们可以用XML格式来表示,可以保存在资料库里,也可以用Java API的形式来表示。它们的这些表示方式,都依赖于这里所列的元数据。
Kettle里的转换和作业使用数据库连接来连接到关系型数据库。Kettle数据库连接实际是数据库连接的描述:也就是建立实际连接需要的参数。实际连接只是在运行时才建立,定义一个Kettle的数据库连接并不真正打开一个数据库的连接。
各个数据库的行为都不是完全相同的,如图7所示的Kettle数据库连接窗口里有很多种数据库,而且数据库的种类还在不断增多。
在数据库连接窗口中主要设置三个选项:
根据选择的数据库不同,右侧面板的连接参数设置也不同,例如图7中,只有Oracle数据库可以设置表空间选项。一般常用的连接参数为:
对于大多数用户来说,使用数据库连接窗口的“一般”标签就足够了。但偶尔也可能需要设置对话框里的“高级”标签的内容,如图8所示。
除了这些高级选项,在连接对话框的 “选项”标签下,还可以设置数据库特定的参数,如一些连接参数。为了便于使用,对于某些数据库(如MySQL),Kettle提供了一些默认的连接参数和值。有几种数据库类型,Kettle还提供了连接参数的帮助文档,通过单击“选项”标签中的“帮助”按钮可以打开对应数据库的帮助页面。
最后,还可以选择Apache的通用数据库连接池的选项。如果运行了很多小的转换或作业,这些转换或作业里又定义了生命期短的数据库连接,连接池选项就显得有意义了。连接池选项不会限制并发数据库连接的数量。
关系数据库是一种高级的软件,它在数据的连接、合并、排序等方面有着突出的优势。和基于流的数据处理引擎,如Kettle相比,它有一大优点:数据库使用的数据都存储在磁盘中。当关系型数据库进行连接或排序操作时,只要使用这些数据的引用即可,而不用把这些数据装载到内存里,这就体现出明显的性能方面的优势。但缺点也是很明显的,把数据装载到关系数据库里也会产生性能的瓶颈。
对ETL开发者而言,要尽可能利用数据库自身的性能优势,来完成连接或排序这样的操作。如果不能在数据库里进行连接这样的操作,如数据的来源不同,也应该现在数据库里排序,以便在ETL里做连接操作。
数据库连接只在执行作业或转换时使用。在作业里,每一个作业项都打开和关闭一个独立的数据库连接。转换也是如此。但是因为转换里的步骤是并行的,每个步骤都打开一个独立的数据库连接并开始一个事务。尽管这样在很多情况下会提高性能,但当不同步骤更新同一个表时,也会带来锁和参照完整性问题。
为了解决打开多个连接而产生的问题,Kettle可以在一个事务中完成转换。在转换设置对话框的 “杂项”标签中,设置“使用唯一连接”,可以完成此功能。当选中了这个选项,所有步骤里的数据库连接都使用同一个数据库连接。只有所有步骤都正确,转换正确执行,才提交事务,否则回滚事务。
当一个大数据库不再满足需求时,就会考虑用很多小的数据库来处理数据。通常可以使用数据库分片技术来分散数据装载。这种方法可以将一个大数据集分为几个数据分区(或分片),每个分区都保存在独立的数据库实例里。这种方法的优点显而易见,可以大幅减少每个表或每个数据库实例的行数。所有分片的组合就是数据库集群。
一般采用标识符计算余数的方法来决定分片的数据保存到哪个数据库实例里。这种分片计算方法得到的分片标识是一组0到“分片数-1”之间的数字,可以在数据库连接对话框的“集群”标签下设置分区数。例如,定义了五个数据库连接作为集群里的五个数据分片。可以在“表输入”步骤里执行一个查询,这个查询就以分区的方式执行:同样的一个查询会被执行五遍,每个数据分区执行一遍。在Kettle里,所有使用数据库连接的步骤都可以使用分片的特性。例如,表输出步骤在分片模式下会把不同的数据行输出到不同的数据分区(片)中。参见“Kettle数据库连接中的集群与分片”
Kettle里有不同的工具,用于ETL的不同阶段。主要工具包括:
参见“Kettle工具——Spoon、Kitchen、Pan、Carte”。
当ETL项目规模比较大,有很多ETL开发人员在一起工作,开发人员之间的合作就显得很重要。Kettle以插件的方式灵活定义不同种类的资源库,但不论是哪种资源库,它们的基本要素是相同的:这些资源库都使用相同的用户界面、存储相同的元数据。目前有3种常见资源库:数据库资源库、Pentaho资源库和文件资源库。
无论哪种资源库都应该具有下面的特性:
灵活而统一的文件处理方式对ETL工具来说非常重要。所以Kettle支持URL形式的文件名,Kettle使用Apache的通用VFS作为文件处理接口,替用户解决各种文件处理方面的复杂情况。例如,使用Apache VFS可以选中.zip压缩包内的多个文件,和在一个本地目录下选择多个文件一样方便。这里显示的是VFS的一些典型的例子。
文件名例子 |
描述 |
文件名:/data/input/customets.dat |
这是最典型的定义文件的方式 |
文件名:file:///data/input/customers.dat |
Apache VFS可以从本地文件系统中找到文件 |
作业:http://www.kettle.be/GenerateRows.kjb |
这个文件可以加载到Spoon里,可以使用Kitchen执行,可以在作业项里引用。这个文件通过Web服务器加载 |
目录:zip:file:///C:/input/salesdata.zip 通配符:.*\.txt$ |
在“文本文件输入”这样的步骤里可以输入目录和文件通配符。例子里的文件名和通配符的组合将查找zip文件里的所有以.txt结尾的文件 |