大数据开发MapReduce性能优化(第十一篇)

161 阅读4分钟

实际的工作中真正需要去写MapReduce代码的场景非常少了,因为后面有个大数据框架Hive,Hive支持SQL。这个Hive底层会把SQL转换成MapReduce执行,不需要写一行代码。所以在工作中都是使用SQL去实现的。但是对于性能优化还是需要去考虑的

一、小文件处理

Hadoop的HDFS和MapReduce都是针对大数据文件来设计的,在小文件的处理上不但效率低下,而且十分消耗内存资源。针对HDFS而言,每一个小文件在namenode中都会占用150字节的内存空间,最终会导致集群中虽然存储了很多个文件,但是文件的体积并不大,这样的数据处理就没有意义了。

解决方案:

选择一个容器,把这些小文件组织起来统一存储,HDFS提供了两种类型的容器,分别是SequenceFile和MapFile

1.1、SequenceFile
  • 是Hadoop提供的一种二进制文件,这种二进制文件直接将<key,value>对序列化到文件中
  • 一般对小文件可以使用这种文件合并,即将文件名作为key,文件内容作为value序列化到大文件中
  • SequenceFile需要一个合并文件的过程,文件较大,且合并后的文件将不方便查看,必须通过遍历查看每一个小文件
package com.strivelearn.hadoop.hdfs.smallfile;
​
import java.io.File;
​
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;
​
/**
 * The type Sequence file main.
 *
 * @author xys
 * @version SequenceFileMain.java, 2022年09月12日
 */
public class SequenceFileMain {
    /**
     * The entry point of application.
     *
     * @param args the input arguments
     */
    public static void main(String[] args) throws Exception {
        //生成SequenceFile文件
        write("/Users/strivelearn/Desktop/small", "/seqFile");
        //读取SequenceFile文件
        read("/seqFile");
    }
​
    /**
     * 生成SequenceFile文件
     *
     * @param inputDir   输入目录-windows目录
     * @param outputFile 输出文件-hdfs文件
     * @throws Exception the exception
     */
    private static void write(String inputDir, String outputFile) throws Exception {
        //创建一个配置对象
        Configuration conf = new Configuration();
        //指定HDFS的地址
        conf.set("fs.defaultFS", "hdfs://192.168.234.100:9000");
        //获取操作HDFS的对象
        FileSystem fileSystem = FileSystem.get(conf);
        //删除输出文件
        fileSystem.delete(new Path(outputFile), true);
        //构造opts数组,有三个元素
        /*
        第一个是输出路径
        第二个是key类型
        第三个是value类型
        */
        SequenceFile.Writer.Option[] opts = new SequenceFile.Writer.Option[] { SequenceFile.Writer.file(new Path(outputFile)), SequenceFile.Writer.keyClass(Text.class),
                                                                               SequenceFile.Writer.valueClass(Text.class) };
        //创建一个writer实例
        SequenceFile.Writer writer = SequenceFile.createWriter(conf, opts);
        //指定要压缩的文件的目录
        File inputDirPath = new File(inputDir);
        if (inputDirPath.isDirectory()) {
            File[] files = inputDirPath.listFiles();
            for (File file : files) {
                //获取文件全部内容
                String content = FileUtils.readFileToString(file, "UTF-8");
                //文件名作为key
                Text key = new Text(file.getName());
                //文件内容作为value
                Text value = new Text(content);
                writer.append(key, value);
            }
        }
        writer.close();
    }
​
    /**
     * 读取SequenceFile文件
     *
     * @param inputFile SequenceFile文件路径
     * @throws Exception the exception
     */
    private static void read(String inputFile) throws Exception {
        //创建一个配置对象
        Configuration conf = new Configuration();
        //指定HDFS的地址
        conf.set("fs.defaultFS", "hdfs://192.168.234.100:9000");
        //创建阅读器
        SequenceFile.Reader reader = new SequenceFile.Reader(conf, SequenceFile.Reader.file(new Path(inputFile)));
        Text key = new Text();
        Text value = new Text();
        //循环读取数据
        while (reader.next(key, value)) {
            //输出文件名称
            System.out.print("文件名:" + key + ",");
            //输出文件的内容
            System.out.println("文件内容:" + value);
        }
        reader.close();
    }
}

输出结果

image-20220912160026358

1.2、MapFile
  • MapFile是排序后的SequenceFile,MapFile由两部分组成,分别是index和data
  • index作为文件的数据索引,主要记录了每个Record的key值,以及该Record在文件中的偏移位置
  • 在MapFile被访问的时候,索引文件会被加载到内存,通过索引映射关系可迅速定位到指定Record所在文件位置
package com.strivelearn.hadoop.hdfs.smallfile;
​
import java.io.File;
​
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.MapFile;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;
​
/**
 * The type Map file main.
 *
 * @author xys
 * @version MapFile.java, 2022年09月12日
 */
public class MapFileMain {
    /**
     * The entry point of application.
     *
     * @param args the input arguments
     */
    public static void main(String[] args) throws Exception {
        //生成MapFile文件
        write("/Users/strivelearn/Desktop/small", "/mapFile");
        //读取MapFile文件
        read("/mapFile");
    }
​
    /**
     * 生成MapFile文件
     *
     * @param inputDir  输入目录-windows目录
     * @param outputDir 输出目录-hdfs目录
     * @throws Exception the exception
     */
    private static void write(String inputDir, String outputDir) throws Exception {
        //创建一个配置对象
        Configuration conf = new Configuration();
        //指定HDFS的地址
        conf.set("fs.defaultFS", "hdfs://192.168.234.100:9000");
        //获取操作HDFS的对象
        FileSystem fileSystem = FileSystem.get(conf);
        //删除输出目录
        fileSystem.delete(new Path(outputDir), true);
        //构造opts数组,有两个元素
        /*
        第一个是key类型
        第二个是value类型
        */
        SequenceFile.Writer.Option[] opts = new SequenceFile.Writer.Option[] { MapFile.Writer.keyClass(Text.class), MapFile.Writer.valueClass(Text.class) };
        //创建一个writer实例
        MapFile.Writer writer = new MapFile.Writer(conf, new Path(outputDir), opts);
        //指定要压缩的文件的目录
        File inputDirPath = new File(inputDir);
        if (inputDirPath.isDirectory()) {
            File[] files = inputDirPath.listFiles();
            for (File file : files) {
                //获取文件全部内容
                String content = FileUtils.readFileToString(file, "UTF-8");
                //文件名作为key
                Text key = new Text(file.getName());
                //文件内容作为value
                Text value = new Text(content);
                writer.append(key, value);
            }
        }
        writer.close();
    }
​
    /**
     * 读取MapFile文件
     *
     * @param inputDir MapFile文件路径
     * @throws Exception the exception
     */
    private static void read(String inputDir) throws Exception {
        //创建一个配置对象
        Configuration conf = new Configuration();
        //指定HDFS的地址
        conf.set("fs.defaultFS", "hdfs://192.168.234.100:9000");
        //创建阅读器
        MapFile.Reader reader = new MapFile.Reader(new Path(inputDir), conf);
        Text key = new Text();
        Text value = new Text();
        //循环读取数据
        while (reader.next(key, value)) {
            //输出文件名称
            System.out.print("文件名:" + key + ",");
            //输出文件的内容
            System.out.println("文件内容:" + value);
        }
        reader.close();
    }
}