study-notes(7 Hadoop)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wpwbb510582246/article/details/83588472

这篇文章是将自己所学技术按模块划分总结而成的笔记,包含了 JavaSE、JavaWeb(SpringMVC、Spring、MyBatis、SpringBoot、SpringCloud 等)、Linux、Hadoop、MapReduce、Hive、Scala、Spark 等,希望通过这些笔记的总结,不仅能让自己对这些技术的掌握更加深刻,同时也希望能帮助一些其他热爱技术的人,这些笔记后续会继续更新,以后自己学习的其他最新技术,也都会以这样笔记的形式来保留,这些笔记已经共享到 Github,大家可以在那里下载到 Markdown 文件,如果大家在看的时候有什么问题或疑问,可以通过邮箱与我取得联系,或者在下面的评论区留言,同时也可以在 Github 上与我进行互动,希望能与大家一起相互学习,相互进步,共同成长。

本篇文章 Github 地址 : https://github.com/wpwbb510582246/study-notes/blob/master/1 JavaSE/1 JavaSE.md

项目 Github 地址 : https://github.com/wpwbb510582246/study-notes

email : [email protected]

博客地址 : https://blog.csdn.net/wpwbb510582246

7 Hadoop

7.1 Hadoop Shell

7.1.1 -p

# -p 保留访问和修改时间,所有权和权限
hdfs dfs -put -p sourceFile desFile

7.1.2 -f

# -f 覆盖原文件(如果已存在)
hdfs dfs -put -f sourceFile desFile

7.1.3 -appendToFile

# -appendToFile 追加一个文件到原文件的末尾
hdfs dfs -put -appendToFile sourceFile desFile

7.1.4 -copyFromLocal

# -copyFromLocal 从本地拷贝一个文件到 HDFS 中去
hdfs dfs -copyFromLocal sourceFile desFile

7.1.5 -copyToLocal

# -copyToLocal 从 HDFS 拷贝一个文件到本地
hdfs dfs -copyToLocal sourceFile desFile

7.1.6 -getMerge

# -getMerge 合并下载多个文件,将多个文件合并下载为一个文件,比如在 HDFS 的 /test 目录下有 log.1、log.2、log.3、log.4,则使用 hdfs dfs -getMerge /test/log.* /log.sum 就可以将这 4 个 log 文件下载到本地,并且合并为一个 log 文件
hdfs dfs -getMerge /test/log.* /log.sum

5.4 MapReduce

5.4.1 WordCount

5.4.1.1 原理

MapReduce 的 WordCount 主要包括以下几个过程 :

1、对文件进行分片、切割

首先,Hadoop 会自动将文件进行分片,然后将每一个分片放到对应节点上的 mapper 里面去读,然后再每一台有 mapper 任务的节点上将对应的分片切割成一行一行的键值对,然后传递到 map 方法中,键是每一行在这个分片中的偏移量,值是每一行中的字符串

2、map

对每一行中的字符串进行切割,生成健值对的形式,然后写出去

3、map 中的 shuffle 阶段

map 中的 shuffle 阶段主要包括 partition、sort、combine,在 partition 阶段主要得到的键值对按照一定的规则分组,比如将 key 中所有首字母为 a 的分到一组,将所有首字母为 b 的分到一组,在 sort 阶段主要对每一个组中的键值对根据键的哈希码排序,在 combine 阶段主要将所有键值相同的键值对合并成一个新的键值对,新的键值对的键是原来的键,键值是所有键的键值之和

4、reduce 中的 shuffle 阶段

reduce 中的 shuffle 阶段主要包括从 partition 中拉取数据、merge、sort,hadoop 决定有多少个 reducer 的时候会规定有多少个 partition,每个 reducer 拉取自己要处理的那个分组的全部成员,例如每一个节点要处理所有以 a 开头的数据,那么它将会从所有的 mapper 中将所有首字母为 a 的数据全部拉取过来,在 merge 阶段,每一个 reducer 会将所有键相同的键值对合并成一个新的键值对,键是以前的键,值是以前所有键值的集合,在 sort 阶段,每一个 reducer 会对所有的键值对根据键的哈希码进行排序

5、reduce

在 reduce 阶段,会将每一个键值对的所有键值累加起来,然后将结果以健值对的形式将结果写出去

6、将结果写入 HDFS

在每一台 reducer 节点上将结果写入,形成一个一个的文件块,但对外表现的形式是一个大文件

5.4.1.2 代码

public class WordCount {

    public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable> {

        /**
         * 单词计数 map
         * 输入数据格式 : <0, hadoop mapreduce hadoop flume> <20, kafka hbase kafka spark>
         * 输出数据格式 : <hadoop, 1> <mapreduce, 1> <hadoop, 1> <flume, 1> <kafka, 1> <hbase, 1> <kafka, 1> <spark, 1>
         *
         * @param key     map 端输入的 key
         * @param value   map 端输入的 value
         * @param context map 上下文
         * @throws IOException
         * @throws InterruptedException
         */

        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            for (String str : value.toString().split("\\t")) {
                context.write(new Text(str), new LongWritable(1));
            }
        }

    }

    public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable> {

        /**
         * 单词计数 reduce
         * 输入数据格式 : <hadoop, <1, 1>> <mapreduce, <1>> <flume, <1>> <kafka, <1, 1>> <hbase, <1>> <spark, <1>>
         * 输出数据格式 : <hadoop, 2> <mapreduce, 1> <flume, 1> <kafka, 2> <hbase, 1> <spark, 1>
         *
         * @param key     reduce 端输入的 key
         * @param values  reduce 端输入的 value
         * @param context reduce 上下文
         * @throws IOException
         * @throws InterruptedException
         */

        @Override
        protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
            Iterator<LongWritable> iterator = values.iterator();
            int count = 0;
            while (iterator.hasNext()) {
                long num = iterator.next().get();
                count += num;
            }
            context.write(key, new LongWritable(count));
        }

    }

    public static void main(String[] args) {

        try {

            //  本地数据来源
            String localInput = "I:\\LearningData\\MyDocumentation\\黑马程序员大数据培训\\所做项目\\BigData\\file\\wordcount\\input";
            //  本地数据输出路径
            String localOutput = "I:\\LearningData\\MyDocumentation\\黑马程序员大数据培训\\所做项目\\BigData\\file\\wordcount\\output";

            //  判断任务是否是本地模式运行
            if (JobUtils.isLocal(Constants.HADOOP_JOB_LOCAL)) {
                //  如果任务是本地模式运行,则将 args 中的参数设置为本地数据来源和输出路径
                args = new String[]{localInput, localOutput};
            }


            //  创建任务
            Job job = Job.getInstance();

            //  设置类的来源
            job.setJarByClass(WordCount.class);

            //  设置 mapper 和 reducer 的类
            job.setMapperClass(MyMapper.class);
            job.setReducerClass(MyReducer.class);

            //  设置 map 的输出的 key 和 value 的数据类型
            job.setMapOutputKeyClass(Text.class);
            job.setMapOutputValueClass(LongWritable.class);

            //  设置 reduce 的输出的 key 和 value 的数据类型
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(LongWritable.class);

            //  设置数据来源和输出路径
            FileInputFormat.setInputPaths(job, new Path(args[0]));
            FileOutputFormat.setOutputPath(job, new Path(args[1] + "/" + DateUtils.formatDateWithSecondsWithoutSeparator(new Date())));

            //  设置任务等待执行完成
            boolean result = job.waitForCompletion(true);
            System.exit(result ? 0 : 1);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

5.4.2 自定义排序

5.4.2.1 原理

自定义排序时,需要建一个实体类,然后实现 WritableComparable 接口,并实现其中的 compareTo、write、readFiles 方法,其中 compareTo 主要用于定义排序规则,write 方法主要用于类的序列化,readFiles 主要用于类的反序列化

5.4.2.2 代码

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class Flow implements WritableComparable<Flow> {

    //  手机号
    private String phone;

    //  上行流量
    private Long upFlow;

    //  下行流量
    private Long downFlow;

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

    /**
     * 对流量进行排序
     * 排序规则 :
     * 1、如果总流量大小不相同,则按照总流量大小进行正序排序
     * 2、如果总流量大小相同,但上行流量大小不相同,则按照上行流量大小进行正序排序
     * 3、如果总流量大小相同,且上行流量大小相同,则按照手机号正序排序
     * @param o 另一个 Flow 对象
     * @return  第二个 Flow 对象与第一个 Flow 对象的比较结果
     */

    @Override
    public int compareTo(Flow o) {
        long result1 = upFlow + downFlow - o.getUpFlow() - o.getDownFlow();
        if (result1 != 0) {
            return (int) result1;
        } else {
            long result2 = upFlow - o.getUpFlow();
            if (result2 != 0) {
                return (int) result2;
            }
        }
        return phone.compareTo(o.getPhone());
    }

    @Override
    public void write(DataOutput dataOutput) throws IOException {
        dataOutput.writeUTF(phone);
        dataOutput.writeLong(upFlow);
        dataOutput.writeLong(downFlow);
    }

    @Override
    public void readFields(DataInput dataInput) throws IOException {
        phone = dataInput.readUTF();
        upFlow = dataInput.readLong();
        downFlow = dataInput.readLong();
    }
}

5.4.3 自定义分区

5.4.3.1 原理

自定义分区时需要写一个类,然后实现 Partitioner<K, V> 接口,其中 K 是指自定义分区的 key,分区时就是根据 key 来进行分区的,V 是自定义分区的 value,然后实现 getPartition 方法,该方法的返回值就是分区号,在实现自定义分区时,需要在 Job 中设置自定义分区类和分区个数,其中分区的个数就是在自定义分区中所分分区的个数

//  设置自定义分区
job.setPartitionerClass(FlowPartitioner.class);
job.setNumReduceTasks(6);

5.4.3.2 代码

public class FlowPartitioner extends Partitioner<Flow, NullWritable> {

    /**
     * 流量排序自定义分区
     * @param flow          自定义分区的 key : 流量
     * @param nullWritable  自定义分区对应的 value : NullWritable
     * @param numPartitions 分区的数量
     * @return  对应的分区序号
     */

    @Override
    public int getPartition(Flow flow, NullWritable nullWritable, int numPartitions) {
        String beforePhone3Digit = flow.getPhone().substring(0, 3);
        switch (beforePhone3Digit) {
            case "134":
            case "135":
                return 0;
            case "136":
            case "137":
                return 1;
            case "138":
            case "139":
                return 2;
            case "150":
            case "159":
                return 3;
            case "182":
            case "183":
                return 4;
            default:
                return 5;
        }
    }

}

5.4.4 自定义 Combiner

5.4.4.1 原理

Combiner 可以对 map 阶段的输出先做一次合并,以减少 map 和 reduce 节点之间的数据传输量,提高网络 IO 性能,是 MapReduce 的一种优化手段之一,在使用时,其方法和 reduce 的方法的逻辑相同,所以可以不用写 combiner 的方法,直接在 job 中设置 Combiner 的类即可

//  设置自定义 Combiner
job.setCombinerClass(FlowSortReducer.class);

5.4.5 MapReduce 中的配置模板

try {

    String localInputPath = "I:\\LearningData\\MyDocumentation\\黑马程序员大数据培训\\所做项目\\BigData\\file\\partitioner\\input";
    String localOutputPath = "I:\\LearningData\\MyDocumentation\\黑马程序员大数据培训\\所做项目\\BigData\\file\\partitioner\\output";

    if (JobUtils.isLocal(Constants.HADOOP_JOB_LOCAL)) {
        args = new String[]{localInputPath, localOutputPath};
    }


    //  创建任务实例
    Job job = Job.getInstance();

    //  设置自定义分区
    job.setPartitionerClass(FlowPartitioner.class);
    job.setNumReduceTasks(6);

    //  设置自定义 Combiner
    job.setCombinerClass(FlowSortReducer.class);

    //  设置类的依赖
    job.setJarByClass(FlowSort.class);

    //  设置 Mapper 和 Reducer 类的依赖
    job.setMapperClass(FlowSortMapper.class);
    job.setReducerClass(FlowSortReducer.class);

    //  设置 map 和 reduce 的输入数据和输出数据格式
    job.setMapOutputKeyClass(Flow.class);
    job.setMapOutputValueClass(NullWritable.class);
    job.setOutputKeyClass(Flow.class);
    job.setOutputValueClass(NullWritable.class);

    //  设置数据来源和数据输出路径
    FileInputFormat.setInputPaths(job, new Path(args[0]));
    FileOutputFormat.setOutputPath(job, new Path(args[1] + "/" + DateUtils.formatDateWithSecondsWithoutSeparator(new Date())));

    //  等待任务运行完成
    boolean result = job.waitForCompletion(true);
    System.exit(result ? 0 : 1);

} catch (Exception e) {
    e.printStackTrace();
}

猜你喜欢

转载自blog.csdn.net/wpwbb510582246/article/details/83588472