一、MapReduce概述

MapReduce 是 Hadoop 的核心组成, 是专用于进行数据计算的,是一种分布式计算模型。由Google提出,主要用于搜索领域,解决海量数据的计算问题.

MapReduce由两个阶段组成:Map和Reduce,用户仅仅须要实现map()和reduce()两个函数。就可以实现分布式计算。非常easy。这两个函数的形參是key、value对,表示函数的输入输出信息。

map、reduce键值对格式

二、MapReduce体系结构及工作流程

1、JobTracker

负责接收用户提交的作业。负责启动、跟踪任务运行。

JobSubmissionProtocol是JobClient与JobTracker通信的接口。

InterTrackerProtocol是TaskTracker与JobTracker通信的接口。

2、TaskTracker

负责运行任务。

3、JobClient

是用户作业与JobTracker交互的主要接口。

负责提交作业的,负责启动、跟踪任务运行、訪问任务状态和日志等。

4、工作流程图

运行步骤:

1.map任务处理

1.1读取输入文件内容,解析成key、value对。对输入文件的每一行。解析成key、value对。每一个键值对调用一次map函数。

1.2 写自己的逻辑,对输入的key、value处理,转换成新的key、value输出。

1.3 对输出的key、value进行分区。

1.4 对不同分区的数据,依照key进行排序、分组。同样key的value放到一个集合中。

1.5 (可选)分组后的数据进行归约。

2.reduce任务处理

2.1 对多个map任务的输出,依照不同的分区,通过网络copy到不同的reduce节点。

2.2 对多个map任务的输出进行合并、排序。

写reduce函数自己的逻辑,对输入的key、value处理,转换成新的key、value输出。

2.3 把reduce的输出保存到文件里。

三、统计单词源代码及凝视

package mapreduce;

import java.io.IOException;
import java.net.URI; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class WordCountApp { static final String INPUT_PATH = "hdfs://liguodong:9000/hello"; static final String OUT_PATH = "hdfs://liguodong:9000/out"; public static void main(String[] args) throws Exception {
Configuration conf = new Configuration(); final FileSystem fileSystem = FileSystem.get(new URI(INPUT_PATH),conf);
if(fileSystem.exists(new Path(OUT_PATH)))
{
fileSystem.delete(new Path(OUT_PATH),true);
} final Job job = new Job(conf, WordCountApp.class.getSimpleName());
//1.1 输入文件夹在哪里
FileInputFormat.setInputPaths(job, INPUT_PATH); //指定对输入数据进行格式化处理的类
//job.setInputFormatClass(TextInputFormat.class); //1.2 指定自己定义的Mapper类
job.setMapperClass(MyMapper.class);
//指定map的输出的<k,v>类型,假设<k3,v3>的类型与<k2,v2>的类型一致,那么能够省略。
//job.setMapOutputKeyClass(Text.class);
//job.setMapOutputValueClass(LongWritable.class); //1.3分区
//job.setPartitionerClass(HashPartitioner.class);
//job.setNumReduceTasks(1); //1.4 TODO 排序、分组
//1.5 TODO (可选)归约 //2.2 指定自己定义的Reducer类
job.setReducerClass(MyReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class); //2.3 指定输出的路径
FileOutputFormat.setOutputPath(job, new Path(OUT_PATH));
//指定输出的格式化类
//job.setOutputFormatClass(TextOutputFormat.class); //把作业交给JobTracker运行
job.waitForCompletion(true);
}
/**
* KEYIN 即K1 表示每一行的起始位置(偏移量offset)
* VALUEIN 即v1 表示每一行的文本内容
* KEYOUT 即k2 表示每一行中的每一个单词
* VALUEOUT 即v2 表示每一行中的每一个单词的出现次数,固定值1
* @author liguodong
* Java Hadoop
* Long LongWritable
* String Text
*/
static class MyMapper extends Mapper<LongWritable,Text,Text,LongWritable>{
@Override
protected void map(LongWritable key, Text value,
Mapper<LongWritable, Text, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
final String[] splited = value.toString().split(" ");
for (String word : splited) {
context.write(new Text(word), new LongWritable(1));
}
}
}
/**
* KEYIN 即k2 表示每一行中的每一个单词
* VALUEIN 即v2 表示每一行中每一个单词出现次数,固定值1
* KEYOUT 即k3 表示整个文件里的不同单词
* VALUEOUT 即v3 表示整个文件里的不同单词的出现总次数
* @author liguodong
*/ static class MyReducer extends Reducer<Text, LongWritable,Text, LongWritable>{
@Override
protected void reduce(Text k2, Iterable<LongWritable> v2s,
Reducer<Text, LongWritable, Text, LongWritable>.Context context)
throws IOException, InterruptedException {
long sum = 0L;
for (LongWritable v2 : v2s) {
sum += v2.get();
}
context.write(k2, new LongWritable(sum));
}
}
}

四、最小的MapReduce驱动

Configuration configuration = new Configuration();
Job job = new Job(configuration, "HelloWorld"); job.setInputFormat(TextInputFormat.class);
job.setMapperClass(IdentityMapper.class);
job.setMapOutputKeyClass(LongWritable.class);
job.setMapOutputValueClass(Text.class); job.setPartitionerClass(HashPartitioner.class);
job.setNumReduceTasks(1); job.setReducerClass(IdentityReducer.class);
job.setOutputKeyClass(LongWritable.class);
job.setOutputValueClass(Text.class);
job.setOutputFormat(TextOutputFormat.class); job.waitForCompletion(true);

五、MapReduce驱动默认的设置

六、Hadoop序列化与基本类型

1、序列化概念

序列化(Serialization)是指把结构化对象转化为字节流。

反序列化(Deserialization)是序列化的逆过程。即把字节流转回结构化对象。

Java序列化(java.io.Serializable)

2、Hadoop序列化格式特点

紧凑:高效使用存储空间。

高速:读写数据的额外开销小

可扩展:可透明地读取老格式的数据

互操作:支持多语言的交互

Hadoop的序列化格式:Writable

3、Hadoop序列化的作用

序列化在分布式环境的两大作用:进程间通信。永久存储。

Hadoop节点间通信。

4、基本数据类型

Hadoop的数据类型要求必须实现Writable接口。

Writable接口是依据 DataInput 和 DataOutput 实现的简单、有效的序列化对象.

MR的随意Key和Value必须实现Writable接口。

public interface Writable {
/**
* Serialize the fields of this object to <code>out</code>.
*
* @param out <code>DataOuput</code> to serialize this object into.
* @throws IOException
*/
void write(DataOutput out) throws IOException; /**
* Deserialize the fields of this object from <code>in</code>.
*
* <p>For efficiency, implementations should attempt to re-use storage in the
* existing object where possible.</p>
*
* @param in <code>DataInput</code> to deseriablize this object from.
* @throws IOException
*/
void readFields(DataInput in) throws IOException;
}

MR的随意key必须实现WritableComparable接口。

/**
* A {@link Writable} which is also {@link Comparable}.
*
* <p><code>WritableComparable</code>s can be compared to each other, typically
* via <code>Comparator</code>s. Any type which is to be used as a
* <code>key</code> in the Hadoop Map-Reduce framework should implement this
* interface.</p>
**/
public interface WritableComparable<T> extends Writable, Comparable<T> {
}

实现Writable接口?

write 是把每一个对象序列化到输出流

readFields 是把输入流字节反序列化

实现WritableComparable?

Java值对象的比較:一般须要重写toString(),hashCode(),equals()方法。


Hadoop与Java常见基本类型的对比

Long—LongWritable

Integer—IntWritable

Boolean—BooleanWritable

String—Text


Text一般觉得它等价于java.lang.String的Writable。

针对UTF-8序列。

例:

Text test = new Text(“test”);

IntWritable one = new IntWritable(1);

java类型怎样转化为hadoop基本类型?

调用hadoop类型的构造方法,或者调用set()方法。

new LongWritable(123L);

hadoop基本类型怎样转化为java类型?

对于Text,须要调用toString()方法,其它类型调用get()方法。

七、基于文件存储的数据结构

SequenceFile—无序存储

MapFile—会对key建立索引文件,value按key顺序存储

基于MapFile的结构有:

ArrayFile—与我们使用的数组一样,key值为序列化的数字

SetFile—仅仅有key,value为不可变的数据

BloomMapFile—在 MapFile 的基础上添加了一个/bloom 文件,包括的是二进制的过滤表。在每一次写操作完毕时,会更新这个过滤表。

八、自己定义数据类型源代码

package mapreduce;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.mapreduce.lib.partition.HashPartitioner; public class KpiApp {
static final String INPUT_PATH = "hdfs://liguodong:9000/wlan";
static final String OUT_PATH = "hdfs://liguodong:9000/out"; public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
final Job job = new Job(new Configuration(),KpiApp.class.getSimpleName()); //1.1 指定输入文件路径
FileInputFormat.setInputPaths(job, INPUT_PATH);
//指定那个类用来格式化输入文件
job.setInputFormatClass(TextInputFormat.class); //1.2 指定自己定义的Mapper类
job.setMapperClass(MyMapper.class);
//指定输出<k2,v2>的类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(KpiWritable.class); //1.3 指定分区类
job.setPartitionerClass(HashPartitioner.class);
job.setNumReduceTasks(1); //1.4 TODO 排序、分组
//1.5 TODO (可选)归约 //2.2 指定自己定义的Reducer类
job.setReducerClass(MyReducer.class);
//指定输入<k3,v3>的值
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(KpiWritable.class);
//2.3 指定输出到哪里
FileOutputFormat.setOutputPath(job, new Path(OUT_PATH));
//设定输出文件的格式化类
job.setOutputFormatClass(TextOutputFormat.class); //把任务及交给JobTrack运行
job.waitForCompletion(true); } static class MyMapper extends Mapper<LongWritable, Text, Text, KpiWritable>{ @Override
protected void map(LongWritable key, Text value,
Mapper<LongWritable, Text, Text, KpiWritable>.Context context)
throws IOException, InterruptedException {
final String[] splited = value.toString().split("\t");
final String msisdn = splited[1];
final Text k2 = new Text(msisdn);
final KpiWritable v2 = new KpiWritable(splited[6],splited[7],splited[8],splited[9]);
context.write(k2, v2); }
} static class MyReducer extends Reducer<Text, KpiWritable, Text, KpiWritable>{ /**
* @param k2 表示整个文件里不同的手机号码
* @param v2表示该手机号在不同一时候段的流量的集合
* @param
*/
@Override
protected void reduce(Text k2, Iterable<KpiWritable> v2s,
Reducer<Text, KpiWritable, Text, KpiWritable>.Context context)
throws IOException, InterruptedException {
long upPackNum = 0L;
long downPackNum = 0L;
long upPayLoad = 0L;
long downPayLoad = 0L;
for (KpiWritable kpiWritable : v2s) {
upPackNum += kpiWritable.upPackNum;
downPackNum += kpiWritable.downPackNum;
upPayLoad += kpiWritable.upPayLoad;
downPayLoad += kpiWritable.downPayLoad;
}
final KpiWritable v3 = new KpiWritable(upPackNum+"",downPackNum+"",upPayLoad+"",downPayLoad+"");
context.write(k2, v3);
} } } class KpiWritable implements Writable{ long upPackNum;
long downPackNum;
long upPayLoad;
long downPayLoad; public KpiWritable(){ }
public KpiWritable(String upPackNum, String downPackNum, String upPayLoad,
String downPayLoad) {
super();
this.upPackNum = Long.parseLong(upPackNum);
this.downPackNum = Long.parseLong(downPackNum);
this.upPayLoad = Long.parseLong(upPayLoad);
this.downPayLoad = Long.parseLong(downPayLoad);
} @Override
public void write(DataOutput out) throws IOException {
out.writeLong(upPackNum);
out.writeLong(downPackNum);
out.writeLong(upPayLoad);
out.writeLong(downPayLoad);
} @Override
public void readFields(DataInput in) throws IOException {
this.upPackNum = in.readLong();
this.downPackNum = in.readLong();
this.upPayLoad = in.readLong();
this.downPayLoad = in.readLong();
}
@Override
public String toString(){
return upPackNum+"\t"+downPackNum+"\t"+upPayLoad+"\t"+downPayLoad;
}
}

九、MapReduce输入输出处理类

1、输入处理类

FileInputFormat:

FileInputFormat是全部以文件作为数据源的InputFormat实现的基类,FileInputFormat保存作为job输入的全部文件。并实现了对输入文件计算splits的方法。至于获得记录的方法是有不同的子类——TextInputFormat进行实现的。

InputFormat 负责处理MR的输入部分.

有三个作用:

验证作业的输入是否规范.

把输入文件切分成InputSplit.

提供RecordReader 的实现类,把InputSplit读到Mapper中进行处理.

public abstract class InputFormat<K, V> {
/**
* Logically split the set of input files for the job.
*
* <p>Each {@link InputSplit} is then assigned to an individual {@link Mapper}
* for processing.</p>
*
* <p><i>Note</i>: The split is a <i>logical</i> split of the inputs and the
* input files are not physically split into chunks. For e.g. a split could
* be <i>&lt;input-file-path, start, offset&gt;</i> tuple. The InputFormat
* also creates the {@link RecordReader} to read the {@link InputSplit}.
*
* @param context job configuration.
* @return an array of {@link InputSplit}s for the job.
*/
public abstract
List<InputSplit> getSplits(JobContext context
) throws IOException, InterruptedException; /**
* Create a record reader for a given split. The framework will call
* {@link RecordReader#initialize(InputSplit, TaskAttemptContext)} before
* the split is used.
* @param split the split to be read
* @param context the information about the task
* @return a new record reader
* @throws IOException
* @throws InterruptedException
*/
public abstract
RecordReader<K,V> createRecordReader(InputSplit split,
TaskAttemptContext context
) throws IOException,
InterruptedException;
}

InputSplit

在运行mapreduce之前。原始数据被切割成若干split。每一个split作为一个map任务的输入,在map运行过程中split会被分解成一个个记录(key-value对),map会依次处理每一个记录。

FileInputFormat仅仅划分比HDFS block大的文件,所以FileInputFormat划分的结果是这个文件或者是这个文件里的一部分。

假设一个文件的大小比block小,将不会被划分,这也是Hadoop处理大文件的效率要比处理非常多小文件的效率高的原因。

当Hadoop处理非常多小文件(文件大小小于hdfs block大小)的时候。因为FileInputFormat不会对小文件进行划分,所以每一个小文件都会被当做一个split并分配一个map任务,导致效率底下。

比如:一个1G的文件,会被划分成16个64MB的split,并分配16个map任务处理。而10000个100kb的文件会被10000个map任务处理。

TextInputFormat

TextInputformat是默认的处理类,处理普通文本文件。

文件里每一行作为一个记录,他将每一行在文件里的起始偏移量作为key,每一行的内容作为value。

默认以\n或回车键作为一行记录。

TextInputFormat继承了FileInputFormat。

其它输入类

◆ CombineFileInputFormat

相对于大量的小文件来说,hadoop更合适处理少量的大文件。

CombineFileInputFormat能够缓解这个问题,它是针对小文件而设计的。

◆ KeyValueTextInputFormat

当输入数据的每一行是两列。并用tab分离的形式的时候,KeyValueTextInputformat处理这样的格式的文件非常适合。

◆ NLineInputformat

NLineInputformat能够控制在每一个split中数据的行数。

◆ SequenceFileInputformat

当输入文件格式是sequencefile的时候,要使用SequenceFileInputformat作为输入。

InputFormat类的层次结构

2、输出处理类

TextOutputformat

默认的输出格式,key和value中间值用tab隔开的。

SequenceFileOutputformat

将key和value以sequencefile格式输出。

SequenceFileAsBinaryOutputFormat

将key和value以原始二进制的格式输出。

MapFileOutputFormat

将key和value写入MapFile中。

因为MapFile中的key是有序的,所以写入的时候必须保证记录是按key值顺序写入的。

MultipleOutputFormat

默认情况下一个reducer会产生一个输出。可是有些时候我们想一个reducer产生多个输出,MultipleOutputFormat和MultipleOutputs能够实现这个功能。

OutputFormat类的层次结构

3、自己定义输入类格式

1)继承FileInputFormat基类。

2)重写里面的getSplits(JobContext context)方法。

3)重写createRecordReader(InputSplit split,TaskAttemptContext context)方法。

MapReduce学习笔记的更多相关文章

  1. Hadoop之MapReduce学习笔记(二)

    主要内容: mapreduce编程模型再解释: ob提交方式: windows->yarn windows->local : linux->local linux->yarn: ...

  2. Hadoop - MapReduce学习笔记(详细)

    第1章 MapReduce概述 定义:是一个分布式运算程序的编程框架 优缺点:易于编程.良好的扩展性.高容错性.适合PB级以上数据的离线处理 核心思想:MapReduce 编程模型只能包含一个Map ...

  3. MongoDB MapReduce学习笔记

    http://cnodejs.org/topic/51a8a9ed555d34c67831fb8b http://garyli.iteye.com/blog/2079158 MapReduce应该算是 ...

  4. mapreduce 学习笔记

    mapreduce基础概念 mapreduce是一个分布式计算框架(hadoop是mapreduce框架的一个免费开源java实现). mapreduce要点 主节点(master node)控制ma ...

  5. Hadoop之MapReduce学习笔记(一)

    主要内容:mapreduce整体工作机制介绍:wordcont的编写(map逻辑 和 reduce逻辑)与提交集群运行:调度平台yarn的快速理解以及yarn集群的安装与启动. 1.mapreduce ...

  6. Hadoop学习笔记—22.Hadoop2.x环境搭建与配置

    自从2015年花了2个多月时间把Hadoop1.x的学习教程学习了一遍,对Hadoop这个神奇的小象有了一个初步的了解,还对每次学习的内容进行了总结,也形成了我的一个博文系列<Hadoop学习笔 ...

  7. Hadoop学习笔记—18.Sqoop框架学习

    一.Sqoop基础:连接关系型数据库与Hadoop的桥梁 1.1 Sqoop的基本概念 Hadoop正成为企业用于大数据分析的最热门选择,但想将你的数据移植过去并不容易.Apache Sqoop正在加 ...

  8. MongoDB学习笔记~环境搭建

    回到目录 Redis学习笔记已经告一段落,Redis仓储也已经实现了,对于key/value结构的redis我更愿意使用它来实现数据集的缓存机制,而对于结构灵活,查询效率高的时候使用redis就有点不 ...

  9. spark学习笔记总结-spark入门资料精化

    Spark学习笔记 Spark简介 spark 可以很容易和yarn结合,直接调用HDFS.Hbase上面的数据,和hadoop结合.配置很容易. spark发展迅猛,框架比hadoop更加灵活实用. ...

随机推荐

  1. RadioButton单选按钮的使用

    namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { Initialize ...

  2. crontab中运行python程序出错,提示ImportError: No module named解决全过程

    将一个python脚本放入crontab执行时,提示如下错:ImportError: No module named hashlib但是在shell中直接执行时没有任何问题,google之后,得到线索 ...

  3. make: *** [sapi/cli/php] Error 1 解决办法

    make: *** [sapi/cli/php] Error 1 一:考虑过make clean,问题依然 二:(采取此方法后出现启动apache报错:/usr/local/apache2/modul ...

  4. js学习笔记25----Event对象

    Event : 事件对象,当一个事件发生的时候,和当前这个对象发生的这个事件有关的一些详细的信息都会被临时保存到一个指定的地方-event 对象,供我们在需要时调用. 事件对象必须在一个事件调用的函数 ...

  5. 使用JAVASCRIPT进行数据完整性验证

    页面输入完整性是编写BS经常遇到的问题,如果那里需要就到那里写,那可是要花不少的时候,并且造成不必要的浪费,下面是一个通过校验脚本,使用非常方便,通过传入FORM名就可以进行校验,通过在页面控件中增加 ...

  6. 《敏捷软件开发-原则、方法与实践》-Robert C. Martin读书笔记(转)

    Review of Agile Software Development: Principles, Patterns, and Practices 本书主要包含4部分内容,这些内容对于今天的软件工程师 ...

  7. sudo执行脚本找不到环境变量解决方法

    问题: 当普通用户下,设置并export一个变量,然后利用sudo执行echo命令,能得到变量的值,但是如果把echo命令写入脚本, 然后再sudo执行脚本,就找不到变量,未能获取到值. 原因 sud ...

  8. ThinkPHP项目笔记之登录,注册,安全退出篇

    1.先说注册 a.准备好注册页面,register.html,当然一般有,姓名,邮箱,地址等常用的. b."不要相信用户提交的一切数据",安全,安全是第一位的.所以要做判断,客户端 ...

  9. 获取UIWebView的内容高度

    本文转载至 http://i.cnblogs.com/EditPosts.aspx?opt=1   #pragma mark - UIWebview delegete - (void)webViewD ...

  10. zoj3696(泊松分布)

    p(k)=(y^k) / (k!) * e^(-y) 其中的y就是平均值 k就是我们要求的大小. Alien's Organ Time Limit: 2 Seconds      Memory Lim ...