Hadoop 提供一个可靠的共享存储和分析系统:其中 HDFS(Hadoop Distributed FileSystem)实现存储,MapReduce 实现分析处理。
MapReduce是一种线性可伸缩的编程模型。编写两个函数:map 函数和 reduce 函数——每个函数定义一个键/值对集合的映射。函数不需要关注数据集及其所用集群的大小。如果输入的数据量是原来的两倍,那么运行的时间也需要两倍;但是如果集群是原来的两倍,运行时间和原来一样。(SQL 查询一般不具备该特性)
MapReduce 的高级查询语言:Pig,Hive
MapReduce 会尽量在计算节点上存储数据,以实现数据的本地快速访问。数据本地化特性是它的核心特征。由于网络带宽是数据中心环境最珍贵的资源(数据量大的时候,可能很多计算节点会由于网络带宽的瓶颈而空闲下来等待数据),复制数据很容易耗尽网络带宽,MapReduce 通过显式网络拓扑结构尽力保留网络带宽,不降低 MapReduce 计算密集型的数据分析能力。
MapReduce 采用无共享框架,实现失败检测,从而使各个任务之间彼此独立。检测到失败的 map 或 reduce 任务,然后重新执行这些任务。
map 函数的输出经由 MapReduce 框架处理后,最后被发送到 reduce 函数。
Hadoop 将 MapReduce 的输入数据划分成等长的小数据块,成为输入分片(input split),简称分片。
Hadoop 在存储有输入数据(HDFS 中的数据)的节点上运行 map 任务,可以获得最佳性能,即数据本地化优化。
通常,一个合理的分片大小趋向于 HDFS 的一个块的大小(因为它是确保可以存储在单个节点上的最大输入块的大小),默认是 64 MB。如果分片跨越两个数据块,那么对于任何一个 HDFS 节点,基本上都不可能同时存储这两个数据块,因此分片中的部分数据需要通过网络传输到 map 任务节点,显然效率更低。
map 任务将其输出写入本地硬盘,而非 HDFS。(map 的输出是中间结果,该结果由 reduce 任务处理后才产生最终输出结果)
单个 reduce 任务的输入通常来自于所有 mapper 的输出。reduce 的输出通常存储在 HDFS 中以实现可靠存储。
Terms:
MapReduce: 分布式数据处理模型和执行环境,运行于大型商用机集群
HDFS:分布式文件系统,运行于大型商用机集群
Pig:一种数据流语言和运行环境,用以检索非常大的数据集。Pig运行在 MapReduce 和 HDFS 的集群上。
Hive:一个分布式、按列存储的数据仓库。Hive 管理 HDFS 中存储的数据,并提供基于 SQL 的查询语言用以查询数据。
HBase:一个分布式、按列存储数据库。HBase 使用 HDFS 作为底层存储,同时支持 MapReduce 的批量式计算和点查询(随机读取)。
Common:一组分布式文件系统和通用I/O的组件与接口
Avro:一种支持高效、跨语言的 RPC及永久存储数据的序列化系统
ZooKeeper:一个分布式、可用性高的协调服务。ZooKeeper 提供分布式锁之类的基本服务用于构建分布式应用
Sqoop:在数据库和 HDFS 之间高效传输数据的工具。
示例:通过一个 map 函数、一个 reduce 函数、一些用来运行作业的代码,这三部分来实现。(示例版本较旧,使用老的API)
示例1-1:查找最高气温的 Mapper
import java.io.IOEception;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reporter;
public class MaxTemperatureMapper extend MapReduceBase
implements Mapper<LongWritavle,Text,Text,IntWritable>{ #Mapper 接口指定 map 函数的输入键(长整数偏移量)、输入值(一行文本)、输出键(年份)、输出值(气温,整数)的类型
private static final int MISSING=9999;
#OutputCollector 实例用于输出内容的写入
public void map(LongWritable key,Text value, OutputCollector<Text,IntWritable> output,Reporter reporter)
throws IOException{
String line=value.toString();
String year=line.sybstring(15,19);
int airTemperature;
if(line.charAt(87)=='+'){// parseInt doesn't like leading plus signs
airTemperature = Integer.parseInt(line.substring(88,92));
} else{
airTemperature = Integer.parseInt(line.substring(87,92));
}
String quality=line.substring(92,93);
if(airTemperature != MISSING && quality.matches("[01459]")){
output.collect(new Text(year), new IntWritable(airTemperature));
}
}
}
示例1-2:查找最高气温的 Reducer
import java.io.IOEception;
import java.util.Iterator;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
public class MaxTemperatureReducer extends MapReduceBase
implements Reducer<Text,IntWritable,Text,IntWritable>{ #reduce 函数的输入类型必须与 map 函数的输出类型相匹配
public void reduce(Text key, Iterator<IntWritable> values,
OutputCollector<Text,IntWritable> output,Repoter repoter)
throws IOException{
int maxValue = Integer.MIN_VALUE;
while (values.hasNext()){
maxValue = Math.max(maxValue, values.next(0.get()); #循环比较当前气温与已看到的最高气温,从而得到最高气温
}
output.collect(key, new IntWritable(maxValue));
}
}
示例1-3:在气象数据集中找出最高气温(负责运行 MapReduce 作业)
import java.io.IOEception;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOnputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf; #Jobconf 对象指定了作业执行规范,控制整个作业的运行
public class MaxTemperature {
public static void main(String[] args) throws IOException{
if (args.length !=2){
System.err.printIn("Usage: MaxTemperature <input path> <output path>");
System.exit(-1):
}
JobConf conf = new JobConf(MaxTemperature.class);
conf.setJobName("Max temperature");
FileInputFormat.addInputPath(conf, new Path(atgs[0])); #定义输入数据的路径
FileOnputFormat.setOnputPath(conf, new Path(atgs[1])); #指定输出路径(reduce 函数输出文件的写入目录),在运行任务前该目录不应该存在,否则报错
conf.setMapperClass(MaxTemperatureMapper.class); #指定 map 类型
conf.setReducerClass(MaxTemperatureReducer.class); #指定 reduce 类型
conf.setOutputkeyClass(Text.class); #控制 map 和 reduce 函数的数据类型
conf.setOutputValueClass(IntWritable.class); #控制 map 和 reduce 函数的数据类型
JobClient.runJob(conf); #提交作业并等待完成,将进展情况写到控制台
}
}
使用 hadoop 命令运行作业:
% export HADOOP_CLASSPATH=build/classes #定义一个 HADOOP——CLASSPATH 环境变量用于添加应用程序类的路径
% hadoop MaxTemperature input/ncdc/sample.txt output
Note:命令的运行需要在代码所在文件夹下进行。