MapReduce(深入)---案例之用户上行流量 下行流量 总流量倒序 按省份分区

MapReduce实战:统计手机号耗费的总上行流量和下行流量 1.需求: 统计每一个手机号耗费的总上行流量、下行流量、总流量 2.数据准备: 输入数据格式: 时间戳、电话号码、基站的物理地址、访问网址的ip、网站域名、数据包、接包数、上行/传流量、下行/载流量、响应码 最终输出的数据格式: 手机号码 上行流量 下行流量 总流量 3.分析: 基本思路: (1)Map阶段: (a)读取一行数据,切分字段 (b)抽取手机号、上行流量... 阅读详情

1. MapReduce的输入和输出

MapReduce执行流程图

MapReduce历程

详细图解如下

在这里插入图片描述

maptask通过自带的TextInputFormat将数据按照一行一行的读取 , 用每一行的起始偏移量作为k , 每行的内容作为value构成<k1,v1>键值对 , 在map中经过业务逻辑 , 对数据进行的"分" , 形成新的键值对<k2,v2>传递给reduce ; reduce将传递过来的键值对按照字典序排序分组 , 然后经过业务逻辑形成新的<k3,v3>键值对 , 通过自带的TextOutputFormat进行"合" , 最后存储结果

整个流程中会有三组<key,value>键值对类型的存在

在这里插入图片描述

2. MapReduce的处理流程解析

2.1 Mapper任务执行过程详解
  • 第一阶段 , 将文件按照一定的标准逐个进行逻辑切片 , 形成切片规划 . 默认情况下 . Split size = Block size ==128M. 每一个切片由一个MapTask处理 . (getSplits)
  • 第二阶段 , 将切片中数据按照一定规则解析成<k,v>对 . 默认规则是把每一行文本内容解析成键值对 , 即每行起始偏移量是k , 每行的内容为v . (TextInputFormat)
  • 第三阶段 , 调用Mapper中的map方法 , 每解析出来一个<k,v> , 调用一次map方法
  • 第四阶段 , 将第三阶段的键值对进行分区 , 默认只有一个分区 . 分区的数量就是Reducer任务运行的数量 .
  • 第五阶段 , 对每个分区中的键值对进行字典序排序 , 一般不是读取一行就向内存中存储一行 , 而一般是存储在缓冲区中 , 缓冲区默认大小是100M , 缓冲区溢出(满) , 就向内存中存储 , 此时存储的数据是分区且排序的 . 如果有combiner则进入第六阶段 , 否则直接输出到文件中
  • 第六阶段 , 对数据进行局部聚合处理 , 即combiner处理 . 本阶段默认是没有的
2.2 Reducer任务执行过程详解
  • 第一阶段 , Reducer主动从Mapper中复制其输出的键值对
  • 第二阶段 , 对复制的数据进行合并 , 合并后进行排序
  • 第三阶段 , 对排序后的键值对调用reduce方法 , 键相等的键值对调用一次reduce方法 , 最后将输出的键值对写入到HDFS文件中

整个MapReduce程序开发过程中 , 我们最大的工作量就是覆盖map函数和reduce函数

完整的MapReduce执行流程图

在这里插入图片描述

  1. 将目标文件按照一定的规则进行逻辑分块 , 一般分块的大小等于block大小128M (分区注意事项就是数据不能重复 , 不能遗漏) , 有多少个切片最终就启动多少个maptask
  2. 通过自带的TextInputFormat一行一行的读取 , 通过重写map方法将数据写入新的kv对 , 然后为了较少频繁的调用磁盘读写操作 , 将新的kv对先存储到内存缓冲区 , 待内存缓冲区溢出(满)之后写入到磁盘
  3. 在写入内存缓冲区之前 , 我们进行分区操作
  4. 因此写入到磁盘中的数据是分区且排序(字典序)的
  5. reducetask会主动从磁盘中拉取数据进行处理
  6. 首先reducetask会对拉取的数据进行字典序的排序操作 , 然后将k相同的分为一组 , 这一组中共同的k成为新的key , 所有的v构造迭代器成为新的v
  7. 最后通过业务逻辑后 , 使用TextOutputFormat方法相应写入到hdfs中属于各自的分区中

3. MapReduce的序列化

3.1 概述

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

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

hadoop 自己开发了一套序列化机制( Writable),精简,高效。

Writable是Hadoop的序列化格式,hadoop定义了这样一个Writable接口。一个类要支持可序列化只需实现这个接口即可.

3.2 自定义Writable 序列化接口
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/**
 * @author CoderCK
 * @Title: com.itck.mr.pojo
 * @ProjectName example-mr
 * @Description: TODO
 * @create 2018/11/16  14:52
 **/
public class FlowBean implements WritableComparable<FlowBean> {
    private long upFlow;
    private long downFlow;
    private long sumFlow;

    //这里反序列化的时候会用到
    public FlowBean() {
    }

    

    public long getUpFlow() {
        return upFlow;
    }

    public void setUpFlow(long upFlow) {
        this.upFlow = upFlow;
    }

    public long getDownFlow() {
        return downFlow;
    }

    public void setDownFlow(long downFlow) {
        this.downFlow = downFlow;
    }

    public long getSumFlow() {
        return sumFlow;
    }

    public void setSumFlow(long sumFlow) {
        this.sumFlow = sumFlow;
    }

    @Override
    public String toString() {
        return upFlow+"\t"+downFlow+"\t"+sumFlow;
    }


    //这里是序列化方法
    @Override
    public void write(DataOutput out) throws IOException {
        out.writeLong(upFlow);
        out.writeLong(downFlow);
        out.writeLong(sumFlow);

    }

    //这里是反序列化方法
    @Override
    public void readFields(DataInput in) throws IOException {
        //注意反序列化的顺序跟序列化的顺序一致
        this.upFlow = in.readLong();
        this.downFlow = in.readLong();
        this.sumFlow = in.readLong();
    }


    //这里进行bean的自定义比较大小
    @Override
    public int compareTo(FlowBean o) {
        //实现进行bean的自定义比较大小
        return this.sumFlow>o.getSumFlow()?-1:1;
    }
}

compareTo方法用于将当前对象与方法的参数进行比较

如果指定的数与参数相等返回0

如果指定的数小于参数返回-1

如果指定的数大于参数返回1

例如:o1.compareTo(o2);

返回正数的话,当前对象(调用 compareTo 方法的对象 o1)要排在比较对象(compareTo 传参对象 o2)后面,返回负数的话,放在前面。

a.compreTo(b)   0 相等  负数小于  正数大于   谁大谁在后面

4. MapReduce的排序初步

  • 在mr编程中,牢牢把握住k是什么 因为在mr中,k有很多默认的行为
    • 排序 k的字典序
    • 分区 k的哈希值取模
    • 分组 k相同的作为一组
案例 : 统计每个用户(手机号)的总上行流量、下行流量 , 总流量 , 并按总流量倒序排序

案例数据:

1363157985066	13726230503	00-FD-07-A4-72-B8:CMCC	120.196.100.82	i02.c.aliimg.com		24	27	2481	24681	200
1363157995052 	13826544101	5C-0E-8B-C7-F1-E0:CMCC	120.197.40.4			4	0	264	0	200
1363157991076 	13926435656	20-10-7A-28-CC-0A:CMCC	120.196.100.99			2	4	132	1512	200
1363154400022 	13926251106	5C-0E-8B-8B-B1-50:CMCC	120.197.40.4			4	0	240	0	200
1363157993044 	18211575961	94-71-AC-CD-E6-18:CMCC-EASY	120.196.100.99	iface.qiyi.com	视频网站	15	12	1527	2106	200
1363157995074 	84138413	5C-0E-8B-8C-E8-20:7DaysInn	120.197.40.4	122.72.52.12		20	16	4116	1432	200
1363157993055 	13560439658	C4-17-FE-BA-DE-D9:CMCC	120.196.100.99			18	15	1116	954	200
1363157995033 	15920133257	5C-0E-8B-C7-BA-20:CMCC	120.197.40.4	sug.so.360.cn	信息安全	20	20	3156	2936	200
1363157983019 	13719199419	68-A1-B7-03-07-B1:CMCC-EASY	120.196.100.82			4	0	240	0	200
1363157984041 	13660577991	5C-0E-8B-92-5C-20:CMCC-EASY	120.197.40.4	s19.cnzz.com	站点统计	24	9	6960	690	200
1363157973098 	15013685858	5C-0E-8B-C7-F7-90:CMCC	120.197.40.4	rank.ie.sogou.com	搜索引擎	28	27	3659	3538	200
1363157986029 	15989002119	E8-99-C4-4E-93-E0:CMCC-EASY	120.196.100.99	www.umeng.com	站点统计	3	3	1938	180	200
1363157992093 	13560439658	C4-17-FE-BA-DE-D9:CMCC	120.196.100.99			15	9	918	4938	200
1363157986041 	13480253104	5C-0E-8B-C7-FC-80:CMCC-EASY	120.197.40.4			3	3	180	180	200
1363157984040 	13602846565	5C-0E-8B-8B-B6-00:CMCC	120.197.40.4	2052.flash2-http.qq.com	综合门户	15	12	1938	2910	200
1363157995093 	13922314466	00-FD-07-A2-EC-BA:CMCC	120.196.100.82	img.qfc.cn		12	12	3008	3720	200
1363157982040 	13502468823	5C-0A-5B-6A-0B-D4:CMCC-EASY	120.196.100.99	y0.ifengimg.com	综合门户	57	102	7335	110349	200
1363157986072 	18320173382	84-25-DB-4F-10-1A:CMCC-EASY	120.196.100.99	input.shouji.sogou.com	搜索引擎	21	18	9531	2412	200
1363157990043 	13925057413	00-1F-64-E1-E6-9A:CMCC	120.196.100.55	t3.baidu.com	搜索引擎	69	63	11058	48243	200
1363157988072 	13760778710	00-FD-07-A4-7B-08:CMCC	120.196.100.82			2	2	120	120	200
1363157985066 	13726238888	00-FD-07-A4-72-B8:CMCC	120.196.100.82	i02.c.aliimg.com		24	27	2481	24681	200
1363157993055 	13560436666	C4-17-FE-BA-DE-D9:CMCC	120.196.100.99			18	15	1116	954	200

思路分析 : 实现自定义的bean来封装流量信息,并将bean作为map输出的key来传输

分析图如下:

在这里插入图片描述

由于默认的hadoop的序列化已经没法满足我们的需求 , 因此我们自定义了一个javaBean来继承hadoop的Writable来实现新的数据类型 .

具体代码如下

FlowBean类

import org.apache.hadoop.io.Writable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/**
 * @author CoderCK
 * @Title: com.itck.mr.flow
 * @ProjectName example-mr
 * @Description: TODO
 * @create 2018/11/17  20:26
 **/
public class FlowBean implements Writable {

    private long upFlow;
    private long downFlow;
    private long sumFlow;

    public FlowBean() {
    }

    public FlowBean(long upFlow, long downFlow, long sumFlow) {
        this.upFlow = upFlow;
        this.downFlow = downFlow;
        this.sumFlow = sumFlow;
    }
    public FlowBean(long upFlow, long downFlow) {
        this.upFlow = upFlow;
        this.downFlow = downFlow;
        this.sumFlow = upFlow+downFlow;
    }
    public void set(long upFlow, long downFlow) {
        this.upFlow = upFlow;
        this.downFlow = downFlow;
        this.sumFlow = upFlow+downFlow;
    }

    public long getUpFlow() {
        return upFlow;
    }

    public void setUpFlow(long upFlow) {
        this.upFlow = upFlow;
    }

    public long getDownFlow() {
        return downFlow;
    }

    public void setDownFlow(long downFlow) {
        this.downFlow = downFlow;
    }

    public long getSumFlow() {
        return sumFlow;
    }

    public void setSumFlow(long sumFlow) {
        this.sumFlow = sumFlow;
    }

    @Override
    public String toString() {
        return upFlow+"\t"+downFlow+"\t"+sumFlow;
    }

    /**
     *
     * @Description  序列化
     * @Param out
     * @return void
     **/
    @Override
    public void write(DataOutput out) throws IOException {
        out.writeLong(upFlow);
        out.writeLong(downFlow);
        out.writeLong(sumFlow);
    }


    /**
     *
     * @Description  反序列化 , 注意顺序
     * @Param in
     * @return void
     **/
    @Override
    public void readFields(DataInput in) throws IOException {
        this.upFlow=in.readLong();
        this.downFlow=in.readLong();
        this.sumFlow=in.readLong();
    }
}

又因为如果读取一次 , new一次对象 , 性能不友好 , 因此我们自定义构造了两个的构造函数 , 因此我们构造了public FlowBean(long upFlow, long downFlow)

又由于设置参数较为麻烦 , 我们在FlowBean中新增了一个 public void set(long upFlow, long downFlow)方法

上述两个的sumFlow都用sumFlow = upFlow+downFlow表示即可

Mapper类FlowCountMapper.java

public class FlowCountMapper extends Mapper<LongWritable,Text,Text,FlowBean> {
    Text k = new Text();
    FlowBean v = new FlowBean();

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();
        String[] fields = line.split("\t");
        String phoNum = fields[1];
        long upFlow = Long.parseLong(fields[fields.length-3]);
        long downFlow = Long.parseLong(fields[fields.length-2]);
        k.set(phoNum);
        v.set(upFlow,downFlow);
        context.write(k,v);
    }
}

Reducer类FlowCountReducer.java

public class FlowCountReducer extends Reducer<Text,FlowBean,Text,FlowBean> {
    FlowBean v = new FlowBean();
    @Override
    protected void reduce(Text key, Iterable<FlowBean> values, Context context) throws IOException, InterruptedException {
        long upFlow = 0;
        long downFlow = 0;
        for (FlowBean value : values) {
            upFlow+=value.getUpFlow();
            downFlow+=value.getDownFlow();
        }
        v.set(upFlow,downFlow);
        context.write(key,v);
    }
}

主类FlowCountRunner.java

public class FlowCountRunner {
    public static void main(String[] args) throws Exception{
        Configuration conf = new Configuration();
        //设置mr本地执行
        conf.set("mapreduce.framework.name","local");
        Job job = Job.getInstance(conf);

        //指定本次mr程序运行的主类是
        job.setJarByClass(FlowCountRunner.class);

        //指定本次mr程序map reduce类是什么
        job.setMapperClass(FlowCountMapper.class);
        job.setReducerClass(FlowCountReducer.class);

        //指定map阶段输出的数据类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FlowBean.class);

        //指定reduce阶段输出的数据类型  也就是mr最终的输出
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);

        //指定mr程序输入 输出的数据位置 , 此处是在hdfs上进行的
        //FileInputFormat.setInputPaths(job,("/wordcount/input"));
        //FileOutputFormat.setOutputPath(job,new Path("/wordcount/output"));

        //指定mr程序输入 输出的数据位置
        FileInputFormat.setInputPaths(job,new Path("J:\\bigdatatest\\flowsum\\in"));
        FileOutputFormat.setOutputPath(job,new Path("J:\\bigdatatest\\flowsum\\out"));

        //job提交
        boolean b = job.waitForCompletion(true);

        //程序退出
        System.exit(b?0:1);
    }
}

此时得出的结果如下 : 按照默认排序规则 : 即k的 字典序正序

13480253104	180	180	360
13502468823	7335	110349	117684
13560436666	1116	954	2070
13560439658	2034	5892	7926
13602846565	1938	2910	4848
13660577991	6960	690	7650
13719199419	240	0	240
13726230503	2481	24681	27162
13726238888	2481	24681	27162
13760778710	120	120	240
13826544101	264	0	264
13922314466	3008	3720	6728
13925057413	11058	48243	59301
13926251106	240	0	240
13926435656	132	1512	1644
15013685858	3659	3538	7197
15920133257	3156	2936	6092
15989002119	1938	180	2118
18211575961	1527	2106	3633
18320173382	9531	2412	11943
84138413	4116	1432	5548

在mr中 , k是有默认排序行为的 字典序正序

在实际需求中 , 如果需要根据某个属性排序 , 不防把该属性作为key ,

如果默认排序符合需求 , 直接用 ;

如果默认排序不符合需求(倒序 , 自定义对象) , 改写默认排序规则compareTo

由于后续需求又要求根据总流量倒序排序 , 因此我们需要修改FlowBean , 让其重写compareTo方法

并且我们将map的输出端修改为<FlowBean,手机号> , reduce的输出端在修改回来<手机号,FlowBean>

具体代码如下

 public class FlowBean implements WritableComparable<FlowBean> {
 ........
 /**
     *
     * @Description  排序的方法
     * @Param o
     * @return int
     **/
    @Override
    public int compareTo(FlowBean o) {
        //默认是返回值大的在后面
        //这里我们将原始数据大的放前面 , 因此符合条件就返回-1 , 这样就排在前面了
        return this.sumFlow > o.sumFlow ? -1:1;
    }
}

排序的mapper , FlowSortCountMapper.java

public class FlowSortCountMapper extends Mapper<LongWritable,Text,FlowBean,Text> {
    FlowBean k = new FlowBean();
    Text v = new Text();
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();
        String[] fields = line.split("\t");
        String phoNum = fields[0];
        long upFlow = Long.parseLong(fields[1]);
        long downFlow = Long.parseLong(fields[2]);
        k.set(upFlow,downFlow);
        v.set(phoNum);
        context.write(k,v);
    }
}

排序的reduce , FlowSortCountReducer.java

public class FlowSortCountReducer extends Reducer<FlowBean,Text,Text,FlowBean> {

    @Override
    protected void reduce(FlowBean key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
        //TODO 如果迭代器中只有一个元素 , 可以使用下属api获取 不需要for迭代
        Text phoNum = values.iterator().next();
        context.write(new Text(phoNum),key);
    }
}

排序的主类FlowSortCountDriver.java

public class FlowSortCountDriver {
    public static void main(String[] args) throws Exception{
        Configuration conf = new Configuration();
        //设置mr本地执行
        conf.set("mapreduce.framework.name","local");
        Job job = Job.getInstance(conf);

        //指定本次mr程序运行的主类是
        job.setJarByClass(FlowSortCountDriver.class);

        //指定本次mr程序map reduce类是什么
        job.setMapperClass(FlowSortCountMapper.class);
        job.setReducerClass(FlowSortCountReducer.class);

        //指定map阶段输出的数据类型
        job.setMapOutputKeyClass(FlowBean.class);
        job.setMapOutputValueClass(Text.class);

        //指定reduce阶段输出的数据类型  也就是mr最终的输出
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);

        //指定mr程序输入 输出的数据位置 , 此处是在hdfs上进行的
        //FileInputFormat.setInputPaths(job,("/wordcount/input"));
        //FileOutputFormat.setOutputPath(job,new Path("/wordcount/output"));

        //指定mr程序输入 输出的数据位置
        FileInputFormat.setInputPaths(job,new Path("J:\\bigdatatest\\flowsum\\out"));
        FileOutputFormat.setOutputPath(job,new Path("J:\\bigdatatest\\flowsum\\sortout"));

        //job提交
        boolean b = job.waitForCompletion(true);

        //程序退出
        System.exit(b?0:1);
    }

}

在mr编程中,可以把上一个mr的输出目录直接作为下一个mr的输入 mr程序能够自动识别里面什么是检验性文件 什么是成功标识文件 什么是真正的数据文件

输出结果如下 : 按照自定义排序规则(此处是总流量的降序)

13502468823	7335	110349	117684
13925057413	11058	48243	59301
13726238888	2481	24681	27162
13726230503	2481	24681	27162
18320173382	9531	2412	11943
13560439658	2034	5892	7926
13660577991	6960	690	7650
15013685858	3659	3538	7197
13922314466	3008	3720	6728
15920133257	3156	2936	6092
84138413	4116	1432	5548
13602846565	1938	2910	4848
18211575961	1527	2106	3633
15989002119	1938	180	2118
13560436666	1116	954	2070
13926435656	132	1512	1644
13480253104	180	180	360
13826544101	264	0	264
13926251106	240	0	240
13760778710	120	120	240
13719199419	240	0	240

5. MapReduce的分区–Partitioner

分区概念图解

在这里插入图片描述

规律 : reducetask个数和最终结果文件被分成几个部分有关系(对等关系)

如果不写默认是1个 , 全局最终只有一个结果文件 : part-r-00000

如果serNumReduceTasks(N) , 最终就分为N个部分 : part-r-00000…part-r-0000(N-1)

分区规则 : 默认的分区规则是k的哈希值对reducetask个数取模<==>k.hashcode%ReduceTask个数 余数为几 , 分区编号就是几

哈希是一个算法 , 可以计算出一串特征值 , 如果数据不变 , 特征值也不变 , 因此k相同一定会分到同一个分区

案例 : 将流量汇总统计结果按照手机归属地不同省份输出到不同的文件中

思路分析 : 默认的分发规则为:根据 key 的 hashcode%reducetask 数来分发

mr的输出多个文件—>reducetask至少两个及以上---->数据分区(partition)—>默认分区规则(HashPartitioner )—>默认分区规则符不符合需求—>如果满足直接用—>如果不满足—>自定义分区规则

具体代码如下

自定义Partitioner类ProvincePartitioner.java

public class ProvincePartitioner extends Partitioner<Text, FlowBean> {
    
    
    public static HashMap<String, Integer>  provinceMap = new HashMap<String, Integer>();
    
    static{
        provinceMap.put("134", 0);
        provinceMap.put("135", 1);
        provinceMap.put("136", 2);
        provinceMap.put("137", 3);
        provinceMap.put("138", 4);
    }
    @Override
    public int getPartition(Text key, FlowBean value, int numPartitions) {
        Integer code = provinceMap.get(key.toString().substring(0, 3));
        
        if (code != null) {
            return code;
        }   
        return 5;
    }

}

主类即Mapper , Reducer类(三者可以写在一个类中)

public class FlowSumProvince {
    
 public static class FlowSumProvinceMapper extends Mapper<LongWritable, Text, Text, FlowBean>{
        
     Text k = new Text();
     FlowBean  v = new FlowBean();
     
     @Override
     protected void map(LongWritable key, Text value,Context context)
             throws IOException, InterruptedException {
             //拿取一行文本转为String
             String line = value.toString();
             //按照分隔符\t进行分割
             String[] fileds = line.split("\t");
             //获取用户手机号
             String phoneNum = fileds[1];
             
             long upFlow = Long.parseLong(fileds[fileds.length-3]);
             long downFlow = Long.parseLong(fileds[fileds.length-2]);
             
             k.set(phoneNum);
             v.set(upFlow, downFlow);
             
             context.write(k,v);
                
        }
        
    }
    
    
    public static class FlowSumProvinceReducer extends Reducer<Text, FlowBean, Text, FlowBean>{
        
        FlowBean  v  = new FlowBean(); 
        
        @Override
        protected void reduce(Text key, Iterable<FlowBean> flowBeans,Context context) throws IOException, InterruptedException {
            
            long upFlowCount = 0;
            long downFlowCount = 0;
            
            for (FlowBean flowBean : flowBeans) {
                
                upFlowCount += flowBean.getUpFlow();
                
                downFlowCount += flowBean.getDownFlow();
                
            }
            v.set(upFlowCount, downFlowCount);
            
            context.write(key, v);
    }
    
    
    public static void main(String[] args) throws Exception{
        

        Configuration conf = new Configuration();

        Job job = Job.getInstance(conf);

        //指定我这个 job 所在的 jar包位置
        job.setJarByClass(FlowSumProvince.class);
        
        //指定我们使用的Mapper是那个类  reducer是哪个类
        job.setMapperClass(FlowSumProvinceMapper.class);
        job.setReducerClass(FlowSumProvinceReducer.class);
        
        // 设置我们的业务逻辑 Mapper 类的输出 key 和 value 的数据类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(FlowBean.class);
        
        // 设置我们的业务逻辑 Reducer 类的输出 key 和 value 的数据类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(FlowBean.class);
        
        
        //这里设置运行reduceTask的个数
        //正常情况:reducetasks个数要跟分区个数一致
        // reducetasks  > 分区个数   可以执行  多的产生空文件  浪费资源
        // reducetasks  < 分区个数   程序报错  Illegal partition
        job.setNumReduceTasks(6);
        
        
        //这里指定使用我们自定义的分区组件
        job.setPartitionerClass(ProvincePartitioner.class);
        
        
        FileInputFormat.setInputPaths(job, new Path("J:\bigdatatest\flowsum\in"));
        // 指定处理完成之后的结果所保存的位置
        FileOutputFormat.setOutputPath(job, new Path("J:\bigdatatest\flowsum\pout"));
        
        boolean res = job.waitForCompletion(true);
        System.exit(res ? 0 : 1);
        
    }
  }
}

正常情况:reducetasks个数要跟分区个数一致
reducetasks > 分区个数 可以执行 多的产生空文件 浪费资源
reducetasks < 分区个数 程序报错 Illegal partition

指定自定义分区组件: job.setPartitionerClass(ProvincePartitioner.class);

输出结果有6个part-r-0000(0-5)文件 , 依次对应134 , 135 ,136 ,137 ,138以及其他的分区

在这里插入图片描述

6. MapReduce的combiner

combiner就是对map端的输出先做一次合并 , 减少在map和reduce节点之间的数据传输量 , 以提高网络IO性能 , 是MapReduce的一种优化手段之一 .

  • combiner是MR程序中Mapper和Reducer之外的一种组件
  • combiner组件的父类就是Reducer
  • combiner和reducer的区别在于所在的节点运行
    • Combiner是在每个maptask所在的节点运行
    • Reducer是接收全局所有的Mapper的输出结果
  • combiner的意义就是对每一个maptask的输出进行局部汇总 , 以减少网络传输量
  • 具体实现步骤
    • 自定义一个combiner继承Reducer , 重写reduce方法
    • 在job中设置 : job.setCombinerClass(CustomCombiner.class)
  • combiner能够应用的前提是不能影响最终的业务逻辑 , 而且 , combiner的输出kv应该跟reducer的输入kv类型要对应起来

扩展 : 如果遇到复杂对象 , 如我们自定义的对象 , 利用对象进行分组 , 两个对象比较是不相等的 , 因此这里需要我们自定义分组

自定义分组 : GroupingComparator

默认分组:比较k是否相同 相同的作为一组

​ 如果key不同 又想来到同一分组 自定义分组

<FlowBean>
upFlow
downFlow
sumFlow

example:如果上行流量相同就认为是一组
100      100
150      300
250      400
由于两个对象比较肯定是不相等的 , 因此我们这里重写比较方法
    @Override
    public int compare(WritableComparable a, WritableComparable b) {
        FlowBean t1 = (FlowBean) a;
        FlowBean t2 = (FlowBean) b;
        return t1.getUpFlow().compareTo(t2.getUpFlow());
    }
MapReduce案例:手机流量的统计 案例:手机流量的统计 对于记录用户手机信息的文件,得出统计每一个用户(手机号)所耗费的总上行流量、下行流量,总流量结果。 分析 1. 实现自定义的 bean 来封装流量信息,使用手机号码作为Key,Bean作为value。这个Bean的传输需要实现可序 列化,因此我们需要实现MapReduce的序列化接口Writable,重写相关方法。 2. 计算上行流量、下行流量、计费流量 3. <k1,v1>的分析:取一行TextInputFormat类去读取,offset做key,一行数据做value, 阅读详情

相关推荐

MapReduce案例-统计手机号总流量

map方法和reduce方法都是循环调用的 map方法---每行数据调用一次 reduce方法---每个KV调用一次 只执行一次的代码写在setup和cleanup中 统计每个手机号的总流量 数据格式: 1363157985066 13726230503 00-FD-07-A4-72-B8:CMCC 120.196.100.82 i02.c.aliimg.com 24 27 2481 24681 200 需求:统计每个手机号的总流量 数据为多个手机号访问不同网站消耗的流量 每个

X043800的博客 1851

MapReduce面向对象实操2——统计手机号的上行流量、下行流量、总流量

大数据相关博客的目录 数据集 以TAB为分隔符 第一列:序号 第二列:手机号 第三列:网络IP 第四列:访问的域名 第五列:上行流量 第六列:下行流量 第七列:HTTP状态码 1 13736230513 192.196.100.1 www.atguigu.com 2481 24681 200 2 13846544121 192.196.100.2 ...

weixin_46192593的博客 1095

Hadoop_day05_MapReduce 的 经典案例(流量统计)

需求一:统计求和 统计每个手机号的上行数据包总和,下行数据包总和,上行总流量之和,下行总流量之和分析:以手机号码作为key值,上行流量,下行流量,上行总流量,下行总流量四个字段作为value值,然后以这个key,和value作为map阶段的输出,reduce阶段的输入 1.1 自定义SumBean public class SumBean implements Writa...

cpz 869

详细讲解MapReduce二次排序过程

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33795806的博客 567

Ubuntu 24.04.3 LTS 安装 OpenClaw 图文版

OpenClow安装指南

qq_17204557的博客 2551

保姆级教程:用HFSS 2023 R2搞定GPS右旋圆极化微带天线(从建模到优化全流程)

本文提供了一份详细的HFSS 2023 R2教程,指导读者从零开始设计GPS右旋圆极化微带天线。涵盖建模、仿真和优化全流程,特别关注圆极化特性的实现与优化,帮助工程师快速掌握天线设计的关键技术。

weixin_42557830的博客 171

MapReduce案例-流量统计

需求一:统计求和 统计每个手机号的上行数据包总和,下行数据包总和,上行总流量之和,下行总流量之和 分 析:以手机号码作为key值,上行流量,下行流量,上行总流量,下行总流量四个字段作为 value值,然后以这个key,和value作为map阶段的输出,reduce阶段的输入 Step1:自定义map的输出对象FlowBean package com.mapreduce.flow_...

散步的博客 2308

VC Spyglass:Lint实战指南之TCL脚本自动化解析

本文详细介绍了如何利用TCL脚本实现VC Spyglass Lint检查的自动化流程。通过实战指南,从基础脚本编写到高级参数化技巧,帮助数字IC设计工程师摆脱手动操作,提升验证效率,并实现与CI/CD流程的集成。

h3i4j的博客 796

Hadoop案例(九)流量汇总案例

流量汇总程序案例 1.自定义输出 统计手机号耗费的总上行流量、下行流量、总流量(序列化) 1)需求:统计每一个手机号耗费的总上行流量、下行流量、总流量 2)数据准备 phone_date.txt 13726230503 00-FD-07-A4-72-B8:CMCC 120.196.100.82 i02.c.aliimg.com 24...

dengshuabi6933的博客 372

i茅台APP加密参数逆向分析:MT-Device-ID与mt-v生成算法

在移动应用安全领域,参数加密是防止API滥用的重要手段。通过对通信协议的逆向分析,可以揭示加密参数的生成原理,这对理解移动应用安全机制具有重要意义。以i茅台APP为例,其关键接口使用了MT-Device-ID和mt-v两个加密参数,前者基于设备硬件信息生成,后者则通过动态签名机制确保请求时效性。这类技术广泛应用于电商、金融等需要高安全性的场景。通过抓包工具和逆向工程手段,可以还原出从设备指纹生成到动态签名的完整技术链条,其中涉及MD5、SHA256等常见哈希算法的应用。掌握这些加密参数的生成逻辑,不仅有助于

weixin_28568819的博客 320

ε-差分隐私之拉普拉斯噪声机制(定义 + 证明 + 代码)

ε-差分隐私之拉普拉斯噪声机制 差分隐私的描述 1-范式的定义:使用matlab调用函数norm(x, 1) 更多范式见 范式 差分隐私的定义应该满足 拉普拉斯噪声的证明全过程如下: DP方向差分隐私证明是基本入门!!! -拉普拉斯噪声分布代码 Laplace分布的概率密度函数的形式是: 使用python - matplotlib来绘制概率分布图: import matplotlib.pyplot as plt import numpy as np def laplace_fun

毕业才是起点 1万+

MapReduce综合练习之上网流量统计

==== MapReduce综合练习之上网流量统计 需求一:统计求和 统计每个手机号的上行流量总和,下行流量总和,上行总流量之和,下行总流量之和 分析:以手机号码作为key值,上行流量,下行流量,上行总流量,下行总流量四个字段作为value值,然后以这个key,和value作为map阶段的输出,reduce阶段的输入 代码定义如下: 第一步:自定义map的输出value对象FlowBean package cn.itcast.mr.demo3.flow; import org.apache.hadoo.

黄道婆的专栏 425

计算电磁学(三)有限差分方法

有限差分方法(FDM)是一种数值处理方法,通过将偏微分方程变换为一组代数方程,通过求解代数方程组来得到原边值问题的近似值。由于有限差分法非常简单,所以也特别的稳定高效。其中时域有限差分方法(FDTD)是电磁分析中的常用方法。在计算电磁学中,常用有限差分方法(finite difference equations)来替代微分方程。一个有限差分法通常包括三个步骤将求解域划分网格用有限差分方程逼近给定的微分方程求解在给定边界条件和初始状态下的方程常用的网格模型方阵斜方阵三角阵圆形阵。

CCC_B的博客 1639

MapReduce习题练习 08

1. 统计求和 1.1 需求 统计每个手机号的上行流量总和,下行流量总和,上行总流量之和,下行总流量之和 1.2 分析 以手机号码作为key值,上行流量,下行流量,上行总流量,下行总流量四个字段作为value值,然后以这个key,和value作为map阶段的输出,reduce阶段的输入 1.3 代码 1.3.1 自定义map的输出value对象FlowBean public class FlowB...

啊策策大数据社区 2869

电子设计(1)二极管防电源反接电路

二极管经常用来作电源防反接保护,所需器件少,电路简单。

记得诚 1万+

飞腾UEFI配置GPIO—飞腾FT2000/4 (D2000/8) GPIO引脚配置及使用

讲解了在飞腾UEFI代码中如何添加GPIO配置相关代码,并以GPIO1_B0—B7为例,将这8个引脚配置为GPIO工作模式并默认输出低电平

qq_30135687的博客 5344

数据库 流量切分_Hadoop_17_MapRduce_案例2_实现用户手机流量统计(ReduceTask并行度控制)...

需求:1.统计每一个用户(手机号)所耗费的总上行流量、下行流量,总流量1.数据如下:保存为.dat文件(因为以\t切分数据,文件格式必须合适)1363157985066 13726230503 00-FD-07-A4-72-B8:CMCC 120.196.100.82 i02.c.aliimg.com 24 27 2481 24681 ...

weixin_39661405的博客 253

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

MapReduce综合练习题(1)上网流量统计

前言 这个练习题是使用hadoop本地模式运行的。 你需要做的是: 1,把代码复制粘贴,改吧改吧 2,自己把结果跑出来,对照结果是否正确 3,再仔细看一遍代码,百度其中不懂的地方 准备数据 链接:https://pan.baidu.com/s/1wFwJMLTDnLCYkgiwemHQ0g 提取码:ih8l 大小:52kb左右 下载data_flow3.1.dat,里面的数据长这个样子: 需...

大数据流浪法师的学习笔记与分享 1233

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Hadoop案例:WritableComparable实现分区内排序

排序是maprduce的重要部分,这里将利用继承WritableComparable实现自定义排序。

小M呀~之大数据系列 2047

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

MapReduce02:分区与自定义数据类型

MapReduce的入门代码示例

ALX3li的博客 486

Hadoop学习笔记(4)——MapReduce的一些案例

一、流量统计 首先给出元数据表 这是一张手机流量统计表 需求一: 统计求和 统计每个手机号的上行数据包总和,下行数据包总和,上行总流量之和,下行总流量之和。 分析:以手机号码作为key值,上行流量,下行流量,上行总流量,下行总流量四个字段作为value值,然后以这个key,和value作为map阶段的输出,reduce阶段的输入 step 1:创建javaBean用于封装数据 public class FlowBean implements Writable { private Integer

weixin_44898959的博客 521

【Hadoop学习之MapReduce】_17Hadoop序列化

文章目录一、序列化概述(一)什么是序列化(二)为什么要序列化(三)为什么不用Java的序列化(四)Hadoop序列化特点二、自定义bean对象实现序列化接口(Writable)三、序列化案例实操(一)序列化案例分析(二)编写程序(三)运行程序 一、序列化概述 (一)什么是序列化 ​ 序列化就是把内存中的对象,转换成字节序列(或其他数据传输协议)以便于存储到磁盘(持久化)和网络传输。 ​ 反序列...

qq_40947493的博客 545

Hadoop-8

流量汇总程序案例 需求1:统计手机号耗费的总上行流量、下行流量、总流量(序列化) 1)需求: 统计每一个手机号耗费的总上行流量、下行流量、总流量 2)数据准备 phone_data.txt 1363157985066 13726230503 00-FD-07-A4-72-B8:CMCC 120.196.100.82 i02.c.aliimg.com 24 27 2481 24681 200 1...

Denovo的博客 326

MapReduce案例-上行流量倒序排序(递减排序)

需求:按照上行流量倒序排序(递减排序) 分析:自定义FlowBean,以FlowBean为map输出的key,以手机号作为Map输出的value,因为MapReduce程序会对Map阶段输出的key进行排序。 原始数据: 从左到右分别为 手机号 上行流量 下行流量 上行数据包 下行数据包 13480253104 3 180 3 180 13502468823 57 110349 102 7335 13560439658 33 5892 24 2034 13600217502 37 203704 266

李思成的博客 517

MR程序案例 手机流量统计

//需求分析:对上一个MR程序生成的文件再处理:按照总流量进行倒序输出。 //MapReduce(shuffle阶段)只能按照key进行排序, //那么Mapper输出的时候应该以FlowBean对象(实现了WritableComparable --> 变成了可持久化与可比较)为key,然后让shuffle去排序。最后在Reduce阶段,只需将key / vlaue调换即可。 FlowCountDriver类: ​ MR程序的入口: public class FlowCountDriver {

m0_38109926的博客 479

MapReduce 统计手机用户的上行流量,下行流量,总流量,并对输出的结果进行倒序排序。(二),划分省份,输出到不同的文件

在(一)的基础上,写一个自己的partitioner就好了。 分区的默认实现HashPartitioner,它根据key的hashcode和Interger. 在Reduce过程中,可以根据实际需求(比如按某个维度进行归档,类似于数据库的分组),把Map完的数据Reduce到不同的文件中。分区的设置需要与ReduceTaskNum配合使用。比如想要得到5个分区的数据结果。那么就得设置5...

Kingsley's Blog 1329
上一篇: Java----【网络编程】
下一篇: 大数据----【Hive入门】
CoderBoom
博客等级 码龄8年 62粉丝 75原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值