MapReduce把对数据记录的所有操作都归结两个步骤--Map和Reduce。其中Map对现有数据做一个先期处理,
得到一个中间数据集,Reduce再对中间数据集进行去重、过滤等后期处理,最后得到你要的结果。Hadoop是一个MapReduce的实现,Nutch项目的大容量数据处理等功能就构建在Hadoop之上。
过程原形:
Map :: (InitialKey, IntialValue) -> [(InterKey, InterValue)]
Reduce :: (Interkey, InterValuesIterator) -> [(InterKey, InterValue)]
Map接收一个Key、Value对,返回一个Key、Value对(如果原始的Key、Value对不满足你的要求你可以不返回,或者你有特殊需求也可以返回多个,一般比较少见), Reduce接收一个Key和一个Values的Iterator,你可以根据情况返回零个或多个Key,Value对。Key是一个实现了org.apache.hadoop.WritableComparable接口的类,Value则实现了Writable,WritableComparable是Writable的子接口,Writable定义了输入输出(序列化)的接口,WritableComparable另外继承Comparable,因此Key总是有序的。
一个使用MapReduce的功能块典型结构如下:
新建一个JobConf
设置输入路径
设置输入文件格式
设置输入Key,Value类型
设置输出路径
设置输出文件格式
设置输出Key,Value类型
设置Partitioner
启动Job
执行完毕,你所有存放在输入路径下的数据都在被转换之后按照你指定的格式存放于输出路径中,
ok,我知道很多人都是看code比看document更兴奋,很不幸,我也是其中一员。
. 排序
比如你有一批URL,存放在文本文件c:/tmp/tmpin/urllist.txt中,一行一个
http://www.sohu.com
http://www.163.com
http://www.sina.com.cn
...
输入格式一般是SequenceFileInputFormat,但是对于urllist.txt这种按行排列的格式却是另外一种(我不知道的)格式,简单来说就是你不需要设定输入格式就能处理它了,在MapReduce传递给你的Map时,你可以忽略Key值,
而Value就是URL了,然后你做一个转换把Value(URL)作为Key来存放,然后你自己合成一个Value值,比如只是简单的一个数字1,或者是一个URL的相关信息,DocumentId, 预计抓取时间等。因为这次的任务是排序,而事实上经过Map处理后数据已经是按Key(URL)排好序了,所以Reduce可以什么也不做。
public class Main {
public static class InjectMapper extends MapReduceBase implements Mapper {
public void map(WritableComparable key, Writable val,
OutputCollector output, Reporter reporter) throws IOException {
UTF8 url = (UTF8) val;
UTF8 v = new UTF8("1");
output.collect(url, v); //生成数据
}
}
public static void main(String[] args) throws IOException {
JobConf job = new JobConf();
Path urlsPath = new Path("C:/tmp/tmpin");
job.setInputPath(urlsPath);
Path outputPath = new Path("c:/tmp/tmpout");
job.setOutputPath(outputPath);
job.setOutputFormat(SequenceFileOutputFormat.class);
job.setOutputKeyClass(UTF8.class);
job.setOutputValueClass(UTF8.class);
job.setMapperClass(InjectMapper.class);
//job.setReduceClass(InjectReducer.class);
JobClient.runJob(job);
}
}
. 去重
你的原始URL列表中可能有相同的URL,去除相同Key值记录的功能就需要Reduce了,定义一个类
public static class InjectReducer extends MapReduceBase implements Reducer {
public void reduce(WritableComparable key, Iterator values,
OutputCollector output, Reporter reporter) throws IOException {
output.collect(key, (Writable) values.next());
}
}
并传递给JobConf就Ok了,由此可以看出Reduce过程对于同一个Key只被调用一次,那个values的Iterator包含这个Key所对应的全部记录,你可以简单的只取第一条数据,对这些记录进行比较,得到你认为最有效的一条记录,或者统计每一个Key都有多少条记录,一句话,你可以做任何事情(事实上是任何MapReduce所支持的事情)。