注:之前写过MapReduce程序,所以对Hadoop和MapReduce编程有一些了解,现在正在阅读《Hadoop实战》一书,这里主要是记下对自己有用的一些点,完全不能覆盖书中的全部要点。想要学习MapReduce入门的同学请移尊步,勿浪费时间。
这里是我觉得不错的几个入门文章:
http://www.cnblogs.com/forfuture1978/category/300670.html
http://blog.csdn.net/aidayei/article/details/6580277
http://www.cnblogs.com/mdyang/category/307547.html
http://www.cnblogs.com/wycg1984/category/238035.html
-----------------------------------------------------------敌我分割线------------------------------------------------------------
ToolRunner类:用来减轻程序员解析一些命令选项任务负担的工具类。可以将main(String[] args)函数中的args传入ToolRunner的run函数,它会解析其中的诸如 –D key=value 等选项。
Hadoop Streaming:Hadoop本身是Java编写的,天然支持Java的MapReduce程序。Hadoop也支持其他语言,这需要借助Streaming。Streaming主要用作编写简答短小的MapReduce程序。Streaming使用Unix的流进行交互,从stdin读取数据,输出到stdout。数据必须为文本而每行被视为一条记录(这正是Unix常规的模式)。Streaming允许这些命令被视为mapper和reducer。
使用示例:
使用Unix命令的方式
hadoop jar contrib/streaming/hadoop-0.20-streaming.jar
-input myInput.txt
-output myOutput.txt
-mapper ‘myLinuxShellM -option’
-reducer ‘myLinuxShellR’
使用脚本的方式
cat myInput.txt | myPyShell.py > myOutput.txt
(显然cat 这种方式不适于大量数据)
Streaming方式使得这些处理标准输入输出的脚本等程序使用Hadoop。
Chapter 5:
多个MapReduce作业链:
串行化执行的若干MapReduce:
Job.runJob()本身就是执行完一个MapReduce任务。例如:
JobClient.runJob(jobConf1);
JobClient.runJob(jobConf2);
JobClient.runJob(jobConf3);
jobConf1,2,3会顺序执行。
具有复杂依赖关系的链:
Job类有一个方法addDependingJob,可以添加本作业所依赖的前一个作业。通过这种方式构造一个作业的有向无环路图DAG。
预处理和后处理阶段的链:
类似于经典的WordCount的MapReduce作业,我们期望在统计各个单词出现次数之前先删掉一些类似于 a the 等不关心的词,再进行词的转化Stemming(将finished finishing都换成finish)操作,这样我们需要这样的流程 --- MAP+ | REDUCE |MAP*
例如: MAP1 | MAP2 | REDUCE | MAP3 | MAP4。这需要ChainMapper / ChainReducer类。
连接Join(联结)
Join操作在SQL中比较容易写,在Hadoop中略麻烦。
连接分为Reduce端连接,基于DistributedCache的复制连接,map侧过滤后在reduce侧连接。(权威指南一书上写的是Map端连接和Reduce端的连接。)
之前写过一个reduce端的Join程序,reduce端连接涉及组合键和标签,分区函数,分组函数。简单示例:
(Ai,Bi)与(Ai,Ci)进行连接,则先map成(<Ai,Tag_B>,Bi), (<Ai,Tag_C>,Ci),然后编写分区函数和分组函数,使得同一个Ai对应的(<Ai,Tag_B>,Bi),(<Ai,Tag_C>,Ci)被分到同一个reducer,reducer就可以进行该Ai键的数据进行连接。
基于DistributedCache的复制连接:
考虑这样的一种情况,某地电话公司有两个数据集,电话号码和姓名数据集,电话号码和通话记录数据集,即(num,name)和(num,histroy),这两个数据集大小差别两到三个数量级,前者可能有几百万,后者可能有几十亿记录,所以进行连接时,可以这样做:将大的数据集分散在各个计算节点中,将小的数据集在所有节点上进行复制(每一个节点都有一个完全的小的数据集),然后在Map阶段对于分散的大数据集的每一个分片,读取一条记录,从小数据集中查询对应的记录进行连接。这个称之为复制连接(replicated join)。
Hadoop中有一个分布式缓存(DistributedCache)的机制,实现将小的数据集复制到所有的节点。程序实现中几个要点:首先在配置任务时,用DistributedCache类的addCachedFile告诉Hadoop将小数据集复制到各个节点上。在Map时,可以在Mapper类的configure函数(override)中用普通的Java IO读取这个小数据集(因为这个小数据集文件已经在本地了,所以可以直接用Java IO读取)。这样,每个节点都有一个大数据集的分片和整个小数据集,就可以在Mapper中进行连接了。举个栗子:
这样Mapper只要对大数据集的分片的每个记录,查找响应的项,拼接即可。
map侧过滤后在reduce侧连接:有时候小数据集并不那么小(小到能放置到内存中),但是小数据集经过处理可以使用基于DistributedCache的复制连接,例如,需要进行Order(id,product,price… …)和Customer(id,name… …)进行连接,并选出属于415地区的那些连接结果,则可以先map时过滤掉不是415地区的记录,生成一个Custom415文件,在对这个文件进行复制链接。如果过滤之后仍然不足够小,就需要进行reduce端连接了。还有一种对这个reduce端连接进行优化的方案,对于Custome415,我们生成一个CustomID415,这个文件只包含415地区的用户的ID(没有name等属性),这个会小得多,假设CustomID415已经可以放在内存中,对其进行分布式缓存,则Order和Customer进行map时,先用这个文件进行过滤,再到reduce端进行连接。
如果刚才那个假设不成立(假设CustomID415已经可以放在内存中),CustomID415仍然太大,不能放置在内存中或者能放在内存但是查询太慢,那怎么办?书中介绍了BloomFilter这个东西。
BloomFilter的详细信息参见 http://blog.csdn.net/jiaomeng/article/details/1495500
BloomFilter可以使数据量进一步减小。