博客园  :: 首页  :: 联系 :: 管理

读书笔记-Hadoop实战-4

Posted on 2012-09-06 19:04  Apprentice89  阅读(377)  评论(0编辑  收藏  举报

注:之前写过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可以使数据量进一步减小。