实际的工作中真正需要去写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();
}
}
输出结果
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();
}
}