转载必须注明出处:http://www.codelast.com/
Apache Pig是用来处理大规模数据的高级查询语言,配合Hadoop使用,可以在处理海量数据时达到事半功倍的效果,比使用Java,C++等语言编写大规模数据处理程序的难度要小N倍,实现同样的效果的代码量也小N倍。
本文基于以下环境:
pig 0.8.1
文章来源:http://www.codelast.com/
(1)CROSS操作
由于求交叉积可能会导致结果数据量暴增,因此,CROSS操作是一个“昂贵”的操作,可能会耗费Hadoop集群较多的资源,使用的时候需要评估一下数据量的大小。
(2)JOIN操作的顺序
如教程《Apache Pig中文教程(进阶)》中的第(6)条所写,当JOIN的各数据集分布严重不均时,你最好考虑一下JOIN的顺序,可以对你的job效率提升有帮助。
(3)FLATTEN一个空的bag将不会输出任何结果
1 2 3 4 | [root@localhost ~]$ cat 1.txt 1 2 {(a,b),(c,d)} 77 88 {(p,q),(r,s)} 123 555 {(u,w),(q,t)} |
有三列,它们之间是以TAB分隔的。
以下代码:
1 2 3 | A = LOAD '1.txt' AS (col1: int , col2: int , col3: bag{t: ( first : chararray, second : chararray)}); B = FOREACH A GENERATE col1, col2, FLATTEN(col3); DUMP B; |
得到的结果是显而易见的:
1 2 3 4 5 6 | (1,2,a,b) (1,2,c,d) (77,88,p,q) (77,88,r,s) (123,555,u,w) (123,555,q,t) |
可见记录被解嵌套了。
但是,如果第三列的bag是空的:
1 2 3 4 | [root@localhost ~]$ cat 1.txt 1 2 {} 77 88 {} 123 555 {} |
那么,与上面同样的代码将什么也不会输出(你可以自己试验一下)。
这一点需要特别注意,所以一般来说,你在FLATTEN一个bag之前,需要判断一下它是否是空的(IsEmpty),如果你需要在FLATTEN的结果中标记空的那些bag,那么你就需要自己在FLATTEN之前将空的bag替换为自己指定的内容。
(4)SAMPLE的结果数量是不确定的
SAMPLE操作符可以对一个关系(relation)进行取样,得到其一定百分比的数据(例如随机取其中10%的数据),但是,这并不保证对同一个relation进行同样比例的SAMPLE,得到的tuple的数量就是相同的。例如,对一个有千万行的数据集,SAMPLE 0.1的结果,可能第一次会得到100万行,重做一次却得到了101万行(这里只是举一个例子,具体的数字是未知的)。
我的试验结果可以肯定地告诉大家:我拿一个含上亿条记录的数据集SAMPLE 0.1两次的结果相差了4万多条记录(指的是数据条数)。
(5)输出几个简单数据的job,没必要单独跑,使用UNION整合在一个Pig脚本中即可
假设有几个Pig job,它们输出的都是一行数据(当然,一行可能有多列),那么没必要单独跑几个job再得到所有结果,你可以用UNION把它们整合放在一个Pig脚本中,例如:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | A1 = LOAD '1.txt' AS (col: chararray); A2 = LOAD 'a.txt' AS (col: chararray); A2 = LOAD 'P.txt' AS (col: chararray); B1 = GROUP A1 ALL ; C1 = FOREACH B1 GENERATE COUNT (A1); B2 = GROUP A2 ALL ; C2 = FOREACH B2 GENERATE COUNT (A2); B3 = GROUP A3 ALL ; C3 = FOREACH B3 GENERATE COUNT (A3); U = UNION C1, C2, C3; STORE U INTO 'res' ; |
有人说,为什么不把A1,A2,A3使用通配符一起加载?答:这里我假设了一种非常简单的情况:三个数据文件都只有一列,而实际中,可能三个文件完全有不同的格式,而且后面的处理针对每个job也是不同的(在这里为简单起见才写成相同的),因此,单独加载有时候是必要的。
这样做之后,会输出3个文件,每个文件中有一个数。比跑3个Pig job方便。
文章来源:http://www.codelast.com/
(6)用ORDER排序时,Pig并不遵守“相同的key的记录会被发送到同一个partition”的惯例
处理海量数据时,我们常常会遇到这样一种情况:某些key的数据远远多于其他key的数据。例如,我们要分析用户的web访问日志,会发现用户访问Google的次数远远多于其他网站的次数。
所以,如果我们要按“访问的网站”这个字段来GROUP或者ORDER的话,就会造成落入某些reducer的数据远远多于其他reducer(GROUP、ORDER都会触发reduce过程)——注意,这里说“某些reducer”,是因为在这个例子中,不仅访问Google是个大户,可能还有其他的网站访问大户。
由于这些reducer需要处理的数据量特别大,也就会导致所需的时间特别长,从而整个job所需的总时间特别长。为了解决这一弊端,Pig使用了一种聪明的方法:先对需要ORDER的数据进行采样,获知其key分布情况,然后根据此分布构造一个可以均衡全体数据的partitioner,从而将数据比较均匀地送到N个reducer上。Pig的这种算法是很有效的,它使得各个reducer之间的执行时间相差不大。
正因为Pig做了这样的工作,所以,前面例子中所说的对Google的访问记录可能会被送到多个reducer中,它们有相同的key,却没有被送到同一reducer中,这没有遵守MapReduce的惯例。如果你的数据处理流程需要遵守此惯例,那么就不能用Pig的ORDER来排序。
同时,正因为Pig在ORDER时需要对输入数据进行采样,所以,ORDER的时候Pig会为你的数据流程添加一个额外的轻量job来完成采样工作——从Pig在控制台输出的信息中,你可以看到一个Feature为“SAMPLER”的job,这个job就是采样用的。
(7)关系操作符(Relational Operator)只能对关系(relation)进行操作,而不能对表达式(expression)进行操作吗?
有人说,从这一句的陈述来看,它根本就是废话——关系操作符当然是操作关系的啊!
不过,答案是:不一定。例如,DISTINCT 是一个关系操作符,但是它却可以对表达式进行操作!
我拿一个例子来说明这个问题。有以下数据文件:
1 2 3 4 5 6 | [root@localhost ~]$ cat 1.txt 1 2 3 2 5 3 2 6 7 8 6 3 1 5 7 |
有以下Pig代码(没什么计算上的意义,就是为了演示用):
1 2 3 4 5 6 7 | A = LOAD '1.txt' AS (col1: int , col2: int , col3: int ); B = GROUP A BY col3; C = FOREACH B { E = DISTINCT A.col3; GENERATE group , COUNT (E); }; DUMP C; |
完全可以成功执行,结果为:
1 2 3 4 5 6 7 8 | A = LOAD '1.txt' AS (col1: int , col2: int , col3: int ); B = GROUP A BY col3; C = FOREACH B { D = A.col3; E = DISTINCT D; GENERATE group , COUNT (E); }; DUMP C; |
这段代码确实没有错误,它先通过 A.col3 这个表达式,生成了一个关系(relation) D,再将 DISTINCT 关系操作符应用于其上,这样就避开了所谓的“关系操作符只能操作关系”的限制。但事实上,我们完全没有必要这样做,正如上面的代码的试验结果,你完全可以直接用 DISTINCT A.col3,并不会出错。
所以说,书本也有坑爹的时候,不可全信。
(8)嵌套的 FOREACH 中的代码是串行执行的
嵌套的 FOREACH 语句可以完成复杂的操作,例如在嵌套的 FOREACH 中对bag进行排序等。FOREACH会被并行执行,但是对嵌套在其中的子语句来说,它们却是串行执行的。
(9)PARALLEL只对强制产生reduce过程的操作符有效
通过PARALLEL,你可以控制Pig程序的并行度(说白了就是控制reducer的数量)。PARALLEL可以附加在任何关系操作符上,但是它只对reduce端的并行度起控制作用,因为MapReduce不允许用户自己控制map端的并行度,它只允许用户控制reduce端的并行度,所以,这就是PARALLEL只对强制产生reduce过程的操作符有效的原因了。
另外,在本地模式(pig -x local)下,PARALLEL是不起任何作用的(被忽略),因为在本地模式下所有操作都是串行执行的。
(未完待续)
联系客服