Pig Latin
结构
一个Pig Latin程序由一组语句构成。一个语句可以理解为一个操作,或一个命 令。例如,GROUP操作是这样一条语句:
grouped_records =GROUP records BY year;
另一个例子是列出Hadoop文件系统中文件的命令:
ls /
如前面的GROUP语句所示,一条语句通常用分号结束。实际上,那是一条必须用 分号表示结束的语句。如果省略了分号,它会产生一个语法错误。而另一方面,ls 命令可以不用分号结束。一般的规则是:在Grunt中交互使用的语句或命令不需要 表示结束的分号。这包括交互式的Hadoop命令以及用于诊断的操作,例如 DESCRIBE。加上表示结束的分号总是不会错。因此,如果不确定是否需要分号, 把它加上是最简单的解决办法。
必须用分号表示结束的语句可以写成多行以便于阅读:
records =LOAD ,input/ncdc/micro-tab/sample.txt,AS (year:chararray, temperature: int, quality:int);
Pig Latin有两种注释方法。双减号表示单行注释。Pig Latin解释器会忽略从第一个减号开始到行尾的所有内容:
-- My programDUMP A; -- What,s in A?
C语言风格的注释更灵活。这是因为它使用/*和*/符号表示注释开始和结束。这 样,注释既可以跨多行,也可以内嵌在某一行内:
/* *Description of my program spanning *multiple lines. */A =LOAD ,input/pig/join/A';B =LOAD ,input/pig/join/B';C =JOIN A BY $0, /* ignored */ B BY $1;DUMP C;
Pig Latin有一个关键词列表。其中的单词在Pig Latin中有特殊含义,不能用作标 识符。这些单词包括操作(LOAD, ILLUSTRATE)、命令(cat,ls)、表达式 (matches,FLATTEN)以及函数(DIFF,MAX)等。详见随后的几个小节。
Pig Latin的大小写敏感性采用混合的规则。操作和命令是大小写无关的(这样交互 式操作更“宽容”),而别名和函数名是大小写敏感的。
语句
在Pig Latin程序执行时,每个命令按次序进行解析。如果遇到句法错误或其他 (语义)错误,例如未定义的别名,解释器会终止运行,并显示错误消息。解释器会 给每个关系操作建立一个逻辑计划。逻辑计划构成了 Pig Latin程序的核心。解释 器把为一个语句创建的逻辑计划加到到目前为止已经解析完的程序的逻辑计划上, 然后继续处理下一条语句。
特别需要注意的是,在整个程序逻辑计划没有构造完成前,Pig并不处理数据。例 如,我们仍然以前面的Pig Latin程序为例:
-- max_temp.pig: Finds the maximum temperature by year records =LOAD Jinput/ncdc/micro-tab/sample.txt,AS (yean:chararray, tempenatune:int ,quality:int); filtered_records -FILTER records BY temperature ! =9999 AND(quality 0 OR quality 1 OR quality 4 OR quality 5 OR quality 9); gnouped_neconds =GROUP filtered_records BY year; max_temp =FOREACH grouped_neconds GENERATE group, MAX(filtered_records.temperature);DUMP max_temp;
Pig Latin解释器看到第一行LOAD语句时,它首先确认它在句法和语义上是正确 的,然后再把这个操作加入逻辑计划。但是,解释器并不真的从文件加载数据(它 甚至不去检查该文件是否存在)。Pig到底要把文件加载到哪里呢?数据是加载到内存吗?即使这些数据可以放入内存,Pig又如何处理数据呢?我们可能并不需要所 有的数据(因为后续的语句可能会过滤数据),因此读入所有数据没有意义。关键问题在于,在没有定义整个数据流之前,开始任何处理都是没用的。与此类似,Pig 验证GROUP和FOREACH---GENERATE语句,并把它们加入逻辑计划中,但并不 执行这两条语句。让Pig开始执行的是DUMP语句。此时,逻辑计划被编译成物 理计划,并执行。
多査询执行 由于DUMP是一个诊断工具,因此它总是会触发语句的执行。但是,STORE 与DUMP不同。在交互模式下,STORE和DUMP —样,总是会触发语句的执 行(包含run命令)。但是,在批处理模式下,它不会触发执行(包含exec命 令)。这是为了性能考虑而进行的设计。在批处理模式下,Pig会解析整个 脚本。 看看是否能够为减少读写磁盘的数据量进行优化。考虑如下的简单示例: A =LOAD J input/pig/multiquery/A';B =FILTER A BY $1 'banana';C =FILTER A BY $1 ! ='banana'STORE B INTO output/b';STORE C INTO output/c'; 关系B和C都是从A导出的。因此,为了防止读两遍A,Pig可以用一个MapReduce任务从人读取数据,并把结果写到两个输出文件中去:一个给B, 一个给C。在Pig的以前版本中不包括这一特性:批处理模式下脚本中的每个 STORE语句都会触发语句的执行,从而每个STORE语句都有一个对应的作 业。可以在执行Pig时使用-M或-no_multiquery选项来禁用多查询执行,从 而恢复使用以前的设置。这一特性称为“多查询执行”(multiquery execution)。 |
Pig的物理计划是一系列的MapReduce作业。在本地模式下,这些作业在本地 了乂頁中运行;而在MapReduce模式下,它们在Hadoop集群上运行。
表11-1概述了能够作为Pig逻辑计划一部分的关系操作。
类型 | 操作 | 描述 |
---|---|---|
加载与存储 | LOAD | 将数据从文件系统或其他存储中加载数据,存入关系 |
STORE | 将一个关系存放到文件系统或其他存储中 | |
DUMP | 将关系打印到控制台 | |
过滤 | FILTER | 从关系中删除不需要的行 |
DISTINCT | 从关系中删除重复的行 | |
FOREACH...GENERATE | 在关系中增加或删除字段 | |
STREAM | 使用外部程序对关系进行变换 | |
SAMPLE | 从关系中随机取样 | |
分组与连接 | JOIN | 连接两个或多个关系 |
COGROUP | 在两个或更多关系中对数据进行分组 | |
GROUP | 在一个关系中对数据进行分组 | |
CROSS | 获取两个或更多关系的乘积(叉乘) | |
排序 | ORDER | 根据一个或多个字段对某个关系进行排序 |
LIMIT | 将关系的元组个数限定在一定数量内 | |
合并与分割 | UNION | 合并两个或多个关系 |
SPLIT | 把某个关系切分两个或多个关系 |
有些语句并不加到逻辑计划中。例如,诊断操作DESCRIBE、EXPLAIN以及 ILLUSTRATE。这些操作是用来让用户能够与逻辑计划进行交互以进行调试的(见 表11-2)。DUMP也是一种诊断操作。它只能用于与很小的结果集进行交互调试, 或与LIMIT结合使用,来获得某个较大的关系的一小部分行。STORE语句应该在 输出包含较多行的时候使用。这是因为STORE语句把结果存入文件而不是在控制台显示。
表11-2.Pig Latin的诊断操作
操作 | 描述 |
---|---|
DESCRIBE | 打印关系的模式 |
EXPLAIN | 打印逻辑和物理计划 |
ILLUSTRATE | 使用生成的输入子集显示逻辑计划的试运行结果 |
为了在Pig脚本中使用用户自定义函数,Pig Latin提供了 REGISTER和DEFINE 语句(见表11-3)。
表11-3. Pig Latin UDF语句
语句 | 描述 |
---|---|
REGISTER | 在Pig运行时环境中注册一个JAR文件 |
DEFINE | 为UDF、流式脚本或命令规范新建别名 |
因为这些命令不处理关系,所以它们不会被加入逻辑计划。相反,这些命令会被立 即执行。Pig提供了与Hadoop文件系统和MapReduce进行交互的命令及其他一些 工具命令(见表11-4)。与Hadoop文件系统进行交互的命令对在Pig处理前和处理 后移动数据非常有用。
表11-4.Pig Latin命令类型命令描述
类别 | 命令 | 描述 |
---|---|---|
HadooFilesystem | cat | 打印一个或多个文件的内容 |
cd | 改变当前目录 | |
copyFromLocal | 把一个本地文件或目录复制到Hadoop 文件系统 | |
copyToLocal | 将一个文件或目录从Hadoop文件系统复制到本地文件系统 | |
cp | 把一个文件或目录复制到另一个目录 | |
fs | 访问Hadoop文件系统外壳程序 | |
Ls | 打印文件列表信息 | |
mkdir | 创建新目录 | |
mv | 将一个文件或目录移动到另一个目录 | |
pwd | 打印当前工作目录的路径 | |
rm | 删除一个文件或目录 | |
rmf | 强制刪除文件或目录(即使文件或目录不存在也不会失败) | |
HadoopMapReduce 工具 | kill | 终止某个MapReduce作业 |
exec | 在一个新的Gnmt外壳程序中以批处理模式运行一个脚本 | |
help | 显示可用的命令选项 | |
quit | 退出解释器 | |
run | 在当前Grunt外壳程序中运行脚本 | |
set | 设置Pig选项 |
文件系统相关的命令可以对任何Hadoop文件系统的文件或目录进行操作。这些命 令和hadoop fs命令很像(这是意料之中的,因为两者都是Hadoop FileSystem 接口的简单封装)。你可以使用Pig的fs命令访问所有的Hadoop文件系统外壳命 令。例如,十5-15显示文件列表,而fs-help显示关于所有可用命令的帮助信息。
准确地说,使用哪种Hadoop文件系统是由Hadoop Core站点文件中的fs.default.name属性决定的。
除了set命令以外,这些命令的含义大都不言自明。set命令用于设置控制Pig行 为的选项。debug选项用于在脚本中打开或关闭调试日志(你可以在启动Pig时用-d或-debug选项控制日志的级别)。
job.name是另一个很有用的选项。这个选项为Pig作业设定一个有意义的名称。 在共享的Hadoop集群上,可通过此名称知道哪个Pig MapReduce作业是自己的。 如果Pig正在运行某个脚本(而不是通过Gmnt运行交互式查询),默认作业名称为 脚本的名称。
表11-4中有两个命令可以运行Pig脚本:exec和run。它们的区别是:exec在一 个新的Grunt外壳程序中以批处理方式运行脚本。因此,所有脚本中定义的别名在 脚本运行结束后再也不能在外壳程序中够访问。另一方面,如果用run运行脚 本,那么效果就和在外壳中手工输入脚本的内容是一样的。因此,运行该脚本的外 壳的命令历史中包含脚本的所有语句。只能用exec进行多査询执行,即Pig以批 处理方式一次执行一批语句。不能用run进行多查询执行。
表达式
可以通过计算表达式得到某个值。在Pig中,表达式可以作为包含关系操作的语句 的一部分。Pig可以使用丰富的表达式类型。Pig中的很多表达式类型和其他编程 语言中的表达式相像。
类型
到目前为止,你已经见过Pig的一些简单数据类型,例如int和chararray。本 节我们将更详细地讨论Pig的内置数据类型。
Pig有四种数值类型:int、long、float和double。它们和Java中对应的数值 类型相同。此外,Pig还有bytearray类型,这类似于于表示二进制大对象的 Java的byte数组。Chararray类似于用UTF-16格式表示文本数据的 java.lang.String。Chararrary也可以加载或存储UTF-8格式的数据。Pig没有 任何一种数据类型对应于Java的boolean、byte、short,或char。Java的这 些数据类型都能方便地使用Pig的int类型(对数值类型)或chararray类型(对char)表示。
类别 | 数据类型 | 描述 | 文字示例 |
---|---|---|---|
数值 | int | 32位有符号整数 | 1 |
long | 64位有符号整数 | 1L | |
float | 32位浮点数 | 1.0F | |
double | 64位浮点数 | 1.0 | |
文本 | chararray | UTF-16格式的字符数组 | 'a' |
二进制 | Bytearray | 字节数组 | 不支持 |
复杂类型 | tuple | 任何类型的字段序列 | (1,'pomegranate') |
bag | 元组的无序多重集合(允许重复元组) | {(1, 'pomegranate'),(2)} | |
map | 一组键-值对。键必须是字 符数组,值可以是任何类 型的数据 | ['a' 'pomegranate'] |
复杂类型通常从文件加载数据或使用关系操作进行构建。但是,要当心,表11-6列 出的文字形式只是在Pig Latin程序中用来表示常数值。用PigStorage加载器从 文件加载的数据的原始形式往往与之不同。例如,文件中如表11-6所示的包的数 据可能形如{(1,pomegranate)}(注意,此时没有单引号)。如果有合适的模 式,该数据可以加载到一个关系。该关系只有一个仅有一个字段的行,而该字段的 值是包。
映射类型只能从文件。因为Pig的关系操作不能创建映射。如果需要,我们可以用 UDF生成映射。
虽然关系和包在概念上是相同的(本质上都是元组的无序集合),但事实上Pig对它 们的处理稍有不同。关系是顶层构造结构,而包必须在某个关系中。正常情况下, 不必操心它们的区别。但是,对它们的使用有一些限制。对于不熟悉它们区别的 人,这些限制仍然可能导致错误。例如,不能根据包文字直接创建一个关系。因 此,如下语句会运行失败:
A = {(l,2),(3,4)}; --Error
针对这种情况,最简单的解决办法是使用LOAD语句从文件加载数据。
另一个例子是,对待关系,不能像处理包那样把一个字段投影为一个新的关系(使 用位置符号,$0 指向A的第一个字段):
B = A.$0;
要做到这一点,必须使用关系操作将一个关系人转换为另一个关系B:
B = FOREACH A GENERATE $0;
PiLatin将来的版本可能采用相同的方法处理关系和包,以消除这种不一致性。
模式
Pig中的一个关系可以有一个关联的模式。模式为关系的字段指定名称和类型。前面介绍过LOAD语句的AS子句是如何关联模式与关系的:
grunt> records = LOAD ,input/ncdc/micro-tab/sample.txt' >> AS (year:int, temperature:int, quality:int); grunt> DESCRIBE records;records: {year; intjteniperature: int,quality: int}
这次,虽然仍然使用和上次的文件来加载数据,但年份已声明为整数类型的,而不 是chararray类型。如果要对年份这一字段进行算术操作(例如将它变为一个时间 戳),那么用整数类型更为合适,相反,如果只是想把它作为一个标识符,那么 chararray类型就更合适。Pig的这种模式声明方式提供了很大的灵活性。这和传 统SQL数据库要求在数据加载前必须先声明模式的方式截然不同。设计Pig的目 的是用于分析不包含数据类型信息的纯文本输入文件的,因此,它为字段确定类型 的时机不同于RDBMS也是理所当然的。
我们也可以完全忽略类型声明:
grunt> records = LOAD 'input/ncdc/micro-tab/sample.txt' >> AS (yeaT, temperature, quality); grunt> DESCRIBE records;records: {year: bytearray,temperature: bytearray,quality: bytearray}
在这个例子中,我们在模式中只确定了字段的名称:year、temperature和 quality。默认的数据类型为最通用的bytearray。它表示一个二进制串。
不必为每一个字段都给出类型。可以让某些字段的类型为默认的字节数组,就像如 下模式声明示例中的year字段:
grunt> records = LOAD 'input/ncdc/micro-tab/sample.txt' >> AS (year, temperature:int, quality:int); grunt> DESCRIBE records;records: {year: bytearray,temperature: int,quality: int}
但是,如果要用这种方式确定模式,必须在模式中定义每一个字段。同样,不能只 确定字段的类型而不给出其名称。另一方面,模式本身是可选的。可以省略AS子 句,如下所示:
grunt> records = LOAD 'input/ncdc/micro-tab/sample.txt'; grunt> DESCRIBE records;Schema for records unknown.
只能使用位置符号引用没有对应模式的关系中的字段:$0表示关系中的第一个字 段,$1表示第二个,依此类推。它们的类型都是默认的bytearray:
grunt> projected_records = FOREACH records GENERATE $0, $l, $2; grunt> DUMP projected_records;(1950,0,1)(1950,22,1)(1950,-11,1)(1949,lll,l)(1949,78,l)grunt> DESCRIBE projected_records;projected_records: {bytearray,bytearray,bytearray}
虽然不为字段分配类型很省事(特别是在写査询的开始阶段),但如果指定字段的类 型,我们能使Pig Latin程序更清晰,也使程序运行得更高效。因此,一般情况 下,建议指明字段的数据类型。
模式合并
在Pig中,不用为数据流中每一个新产生的关系声明模式。在大多数情况下,Pig 能够根据关系操作之输入关系的模式来确定输出结果的模式。
那么模式是如何传播到新关系的呢?有些关系操作并不改变模式。因此,LIMIT操 作(对一个关系的最大元组数进行限制)产生的模式就和它所处理的关系的模式相 同。对于其他操作,情况可能更复杂一些。例如,UNION操作将两个或多个关系合 并成一个,并试图同时合并输入关系的模式。如果这些模式由于数据类型或字段个 数不同而不兼容,那么UNION产生的模式是不确定的。
针对数据流中的任何关系,可以使用DESCRIBE操作来获取它们的模式。如果要重 新定义一个关系的模式,可以使用带AS子句的FOREACH...GENERATE操作来定义输 入关系的一部分或全部字段的模式。