第一部分Hadoop 分布式的编程框架 

第一章Hadoop 简介 
1
philosophy move-code-to-data,适合数据密集性应用。 
2
SQL database VS Hadoop: 
   1) SCALE-OUT INSTEAD VS SCALE-UP 
   2) Key/value
VS 关系表:无结构、半结构数据VS 结构化的数据 
   3
)函数式编程(MapReduce)VS 声明式编程(SQL):hivecan map the sql to the job 
   4)
离线批处理VS 在线事务处理 
3
、理解MapReduce 
  1
2个阶段: 
       map
:转换+过滤数据: -> list(
       reduce
-> list(
       map
reduce之间按照key进行grouphadoop负责处理、只需要写mapreduce程序 
  2
word count 例子 


第二章Starting Hadoop 
1
Blocks of Hadoop: 
  NameNode: Master, bookkeeper of the HDFS
keepstrack of how your fi les are broken down into file blocks, which nodes storethose blocks, and the overall health of the distributed filesystem 
 
内存、I/O密集型。单点,但SNN可以作为master的备用 

  DataNode: Slave of the HDFS,
存储数据的节点、冗余备份、向NameNode报告本地数据的变化。 
  
  Secondary NameNode
SNN):作为master的备用节点、获得NameNodeHDFS元数据的快照、集群的配置 
  
  JobTracker
:分配提供的job成为多个task,监控各个task,检测各个task的心跳,重启动失败的任务。计算中的master 
  
  TaskTracker
:负责执行JobTracker分配的单个任务,像JobTracker发送心跳信息。每个DN节点一个TaskTracker,但它可以创建多个jvm实例,并行的处理多个mapreduce的任务。 
 
计算中的slave 

2
、安装Hadoop 
三种模式:Local (standalone) modePseudo-distributed modeFullydistributed mode 

3
Web-based cluster UI查看节点和job的信息 


第三章Hadoop各个组件 
这章主要从程序员的角度介绍了Hadoop的计算框架。 
3.1
HDFS文件系统下工作 
HDFS
是为分布式计算框架设计的大规模的分布式数据处理而设计的。 
Hadoop shell
提供了很多类似Unix的命令行工具,是HDFS系统的主要接口。 
Hadoop
也提供了HDFS的编程接口。 

3.1.1
基本的文件命令 
基本形式:hadoop fs -cmd  
hadoop fs -ls 
hadoop fs -lsr #
相当于linuxls-r 
hadoop fs -put example.txt . #
example.txt从本地文件系统copyHDFS上。 
hadoop fs -get example.txt . #
HDFSexampleget到本地 
hadoop fs -cat example.txt  #
相当于linuxcat 
hadoop fs -tail example.txt #linux tail 
可以结合Unix管道: 
hadoop fs -cat example.txt | head -n 10 
hadoop fs -rm example.txt #linux rm 
查看帮助,比如ls的帮助: 
hadoop fs -help ls 

可以使用URI来制定精确的文件和目录位置: 
hadoop fs -cathdfs://localhost:9000/user/chunk/example.txt 
如果处理本地文件系统,那么可以通过配置fs.default.name来配置默认的file://scheme部分。 

Xml代码  

1.   

2. fs.default.name  

3. hdfs://localhost:9000  

4.   


这样就可以直接hadoopfs -cat /user/chunk/example.txt 

3.1.2
编程的方式读写HDFS 
Java
编程方式操作HDFS,主要在org.apache.hadoop.fs包下面。Hadoop文件操作主要包括: 
打开、读、写、关闭,不仅可以操作HDFS,也可以操作本地普通的文件系统。 

FileSystem
:是文件系统的交互的一个抽象类,有很多具体的子类来处理HDFS和本地文件系统。可以使用:FileSystem.get(Configurationconf)这个工厂来创建期望的实例。 
Configuration
:只有key/value配置参数的类。默认的配置是基于HDFS系统的资源配置的。 

Java代码  

1. Configuration conf = new Configuration();  

2. FileSystem hdfs = FileSystem.get(conf);  


FileSystem.getLocal(Configuration conf)
可以创建一个针对本地的文件系统。 

Path:
文件和目录的名字 
FileStatus:
文件和目录的元数据信息 

Java代码  

1. FileSystem local = FileSystem.getLocal(conf);  

2. Path inputDir = new Path(args[0]);  

3. FileStatus[] inputFiles = local.listStatus(inputDir);  



FSDataInputStream: 

Java代码  

1. FSDataInputStream in = local.open(inputFiles[i].getPath());  

2. byte buffer[] = new byte[256];  

3. int bytesRead = 0;  

4. while( (bytesRead = in.read(buffer)) > 0 ){  

5. //...  

6. }  

7. in.close();  


FSDataInputStream
javaDataInputStream的子类,支持随机访问

FSDataOutputStream:
FSDataInputStream相对应的输出流

Java代码  

1. Path hdfsFile = new Path(args[1]);  

2. FSDataOutputStream out = hdfs.create(hdfsFile);  

3. out.write(buffer,o,bytesRead);  

4. out.close();  



3.2
一个MapReduce程序剖析 
Map Reduce
数据流: 
 

3.2.1 Hadoop
数据类型 
MapReduce
key,value不能是普通的class,它需要key/value实现序列化的方法, 
key
还需要具有可比较性。所以MapReduce对基本类型进行了封装。 
一般key/value会实现WritableComparable接口,valueWritable接口。 
Hadoop
预定义了一些对基本类型封装的类型:BooleanWritable,ByteWritable, 
DoubleWritable,FloatWritable,IntWritable,LongWritable,Text,NullWritable
 
你可以自己定义类型,实现Writable或者WritableComparable接口。 

3.2.2 Mapper 
作为一个Mapper,一般实现了Mapper接口并且继承了MapReduceBase类。MapReduceBase从名字可以看出,作为MapperReducer的基类。 
有两个方法作为构造和析构: 
void configure(JobConf job)
在数据处理之前调用,加载配置项 
void close()
map任务结束调用,进行资源回收,比如数据库连接、打开文件关闭。 

Mapper
接口负责数据处理阶段,他有一个map方法,来处理key/value对: 

Java代码  

1. void map(K1 key, V1 value, OutputCollector output,Reporter reporter)  

2. throws IOException  


这个方法给定输入(k1,v1)得到list(k2,v2) 
OutputCollector
接受mapper过程的结果,Reporter记录了任务进度的相关信息。 
Hadoop
预定义了一些Mapper: 
IdentityMapper:
实现了Mapper将输入直接映射为输出 
InverseMapper:
实现了Mapper逆置key/value 
RegexMapper:
实现了Mapper,对匹配的项生成(match,1) 
TokenCount:
实现了Mapper,生成(token,1) 

3.2.3 Reducer 
Reducer
Mapper一样都继承了MapReduceBase类,同时还实现了Reducer接口,它包含了 
单个方法: 

Java代码  

1. void reduce(K2 key,Iterator values,OutputCollector output,  

2.  Reporter reporter) throws IOException  


Reducer
接受到各个mapper的输出,将key/value对按照key进行排序然后按照key进行分组。 
然后调用reduce函数。OutputCollection接收reduce过程的输出,并将输出写入文件中。 
Reporter
记录了reducer任务的进度的额外信息。 

Hadoop
默认实现了一些Reducer 
IdentityReducer
:实现了Reducer将输入直接映射为输出。 
LongSumReducer:
实现了Reducer,计算出一个key所有value的和。 

3.2.4
划分--Mapper的输出重定向 
一个常见的误解是,MapReduce程序只有一个Reducer 
有多个Reducer就需要将mapper的输出正确的发送的某个Reducer上。默认的是将key进行hash 
然后决定输出到哪个Reducer上,Hadoop提供了HashPartitioner类。 
有时候我们需要自定义Partitioner,需要实现configure() 
getPartition()
方法,configure根据hadoopjob的配置来配置partitioner 
getPartition
返回分配到的reducer的号,大小从0reducer数。 

比如分析航线信息,计算从离开飞机场乘客的数量。 

引用


(San Francisco, Los Angeles) Chuck Lam 
(San Francisco, Dallas) James Warren 
... 


我们实现EdgePartitioner: 

Java代码  

1. public class EdgePartitioner implements Partitioner  

2. {  

3. @Override  

4. public int getPartition(Edge key, Writable value, int numPartitions)  

5. {  

6. return new Long(key.getDepartureNode()).hashCode() % numPartitions;  

7. }  

8. @Override  

9. public void confi gure(JobConf conf) { }  

10.}  



3.2.5
组合--本地reducer 
很多MapReducer程序,在分发mapper结果之前希望进行一次本地的Reducer操作。 
比如WordCount的例子,如果一个job处理一个文档包含the 574词,存储和shuffle(the,574)一次要比多次(the,1)要高效。 

3.2.6 Word Counting
和预定义的MapperReducer 
使用hadoop预定义的TokenCountMapperLongSummReducer类重写rWordCount例子。 

3.3
读和写 
MapReduce
需要读取输入的数据,写输出的数据,所以文件的格式需要关注。hadoop提供了 
灵活的处理各种数据格式的方法。 
每个split大小要合适,既要足够小,提供并行处理能力,又不能太小,以至于启动和停止的时间占了大部分。 
Hadoop
FSDataInputStream具有随机读的能力,所以能够有效的定位到文件split的位置。 

Hadoop
提供一些数据格式,你还可以自定义格式。 

3.3.1
输入格式: 
InputFormat
接口:所有的实现输入文件split uphadoop读取实现的接口。 

TextInputFormat:
默认的InputFormat实现类。这对于没有定义key的,但是想一行一行处理的数据来说非常有用。每一行一条记录 
key:
当前行的byte offset, LongWritable 
value
:当前行,Text 

KeyValueTextInputFormat:
每行一条记录,第一个分隔符将一行分开, 
key
分割符之前的部分,Text 
value:
分割符之后的部分,Text 

SequenceFileInputFormat:
一种对于一个MapReducejob是另一个MapReduce输入的一种优化的格式: 
key: K
用户定义 
value: V
用户自定义 

NLineInputFormat:
TextInputFormat类似,每个split保证含有N行,mapred.line.input.format.linespermap属性,默认是1,设置了
key: LongWritable 
value: Text 

你可以在配置输入使用的格式: 
conf.setInputFormat(KeyValueTextInputFormat.class); 

创建自定义的输入格式: 
有时候hadoop提供的标准的几个输入格式不能满足要求,需要自定义。InputFormat接口 
包含了两个方法: 

Java代码  

1. public interface InputFormat{  

2.   InputSplit[] getSplits(JobConf job, int numSplits) throws IOException;  

3.   RecordReader getRecordReader(InputSplit split,  

4.                                     JobConf job,  

5.                                     Reporter reporter) throws IOException;  

6. }  


这两个方法提供的功能: 
1.
将输入数据分成输入的split,每一个map任务处理一个split 
2.
提供迭代给定split的每个记录的能力,并且能够将每个记录解析成预定义类型的keyvalue 

一般继承FileInputFormat,它实现了getSplits方法,但没有实现getRecordReader,FileInputFormat还提供了一些protected的方法,供子类覆写。 
比如isSplitable(FileSystemfs, Path filename),它检查是否可以将一个文件分块。 
有些压缩文件和其他的文件需要将一个文件视为原子记录,那么可以覆写,返回false 

使用了FileInputFormat之后,需要关注的就是自定义RecordReader: 

Java代码  

1. public interface RecordReader{  

2.   boolean next(K key, V value) throws IOException;  

3.   

4.   K createKey();  

5.   V createValue();  

6.   long getPos() throws IOException;  

7.   void close() throws IOException;  

8.   float getProgress() throws IOException;  

9. }  


Hadoop
有一些实现好的RecordReader,比如LineRecordReader 
它在TextInputFormat被使用,KeyValueLineRecordReaderKeyValueTextInputFormat被使用。 

3.3.2
输出格式。 
InputFormat对应,输出有OutputFormat类,输出没有splits,每个reducer写入自己的文件。 
Hadoop
提供了一些预定义的输出格式实现,可以通过JobConfsetOutputFormat来指定。 
TextOutputFormat
将每个记录写成一行,keyvalue\t分割,可以在mapred.textoutputformat.separator中指定分隔符。 
SequenceFileOutputFormat
key/value写入hadoopsequence文件格式。和 
SequenceFileInputFormat
对应。 

NullOutputFormat
不输出。