MapReduce编程【WordCount】

课程回顾:
    wordcount程序:
    单机版:
        统计的6个文件
        定义一个方法------读取每一个小文件进行统计
        这个方法调用了6次
        定义了一个最终统计的方法
        这个方法调用了1次
        最大值   最小值   平均值
        map-----分
        reduce----汇总
    mapreduce的编程:
        mapper<输入的key---默认是每一行的偏移量,输入的value---每一行的内容,
        输出的key,输出的value>{
            map(偏移量,一行内容,context){
            }
        }
            
        reducer<输入key,输入value,输出key,输出value>{
            reduce(一组的中的相同的key,一组中的所有value的迭代器,上下文对象){
            
            }
        }
        driver-----main
    mapreduce的运行模式:
        1.打jar包的方式运行
        生产用的    缺点:不便于本地调试
        2.本地模式
        
        本地的代码调试
        
            1.路径  全路径
            2.权限
                System.setProperty("HADOOP_USER_NAME", "hadoop");
        3.本地模式运行的时候提交到yarn集群上
            不用    配置太麻烦---环境   修改源代码
mapreduce----分布式的并行计算
maptask的并行度:map阶段的分布式运行
    整个map阶段的任务被分成了几个小任务运行  每一个小任务就称为一个maptask任务
    maptask的概念:运行mapper的任务
    并行度:同时运行的任务数量
    同时可以运行的maptask的个数
    wordcount-----只运行一个maptask  运行多个maptask
    注意:一个maptask任务只能运行在一个节点上,一个节点上可以运行多个maptask任务的
    整个任务被分成几个maptask任务,取决于  节点    数据量
    一个maptask--------计算的任务量
    4个文件进行数据统计
        大小1.42kb
        3个从节点  datanode   nodemanager
        maptask任务数量---3?  4?   ----mapper---java---jps
        运行wordcount的时候  发现  map阶段执行的时候 4个yarnchild
            yarnchild-----maptask任务----mapper类
            1个yarnchild-----1个maptask任务
            maptask任务和数据有关的  和数据的文件个数有关?
            4份数据:1T   ---   假设和数据的文件个数有关----4maptask----1T
            节点:10个   最多运行在4个节点上   剩下的6个节点  没有事情做--负载不均衡
                1)负载不均衡
                2)数据存储的  分块存储   1t---8192块   分散存储在10个节点上
                每一个maptask要访问8192个数据块    跨节点访问数据    
            maptask任务和文件的个数无关
            maptask任务应该数据量有关
            一个maptask任务对应多少数据量?
                wc---1个文件    500M 
                    实际存储:
                    blk01:0-127M
                    blk02:128-255M
                    blk03:256-383M
                    blk04:384-500M
                对应100M数据量---一个maptask任务  处理100M的数据
                    maptask01:0-99M    blk01
                    maptask02:100-199M  blk01  blk02
                    maptask03:200-299M  blk02  blk03
                    .......
                    有极大的可能跨数据块  跨节点访问数据  性能低  不合理的
                对应的数据量200M---一个maptask任务  处理200M数据量
                    maptask01:0-199M     blk01  blk02
                    ....
                    有极大的可能跨数据块  跨节点访问数据    不合理
                分析  maptask对应的数据量128M的时候最合理的
                    maptask01:blk01
                    maptask02:blk02
                    maptask03:blk03
                    maptask04:blk04
                底层实现是否是这样的?
                    事实上一个maptask任务对应的数据量   一个切片大小---split
                    切片:逻辑上的概念(逻辑切块) 逻辑切片    仅仅是一个偏移量的划分
                    一个maptask任务对应数据量就是一个逻辑切片
                    一个逻辑切片假设100M    意思就是这个逻辑切片对应的数据0-99M数据  不会真正的切分数据
                    理论上切片的大小就是数据块的大小
                    文件输入类:
                        FileInputFormat
                            getSplits()
                            
                            
                              //获取最终的切片大小
                              //128M      1      long_max
                              protected long computeSplitSize(long blockSize, long minSize,
                                                              long maxSize) {
                                //Math.min(maxSize====long_max, blockSize==128*1024*1024M)    ====blocksize
                                  //Math.max(minSize===1,blocksize===128M)   ========   blocksize
                                return Math.max(minSize, Math.min(maxSize, blockSize));
                              }
                              }
                        修改切片大小:
                            小于128M-----修改max
                            大于128M-----修改min
                            
                            修改的方式:
                            1.修改配置文件  mapred-site.xml  
                            增加以下的两项配置
                            max---
                              mapreduce.input.fileinputformat.split.maxsize
                              min----
                              mapreduce.input.fileinputformat.split.minsize
                              一般不使用
                            2.代码中修改
                            FileInputFormat.setMaxInputSplitSize(job, 1*1024);
    总结:maptask的并行度  和切片大小有关
        一个切片----1个maptask任务
        默认的时候1个切片的大小就是一个数据块的大小
        block   split的区别?
            block:hdfs的数据存储的切分单元  默认128M   物理切分
            split:mapreduce中maptask任务的时候  进行数据划分的单元  逻辑上的概念  没有真正的切分数据
            理论上默认的split的大小===blocksize
            当前的文件  不够128M   则单独成一个逻辑切片
            最后一个切片   最大大小  blockSize/splitSize*1.1  128M*1.1=140.8M
            
        maptask任务在哪一个节点执行---yarn资源分配决定的    
        maptask任务  执行MyMapper的类的代码        

自定义的类---Writable接口
    实现了自己的一套  序列化反序列化的规则  数值   Writable
    hadoop内置的序列化 反序列化的类型:
        intwritable
        longwritable
        text
        nullwritable
        bytewritable
        doublewritable
        floatwritable
        booleanwritable
    用户自定义的类型  作为mapreduce的传输的类怎么办?
        具备序列化 和  反序列的能力    实现Writable接口
        案例:
            1、 统计每一个用户(手机号)所耗费的总上行流量、总下行流量,总流量
                关键字:每一----分组
                分组:手机号
                map端:
                    取到每一行的内容  拆分每一个字段  发送
                    key:手机号
                    value:上行流量+下行流量
                        两种发送方式:
                            1)拼接字符串---练习
                            2)自定义一个类  FlowBean
                reduce端:
                    循环遍历values  求和
                mapreduce程序中  输出的key  --  value的分隔
                实现Writable 接口  
                    重写:write()  readFields方法

mapreduce的排序:shuffle(快速排序    归并排序)
    mapreduce:中会默认按照map输出的key进行排序  默认升序
        如果map输出key是数值类型    按照大小排序的  升序排序的
        如果map输出的key是字符串类型的  按照顺序进行排序的   升序排序的
    需求:
        对wordcount的结果进行排序  按照单词出现的次数进行排序
        shuffle过程中  可以进行排序的   利用这个排序为我们排序
            hadoop    256
            hadoophello    4
            hello    164
            hive    176
            lily    84
            spark    168
            word    84
            ww    84
        想要利用shuffle排序   将需要排序的字段  放在map的输出的key上   输出结果还想要  单词--次数
            reduce端输出的时候  将map的结果进行一个反转
        map端:
            获取一行数据   切分
            key:次数
            value:单词
        reduce端:
            将map输过来的数据   进行对调之后  输出就可以
        降序排序:
    原始类型:
        hadoop中默认实现的类型  8个  都实现WritableComparable  具备比较能力 也具备序列化的能力
        intwritable----
            public class IntWritable implements WritableComparable<IntWritable>
                public interface WritableComparable<T> extends Writable, Comparable<T> {
}
        
    需要排序的  类型是自定义的类型   flowbean
        自定义的类型  具备以下两点:
            1)序列化和反序列功能  ---Writable(write  readFields)
            2)具备比较的能力   ---  comparable(comparaTo)
        自定义的类型必须同时实现两个接口
            实现以下的接口:
            WritableComparable-----(write  readFields  comparaTo)
        需求:    
            
            2.得出上题结果的基础之上再加一个需求:将统计结果按照总流量倒序排序  再按手机号排序
            借助shuffle排序---map的key  将排序字段放在map的key中
            排序字段:总流量  手机号
            Text---字典顺序排序的时候
            1000000    13552785495
            80    13552785494
                map端:    
                    key:总流量+手机号  --Text
                    value:剩下的字段
                reduce:
                    key:手机号---text
                    value:上+下+总---text
                                -----练习
                缺点:1)排序的性能  Text--String---字典顺序   一个字符一个字符比较的
                    流量---数字
                    2)结果有问题的
            自定义的类实现:FlowBean---手机号   上行流量  下行流量   总流量
                map:
                    key:fb
                    value:nullwritable
                reduce:
                    输出
            对于自定义类的排序要想借助shuffle过程排序   自定义的类必须放在map的输出的key
                这个类必须实现writablecomparable接口
                实现write   readFields   comparaTo
                比较规则定义在comparaTo方法中的
        如果自定义的类没有排序需求  放在map的key上了  一定要实现的writablecomparable
            只要自定义的类放在map的key位置  就会默认按照他进行排序
            map的key的自定义类---慎重
reducetask的并行度----分区

package com.ghgj.cn.testmapreduce;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
	Text mk = new Text();
	IntWritable mv = new IntWritable();
	@Override
	protected void map(LongWritable key, Text value,Context context)
			throws IOException, InterruptedException {
		//获取每一行内容
		String line = value.toString();
		//切分
		String[] num = line.split("\t");
		//循环遍历 发送
		for(String n:num){
			mk.set("hzh");
			mv.set(Integer.parseInt(n));
			context.write(mk, mv);
		}
	}
} 


package com.ghgj.cn.testmapreduce;

import java.io.IOException;

import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

/**
 * reduce输出
 * key :输出最大最小
 * value:值
 *
 */
public class MyReducer extends Reducer<Text,IntWritable, Text, DoubleWritable>{
	Text rk = new Text();
	DoubleWritable rv = new DoubleWritable();
	@Override
	protected void reduce(Text key, Iterable<IntWritable> values,
			Context context) throws IOException, InterruptedException {
		//循环遍历values
		int max =0;
		int min =10000;
		int sum =0;
		int count =0;
		for(IntWritable v:values){
			int data = v.get();
			count++;
			sum+=data;
			//最大
			if(max<data){
				max=data;
			}
			//最小
			if(min>data){
				min=data;
			}
			//平均
			double avg = (double)sum/count;
			rk.set("最大值");
			rv.set(max);
			//context.write()可以调用多次,调用一次输出一条数据
			context.write(rk, rv);
			
			rk.set("最小值");
			rv.set(min);
			context.write(rk, rv);
			
			rk.set("平均值");
			rv.set(avg);
			context.write(rk, rv);
		}
	}
	
}

package com.ghgj.cn.testmapreduce;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DoubleWritable;
import org.apache.hadoop.io.IntWritable;
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 Driver {
	public static void main(String[] args) {
		//将mapper  和  reducer 类进行一个封装   封装为一个任务job(作业)
				//加载配置文件
				Configuration conf = new Configuration();
				//启动一个job【创建一个job对象】
				try {
					Job job = Job.getInstance(conf);
					//设定job
					//先设置整个job的主函数入口
					job.setJarByClass(Driver.class);
					//设置job的mapper的类
					job.setMapperClass(MyMapper.class);
					//设置job的reducer的类
					job.setReducerClass(MyReducer.class);
					//设置map输出key   value的类型
					//指定了泛型这里为什么还要设置一次  泛型的作用范围 编译的时候生效  运行的时候泛型擦除
					job.setMapOutputKeyClass(Text.class);
					job.setMapOutputValueClass(IntWritable.class);
					//设置reduce的输出的key value类型 以下设置的方法设置的是mr的最终输出
					job.setOutputKeyClass(Text.class);
					job.setOutputValueClass(DoubleWritable.class);
					//指定需要统计的文件的输入路径 FileInputFormat文件输入类
//					Path inpath = new Path("/wcin");
					Path inpath = new Path(args[0]);
					FileInputFormat.addInputPath(job, inpath);
					//指定输出目录   输出路径不能存在  否则会报错,默认输出是覆盖式的输出 如果目录存在,有可能造成元数据丢失
//					Path outpath = new Path("/wc_out01");
					Path outpath = new Path(args[1]);
					FileOutputFormat.setOutputPath(job, outpath);
					//提交job 执行这一句时候job才会提交 上面做的一系列的工作都是设置job
					//job.submit
					job.waitForCompletion(true);
				} catch (IOException e) {
					e.printStackTrace();
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//hadoop fs -cat /dataout01/part-r-00000
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值