转载必须注明出处: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将不会输出任何结果

FLATTEN操作本来会将嵌套展开,生成更多行的结果,但如果被展开的bag是空的,则一行记录也不会生成,这与你想像的可能有点不同:至少也应该生成一行结果吧?不会的,就是一行也不会生成。
在这里我用一个实例来说明。假设有数据文件 1.txt:

[root@localhost ~]$ cat 1.txt
1	2	{(a,b),(c,d)}
77	88	{(p,q),(r,s)}
123	555	{(u,w),(q,t)}

有三列,它们之间是以TAB分隔的。
以下代码:

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,a,b)
(1,2,c,d)
(77,88,p,q)
(77,88,r,s)
(123,555,u,w)
(123,555,q,t)

可见记录被解嵌套了。
但是,如果第三列的bag是空的:

[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脚本中,例如:

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 是一个关系操作符,但是它却可以对表达式进行操作!
我拿一个例子来说明这个问题。有以下数据文件:

[root@localhost ~]$ cat 1.txt 
1	2	3
2	5	3
2	6	7
8	6	3
1	5	7

有以下Pig代码(没什么计算上的意义,就是为了演示用):

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;

完全可以成功执行,结果为:

(3,1)
(7,1)
从这段代码能看出什么?首先,A.col3 是一个表达式(expression),而不是一个关系(relation),但是,DISTINCT 这个操作符却应用在了它上面,这说明,关系操作符完全是有可能应用在表达式上的。
某些书/资料上有一种说法是,上面的代码是有语法错误的,你必须用以下的代码来替代:

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是不起任何作用的(被忽略),因为在本地模式下所有操作都是串行执行的。

(未完待续)

[原创]使用Apache Pig时应该注意/避免的操作或事项
Tagged on:                             

2 thoughts on “[原创]使用Apache Pig时应该注意/避免的操作或事项

  • 2013 年 09 月 23 日 at 16:51
    Permalink

    你好,请问怎么实现在嵌套的 FOREACH 中对bag进行排序?

    Reply

发表评论

电子邮件地址不会被公开。 必填项已用*标注