hadoop的编程模型其实就是mapper和reducer,由于Hadoop是由Java比编写的,所以用Java来写任务就非常自然,但是每次都得打包上传,用起来有点不方便。
hadoop提供了工具:hadoop streaming。mapper和reducer从stdin和stdout中读取用户数据,逐行处理之后,再发送给标准输入输出。streaming工具会创建mapreduce作业,发送给各个tasktracker,同时监控整个作业的执行过程。
mapper启动时,会将脚本作为一个单独的进程启动。
mapper运行时,会把输入切分成行,并把每一行提供给可执行文件进程的标准输入。
mapper收集可执行文件标准输出的内容,并把每一行换成key/value对,作为mapper的输出(默认情况下,一行中第一个tag之前的部分作为key,之后的部分作为value,如果没有tag,那么整行为key,null为value)。
reducer与mapper类似。用perl写mapper的时候可以这样:
#!/usr/bin/perl -w
MAIN:
{
while (my $line = <STDIN>)
{
chomp($line);
next unless (length($line) > 0);
my @fields = split(/\t/, $line);
...
}
}
reducer的代码也类似。考虑一个实际的问题:通过user_id来join两个用户表,mapper得到的是两种都包含user_id的记录,那么在输出的时候可以是这样的:
print user_id."\t".userInfo."\n";
这样reducer中会把相同的user_id对应的不同的userInfo聚集起来。如果本来的数据就是符合这种格式的,那么,直接用cat就可以来作为mapper(或者reducer)。
这里介绍几个hadoop streaming相关的配置:
1、stream.map.output.field.separator:map的输出中key、value的分隔符。
2、stream.num.map.output.key.fields:分隔符的位置,之前的作为key,之后的作为value。
3、map.output.key.field.separator:map输出中key内部的分隔符。
4、num.key.fields.for.partition:指定分桶时,key按照分隔符切割后,其中用于分桶key所占的列数。
5、stream.reduce.output.field.separator:设置reduce输出中key和value的分隔符。
6、stream.num.reduce.output.key.fields:设置reduce程序分隔符的位置。
还有一个很有用的设置:
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner
在map和reduce还有一个patition的过程,使用KeyFieldBasedPartitioner可以方便地实现二次排序。比如map的输出为id.'\t'.sortkey.'\t'.value.'\n',此时设置:
num.key.fields.for.partition = 1
stream.num.map.output.key.fields = 2
那么就可以实现将map的输出根据id输出给不同的reducer,每个reducer接受的到的是根据sorkkey排序的。
到这里大家也应该清楚,如果使用c/c++或者是其他的什么语言来写,都是类似的写法。