当前位置: 首页 > 工具软件 > Apache Pig > 使用案例 >

Apache Pig中文教程(进阶)go on

黎奇略
2023-12-01

(3)重载(overloading)一个UDF
类似于C++的函数重载,pig中也可以重载UDF,例如一个函数ADD可以对两个int进行操作,也可以对两个double进行操作,那么我们可以为该函数实现 getArgToFuncMapping 方法,该函数返回一个 List<FuncSpec> 对象,这个对象中包含了参数的类型信息。具体怎么实现,可以看这个链接(搜索“Overloading UDFs”定位到所需章节)。

(4)pig运行不起来,提示“org.apache.hadoop.ipc.Client - Retrying connect to server: localhost/127.0.0.1:9000. Already tried 1 time(s)”错误的解决办法
发生这个错误时,请先检查Hadoop的各个进程是否都运行起来了,例如,在我的一次操作中,遇到这个错误时,我发现Hadoop namenode进程没有启动起来:

1
ps -ef | grep java | grep NameNode

应该有两个进程启动起来了:

org.apache.hadoop.hdfs.server.namenode.NameNode
org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode

如果没有,那么你要到Hadoop安装目录下的“logs”目录下,查看NameNode的日志记录文件(视用户不同,日志文件的名字也会有不 同),例如,我的NameNone日志文件 hadoop--namenode-root-XXX.log 的末尾,显示出有如下错误:

ERROR org.apache.hadoop.hdfs.server.namenode.NameNode: org.apache.hadoop.hdfs.server.common.InconsistentFSStateException: Directory /tmp/hadoop-root/dfs/name is in an inconsistent state: storage directory does not exist or is not accessible.

文章来源:http://www.codelast.com/
我到它提示的地方一看,果然不存在最后一级目录(我是伪分布式运行的Hadoop,不要觉得奇怪),于是手工创建了这个目录,然后停掉Hadoop:

1
stop-all.sh

稍候一会儿再重新启动Hadoop:

1
start-all.sh

然后再去看一下NameNode的日志,又发现了新的错误信息:

ERROR org.apache.hadoop.hdfs.server.namenode.NameNode: java.io.IOException: NameNode is not formatted.

这表明NameNode没有被格式化。于是将其格式化:

1
[root@localhost bin] # hadoop namenode -format

命令行问你是否要格式化的时候,选择YES即可。格式化完后会提示:

common.Storage: Storage directory /tmp/hadoop-root/dfs/name has been successfully formatted.

说明成功了。这个时候,再像前面一样重启Hadoop进程,再去看NameNode的日志文件的最后若干行,应该会发现前面的那些错误提示没了。这 个时候,再检查Hadoop各进程是否都成功地启动了,如果是的话,那么这个时候你就可以在Hadoop的伪分布式模式下启动pig:

1
[root@localhost home] # pig

而不用以本地模式来运行pig了(pig -x local)。
总之,配置一个伪分布式的Hadoop来调试pig在某些情况下是很有用的,但是比较麻烦,因为还牵涉到Hadoop的正确配置,但是最好搞定它,以后大有用处啊。

(5)用Pig加载HBase数据时遇到的错误“ERROR 2999: Unexpected internal error. could not instantiate 'com.twitter.elephantbird.pig.load.HBaseLoader' with arguments XXX”的原因之一
你也许早就知道了:Pig可以加载HBase数据,从而更方便地进行数据处理。但是在使用HBase的loader的时候,可能会遇到这样那样的问题,我这里就遇到了一例,给大家分析一下原因。
使用 org.apache.pig.backend.hadoop.hbase.HBaseStorage() 可以加载HBase数据,例如:

1
A = LOAD 'hbase://table_name' USING org.apache.pig.backend.hadoop.hbase.HBaseStorage( 'column_family_name:qualifier_name' , '-loadKey true -limit 100' ) AS (col1: chararray, col2:chararray);

其中,table_name 是你要加载数据的HBase表名,column_family_name:qualifier_name 是表的column family:qualifier(当然,可以有多个column family:qualifier,以空格隔开即可),-loadKey true -limit 100 是加载数据时指定的参数,支持的参数如下:

-loadKey=(true|false)  Load the row key as the first column

-gt=minKeyVal

-lt=maxKeyVal 

-gte=minKeyVal

-lte=maxKeyVal

-limit=numRowsPerRegion max number of rows to retrieve per region

-delim=char delimiter to use when parsing column names (default is space or comma)

-ignoreWhitespace=(true|false) ignore spaces when parsing column names (default true)

-caching=numRows  number of rows to cache (faster scans, more memory).

-noWAL=(true|false) Sets the write ahead to false for faster loading.

    To be used with extreme caution, since this could result in data loss

    (see http://hbase.apache.org/book.html#perf.hbase.client.putwal).

由这些参数的解释,可知我上面的 -loadKey true 使得加载出来的数据的第一列是HBase表的row key;-limit 100 使得从每一个region加载的最大数据行数为100(当你有N个region时,总共加载的数据是不是 N*region总数 条,我没有试验)。
org.apache.pig.backend.hadoop.hbase.HBaseStorage() 包含在Pig的jar包中,所以你不需要REGISTER额外的jar包。
我遇到的问题是:在按上面的代码加载HBase数据之后,在grunt中一回车,马上报错:

ERROR 2999: Unexpected internal error. could not instantiate 'com.twitter.elephantbird.pig.load.HBaseLoader' with arguments XXX
Details at logfile: XXX

这个时候,你当然应该去查看logfile,以确定具体问题是什么。
logfile内容较多,在其尾部,有下面的内容:

Caused by: java.lang.NoSuchMethodError: org.apache.hadoop.hbase.HBaseConfiguration.create()Lorg/apache/hadoop/conf/Configuration;

        at org.apache.pig.backend.hadoop.hbase.HBaseStorage.<init>(HBaseStorage.java:185)

文章来源:http://www.codelast.com/
好吧,到了这里,只能去看看pig的源码了。打开 HBaseStorage.java 文件,找到提示的185行,看到如下代码:

1
m_conf = HBaseConfiguration.create();

可见它调用了HBase代码中的一个类HBaseConfiguration的create方法。按上面的提示,它是找不到这个方法,于是我们再看 看使用的HBase的 HBaseConfiguration.java 里的代码,找遍全文,都找不到create方法!那么,我们再看看更新一点的版本的HBase的相同文件中是否有这个方法呢?下载0.90.4版本的 HBase,果然在 HBaseConfiguration.java 中找到了create方法:

1
2
3
4
5
6
7
8
/**                                                                                                                                                      
  * Creates a Configuration with HBase resources                                                                                                      
  * @return a Configuration with HBase resources                                                                                                      
  */
public static Configuration create() {
   Configuration conf = new Configuration();
   return addHbaseResources(conf);
}

所以,问题就在这里了:Pig的HBase loader不能使用某些版本的HBase,升级HBase吧!
另外,就算HBase版本适用了,你也得让Pig知道HBase的参数配置(要不然怎么可以指定一个HBase表名就可以加载其数据了呢),具体你可以看这个链接的说明。

(6)JOIN的优化
如果你对N个关系(relation)的某些字段进行JOIN,也就是所谓的“多路的”(multi-way)JOIN——我不知道用中文这样描述是否正确——在这种情况下,请遵循这样的原则来写JOIN语句:
JOIN用到的key所对应的记录最多的那个关系(relation)应该被排在最后。例如:

1
D = JOIN A BY col1, B BY col1, C BY col1;

在这里,假设C这个relation就是上面所说的那种情况,所以把它排在最后。
文章来源:http://www.codelast.com/
为什么要遵循这样的原则?这是由Pig处理JOIN的方式来决定的。在JOIN的n个关系中,前面的n-1个关系的处理结果会被cache在内存中,然后才会轮到第n个关系,因此,把最占内存的放在最后,有时候是能起到优化作用的。

(7)错误“Backend error : org.apache.pig.data.BinSedesTuple cannot be cast to org.apache.pig.data.DataBag”的原因
如果你正在使用Pig 0.8,那么要注意了:出现这个错误,可能是Pig的bug导致的,详见这个链接。
说得简单点就是:此bug会导致无法解引用一个tuple中的bag。通常我们取一个tuple中的bag,是为了FLATTEN它,将记录展开,但是此bug使得你根本连tuple中的bag都输出不了。
此bug并不会影响你的Pig脚本语法解析,也就是说,你的Pig脚本只要写对了,就能运行起来,但是它执行到后面会报错。

(8)如何加载LZO压缩的纯文本数据
如果你的数据是纯文本经由LZO压缩而成,那么你可以用elephant-bird的 com.twitter.elephantbird.pig.store.LzoPigStorage 来加载它:

1
A = LOAD '/user/codelast/text-lzo-file' USING com.twitter.elephantbird.pig.store.LzoPigStorage() AS (col1: chararray, col2: int );

注意,这里没有写REGISTER jar包的命令,你需要自己补上。

(9)如何用Pig设置map端的并行度(map数)
这个链接中的第(9)条,我们知道,无法通过PARALLEL来设置Pig job map端的并行度,但是,有没有什么办法可以间接实现这一点呢?
在Java编写的MapReduce程序中,你可以像这个链接中的第(25)点所说的一样,通过FileInputFormat.setMinInputSplitSize()来间接更改map的数量,其实它就与设置 mapred.min.split.size 参数值的效果是一样的。
在Pig中,我们是可以通过set命令来设置job参数的,所以,我们如果在Pig脚本的开头写上:

1
set mapred. min .split. size 2147483648;

将使得对map来说,小于2G的文件将被作为一个split输入,从而一个小于2G的文件将只有一个map。假设我们的Pig job是一个纯map的job,那么,map数的减少将使得输出文件的数量减少,在某些情况下,这种功能还是很有用的。
注意:上面的命令中,set的参数的单位是字节,所以2G=2*1024*1024*1024=2147483648。

(10)Pig调用现存的静态Java方法
不是每个人都会开发UDF,或者每个人都愿意去写一个UDF来完成一件极其简单的操作的,例如,把一个编码过的URL解码,如果我只想临时用一下这个功能,那么我还要去写一个UDF,累不累啊?
我们知道,java.net.URLDecoder.decode 这个静态方法已经实现了URL解码功能:

static String decode(String s, String enc) 

使用指定的编码机制对 application/x-www-form-urlencoded 字符串解码

那么,如何在Pig中使用这个现成的静态方法呢?为了展示这个使用过程,我造了一个数据文件:

1
2
3
[root@localhost ~]$ cat a.txt
 
http: //zh .wikipedia.org /zh/ %E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E

就一行,这个URL解码之后应该是:
http://zh.wikipedia.org/wiki/搜索引擎
因为里面含中文,所以被编码了。
处理此文件的Pig脚本url.pig如下:

1
2
3
4
DEFINE DecodeURL InvokeForString( 'java.net.URLDecoder.decode' , 'String String' );
A = LOAD 'a.txt' AS (url: chararray);
B = FOREACH A GENERATE DecodeURL(url, 'UTF-8' );
STORE B INTO 'url' ;

文章来源:http://www.codelast.com/
用 pig -x local url.pig 执行这个脚本,完成后我们查看输出目录下的 part-m-00000 文件内容,可见它确实被解码成了正确的字符串。
这样,我们就利用了现存的静态Java方法来偷了个懒,很方便。
需要注意的是:只能调用静态方法,并且此调用比同样的UDF实现速度要慢,这是因为调用器没有使用Accumulator或Algebraic接口。根据这位兄台的测试,百万条的记录规模下,调用Java静态方法比UDF大约要慢一倍。至于这样的cost能不能接受,就看你自己的判断了。


转载于:https://my.oschina.net/u/1170537/blog/384434

 类似资料: