Hadoop学习笔记

hadoop

以下文章都是关于hadoop学习的笔记,不太成体系,知识慢慢积累吧

  • namenode相当于拿着账本的记账员,记录来的货物在哪个仓库里,当然仓库没有备份

  • secondnamenode: 比如此处是个中转站,货物需要再次移动,需要再次记录, secondnamenode相当于记账员的助理,来协助namenode来记录数据

  • datanode:相当于码头上的一块块的分区的货物

    hadoop文件合并Hadoop学习笔记_第1张图片

文件合并:
	将edits文件和fsimage文件通过http get方式获取到。
		复制出来后,就不能用旧的edits文件了,namenode中创建edits.new文件,合并过程中客户端发送的数据信息记录在edits.new文件中。
		seconednode中将就的edits文件和 get方式获取的文件合并后,生成并返回带有扩展名 fsimage.ckpt文件。 再次传到namenode, 并重命名为	fsimage文件。 最后将edits.new文件改为edits文件。
		client新传递的文件将记录在新的edits文件中。
		当edits文件大小达到64M,或者时间到了1小时,将在执行一次。

MapReduce是一个分布式计算框架(编程模型),设计了基于HDFS的Mapreduce分布式计算框架

  • MR框架对于程序员最大的意义在于,不需要掌握分布式计算编程,不需要考虑分布式编程里可能存在的种种难题,比如任务调度和分配,文件逻辑切块
  • MR由两个阶段组成,Mapper和Reduce,用户只需要实现map()和reduce()两个函数,即可实现分布式计算,非常简单,这两个函数的形参是key和value对,表示函数的输入信息

MapReduce框架的节点组成结构

JobTracker / ResourceManage工作职能
  • 知道管理哪些机器,及管理哪些NodeManager
  • 要有检测机制,能够检测到NodeManager的状态变换,通过RPC心跳来实现
  • 任务的分配和调度,ResourceManager能够做到细粒度的任务分配,比如一个任务需要占用多大的内存,需要多少计算资源。
  • 注: ResourceManager是hadoop2.0版本之后引入yarn,由yarn来管理hadoop之后,jobtracker就被替换成了ResourceManager
TaskTracker / NodeManager工作职能
  • 能够收到ResourceManager发过来的任务,并进行任务的处理。这里处理任务指的是Map任务Reduce任务。(真正干活的)
Map、Reduce的执行步骤 Hadoop学习笔记_第2张图片
  • map任务处理
    读取输入文件内容,解析成key、value对。对输入文件的每一行,解析成key,value对。每一个键值对调用一次map函数。
    写自己的逻辑,对输入的key,value处理,转换成新的key,value输出
    对输出的key、value进行分区。
    对相同分区的数据,按照key进行排序(默认按照字典顺序进行排序)、分组。相同key的value放到一个集合中。
    (可选)分组后的数据进行归约
    key一般是LongWritable类型的偏移量
  • Reduce任务处理
    对多个map任务的输出,按照不同的分区,通过网络copy到不同的reduce节点。这个过程并不是map将数据发给reduce的,而是reduce主动去获取的。
    对多个map任务的输出进行合并、排序。写reduce函数自己的逻辑。对输入的key、value处理,转换成新的key,value输出。
    把reduce的输出保存到文件中(HDFS)

MapReduce实现单词数量统计(WordCount)

  • 案例分析
  • 导入相关jar包
    Hadoop学习笔记_第3张图片
  • WordCountMapper.java
 package com.xiaoxu.hadoop;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

/**
 * MapReduce实现单词数量统计
 * Mapper 每行内容调用一次map()方法
 *
 *   mapper.txt
 *      hello tom
 *      hello rose
 *      hello joy
 *      hello jerry
 *      map到reducer会经过洗牌,将map输出的key相同的值,合并value,组成list传到reduces
 */
public class WordCountMapper extends Mapper {

    /**
     *
     * @param key 输入的key 及偏移量
     * @param value
     * @param context 向外输出的对象,输出的key和value的类型
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //获取读取到的文本内容
        String line = value.toString();
        System.out.println("map读到line: key:"+ key.get() +"  内容:  "+  line);
        //使用空格进行拆分
        String [] words = line.split(" ");
        //
        for (String word: words) {
            System.out.print("输出: "+word+ "1, ");
            context.write(new Text(word),new LongWritable(1));
        }
    }
}

  • WordCountReducer.java
 package com.xiaoxu.hadoop;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;
import java.util.Iterator;

/**
 * Text:输入的key的类型
 * LongWritable: 输入value集合中元素的类型
 * Text: 输出key的类型
 * LongWritable: 输出value的类型
 */
public class WordCountReducer extends Reducer{

    /**
     * @param key 输入的key hello
     * @param values  1,1,2,3
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException {

        Iterator iterator =  values.iterator();
        long count = 0;
        while (iterator.hasNext()){
            long val = iterator.next().get();
            System.out.println("reduce_in: "+ key.toString() +  " values: "+ val);
            count += val;
        }
        context.write(key,new LongWritable(count));
        System.out.println("reduce_out  key: "+key.toString() +" value:  "+ count);
    }



}

  • WordCountDriver.java 启动类
 package com.xiaoxu.hadoop;

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.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WordCountDriver {

    public static void main(String[] args) throws Exception{

        Configuration configuration = new Configuration();
        //创建job对象,指定job名称,config对象
        Job job = Job.getInstance(configuration,"wordCountJob");
        //指定job的执行的类
        job.setJarByClass(WordCountDriver.class);
        //指定mapper类
        job.setMapperClass(WordCountMapper.class);
        //指定reduce类
        job.setReducerClass(WordCountReducer.class);
        //指定mapper,和reduce的输出类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(LongWritable.class);
        //指定reduce 的输出类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);
        //指定任务操作的资源的位置 到目录一级
        FileInputFormat.setInputPaths(job,new Path("hdfs://127.0.0.1:9000/user/hadoop/word/mapperreduce.txt"));
        //指定任务结束后,生成的结果文件位置 hdfs上
        FileOutputFormat.setOutputPath(job,new Path("hdfs://127.0.0.1:9000/user/hadoop/word/result"));
        //执行任务
        job.waitForCompletion(true);
    }

}

  • 测试文件
    Hadoop学习笔记_第4张图片
    控制台输出

Hadoop学习笔记_第5张图片
结果文件:

主要map到reducer经过shuffle洗牌,将同样的key,value洗到一起, value拼成valueslist。传到reducer.(可以设置reduces个数),若文件比较大,分为三块,则会有三个WordCountMapper来调用mapper方法。

Hadoop学习笔记_第6张图片

Hadoop学习笔记_第7张图片

  • 问题: mapper传递的参数拼成字符串转换为Text,后续害的split、类型转换、还需要记着原来的顺序,开发时比较麻烦,那么如何解决这个问题那?可以将传递参数封装到一个自定义类的对象中。

自定义类的对象

  • 写一个Bean实现Writable接口,实现其中的write和readFields方法,注意这两个方法中属性处理的顺序和类型。此后这个类的对象就可以用于MapperReducer了。
  • 案例 统计用户流量使用信息 日志文件 flow.txt 格式: 15001224521 bj lxx 1024
  • 省略的部分属性的是set get 方法 Hadoop学习笔记_第8张图片
  • FlowMapper.java
    Hadoop学习笔记_第9张图片
  • FlowReducer.java
    Hadoop学习笔记_第10张图片
  • 启动类 FlowDriver.java
    Hadoop学习笔记_第11张图片
  • 测试文件 flow.txt
    Hadoop学习笔记_第12张图片
  • result:
    运行结果,这个结果放到hdfs上了,在FlowDriver中指定了位置
    Hadoop学习笔记_第13张图片
    Hadoop学习笔记_第14张图片

分区原理

分区操作是shuffle操作中的一个重要过程,作用就是讲map的结果按照规则分发到不同reduce中进行处理,从而安装分区到多个输出结果。
Partitioner是partitioner的基类,如果需要定制partitioner也需要继承该类。
HashPartitioner是mapreduce的默认partitioner。计算方法是
reducer = (key.hashcode() & Integer.MAXVALUE)%numReduceTasks;
注: 默认情况下,reduceTask数量为 1
很多时候MR自带的分区规则并不能满足我们需求,为了实现特定的效果,可以需要自己来定义分区规则。
比如这里要按 不同的地区 来统计流量信息,北京,上海,深圳 分别统计,并生成三个结果文件。默认的不满足

案例: 改写流量统计案例,根据不同地区分区存放数据

  • 开发Partitioner代码,写一个类继承Partitioner类,在其中描述分区规则:
  • 在任务调度代码中,增加Partitioner配置 设置Partitioner类
  • job.setPartitionerClass(FlowPartitioner.class)
  • 指定Reducer的数量
  • job.setNumReducerTask(4)
  • Partitioner将会将数据发往不同reducer,这就要求reducer的数量应该大于等于Partitioner的数量,如果少于则在执行的过程中会报错。
  • 增加 FlowPatitioner.java
    Hadoop学习笔记_第15张图片
    Hadoop学习笔记_第16张图片
    运行后,查看到result2下有多个结果文件,下载后打开,验证是按地区统计的流量信息。
    Hadoop学习笔记_第17张图片

排序 Sort

  • 默认的字典排序可能不满足业务规则。
  • Map执行过后,在数据进入reduce操作之前,数据默认将会按照 map输出key的字典排序传入到reduce。利用这个特性可以实现大数据场景下排序的需求。

案例:计算每月收益(收入-成本),并按照收益进行排序(文件profit.txt), 按每个人的总收益从高到低排序。

月份 姓名 收入 成本
1 ls 3000 200
1 zs 3000 210
2 ww 3000 220
1 wy 2500 230
4 gd 2000 100
1 gh 1000 50
2 gh 1000 50
3 gh 2500 50
  • 此案例需要两个MR来操作,合并数据(combiner),进行排序, 排序MR
  1. 创建Bean对象实现WritableComparable接口实现其中的wirte readFields compartTo 方法。
  2. 在Map操作时,将Bean对象作为key输出,从而在Reduce接收到数据时已经经过了排序。context.write(bean,NullWritable.get())
  3. 而Reduce操作时,只需要原样输出数据即可。

Hadoop学习笔记_第18张图片
Hadoop学习笔记_第19张图片
Hadoop学习笔记_第20张图片
Hadoop学习笔记_第21张图片

  • 结果: 打印的是toString()方法的结果 3
    Hadoop学习笔记_第22张图片
  • 注意:这里进行了两次的mapper – reduce方法,才实现了此功能,以上代码是直接对第一次map reduce后执行的结果文件 进行二次处理的。
    原始文件: 1
    Hadoop学习笔记_第23张图片
    第一次执行后: 2
    Hadoop学习笔记_第24张图片
    这三个截图文件只参考数据格式就行了。

Combiner – 合并

  • 每一个MapperTask可能会产生大量的输出,combiner的作用就是在MapperTask端对输出先做一次合并,以减少输出到reducerTask的数据量。
  • combiner是实现在Mapper端进行key的归并,combiner具有类似本地的reduce功能。
  • 如果不用combiner,那么所有的结果都是reduce完成,效率会相对低下,使用combiner,先完成在Mapper的本地聚合,从而提升速度。
    Hadoop学习笔记_第25张图片
    Hadoop学习笔记_第26张图片
    Hadoop学习笔记_第27张图片
    其余的和第一个统计单词个数的案例一致。
    测试文件:
    Hadoop学习笔记_第28张图片
    结果:
    Hadoop学习笔记_第29张图片
1. Mapper
每一个 MapperTask有一个环形内存缓冲区,用于存储map任务的输出。默认大小是100M,(io.sort.mb属性指定),一旦达到阈值0.8,(io.sort.spill.percent),一个后台线程把内容写到(spill)磁盘的指定目录,(mapred.local.dir)下的新建的一个溢出文件。
写磁盘前,要partition (分区),sort(排序),Combiner(合并).如果有后续的数据,将会继续写入环形缓冲区,最终写入下一个溢出文件中。
等最后记录写完,合并全部溢出写文件为一个分区且排序的文件。
如果最终合并时,被合并的文件大于等于3个,则合并完再执行一次Combiner,否则不会。
2 Reducer
Reducer通过Http方式得到输出文件的分区
NodeManager为分区文件运行Reduce任务。复制阶段把	Map输出复制到Reducer的内存或磁盘上,一旦MAP
任务完成,Reducer就开始复制输出。
3. Mapper数量
Mapper数量在默认情况下不可直接控制干预,Mapper数量由输入的大小和个数决定。
在默认情况下,最终Input占据多少个block,就应该启动多少个Mapper
可以通过配置mapred.min.split.size来控制split的size的最小值。

执行流程:

  • 从切块中,取文件,一个文件对应一个mapTask,读到之后,往圆形缓冲区里溢出内容,buffer in memory
  • 写的过程中会产生多个溢出文件,(map中向缓冲区写,默认100M,边写边读,当缓冲去的文件大于容量的0.8之后,会生成溢出文件,一个map可能会生成多个溢出文件。)
  • 溢出文件如果超过了三个,本身还要进行combiner合并,多个map生成的文件进行merge进行梳理。
  • reduce http方式获取分区数据。(通常reduce机器和map不在一个机器上)
    Hadoop学习笔记_第30张图片
    Hadoop学习笔记_第31张图片
    生成溢出文件。
    Hadoop学习笔记_第32张图片

你可能感兴趣的:(大数据)