我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第n篇文章,点击查看活动详情
一、Shuffle过程详解
Shuffle是一个网络拷贝的过程,是指通过网络把数据从map端拷贝到reduce端的过程。
二、Hadoop中序列化机制
之前MapReduce程序的时候使用到了LongWritable和Text这些数据类型,这个数据类型对应的是Java中的Long和String,那为什么不使用Java中的数据类型呢?
这就涉及到了序列化的知识。
- map阶段在读取数据的是需要从hdfs中读取的,这里需要经过磁盘IO和网络IO,不过正常情况下map任务会执行本地计算,也就是map任务会被分发到数据所在的节点进行计算,这个时候,网络IO几乎就没有了,就剩下了磁盘IO。
- map阶段执行完之后,数据会被写入到本地磁盘文件,这时候也需要经过磁盘IO,后面的Shuffle拷贝数据其实也需要经过磁盘IO把数据从本地磁盘读出来再通过网络发送到reduce节点,再写入reduce节点的本地磁盘。
- reduce阶段在执行的时候会经过磁盘IO读取本地文件中的数据,计算完成后还会经过磁盘IO和网络IO把数据写入到hdfs中。
当程序在向磁盘中写数据以及从磁盘中读取数据的时候会对数据进行序列化和反序列化,磁盘IO这些步骤省略不了,所以序列化与反序列化特别重要。
- Java的序列化会把对象及其父类、超类的整个继承体系信息都保存下来,这样存储的信息太大了,就会导致写入文件的信息过大,这样写入是会额外消耗性能的。
- Reduce端想把文件中的对象信息加载到内存中,如果文件很大,在加载的时候也会额外消耗很多性能,所以我们把对象存储的信息尽量精简,那么就可以提高数据写入和读取消耗的性能
| Java基本类型 | Hadoop数据类型 | 序列化大小(字节) |
|---|---|---|
| boolean | BooleanWritable | 1 |
| byte | ByteWritable | 1 |
| int | IntWritable、VIntWritble | 4、1~5 |
| float | FloatWritable | 4 |
| long | LongWritable、VLongWritable | 8、1~9 |
| double | DoubleWritable | 8 |
- Text等价于java.lang.String的Writable,针对UTF-8序列
- NullWritable是单例,获取实例使用NullWritable.get()
2.1、Java序列化与Hadoop序列化代码对比
Java序列化演示
public class JavaSerialize {
@SneakyThrows
public static void main(String[] args) {
Student student = new Student(10L, "张三");
try (FileOutputStream fileOutputStream = new FileOutputStream("/Users/strivelearn/Desktop/java_student.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);) {
objectOutputStream.writeObject(student);
}
}
@Getter
@Setter
@ToString
@AllArgsConstructor
public static class Student implements Serializable {
private static final long serialVersionUID = 1803337923936206206L;
private Long id;
private String name;
}
}
Hadoop序列化
package com.strivelearn.hadoop.hdfs.serialize;
import java.io.*;
import org.apache.hadoop.io.Writable;
import lombok.*;
/**
* @author xys
* @version HadoopSerialize.java, 2022年09月11日
*/
public class HadoopSerialize {
@SneakyThrows
public static void main(String[] args) {
StudentWritable student = new StudentWritable(10L, "张三");
try (FileOutputStream fileOutputStream = new FileOutputStream("/Users/strivelearn/Desktop/hadoop_student.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);) {
student.write(objectOutputStream);
}
}
@Getter
@Setter
@ToString
@AllArgsConstructor
public static class StudentWritable implements Writable {
private Long id;
private String name;
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeLong(this.id);
dataOutput.writeUTF(this.name);
}
@Override
public void readFields(DataInput dataInput) throws IOException {
this.id = dataInput.readLong();
this.name = dataInput.readUTF();
}
}
}
序列化结果
执行结果发现,Java序列化的文件大小是比Hadoop的文件大小大了10倍。由此可见:
Hadoop序列化的优点:
- 紧凑:高效使用存储空间
- 快速:读写数据的额外开销小
- 可扩展:可透明的读取老格式的数据
- 互操作:支持多语言的交互
三、InputFormat
Hadoop中有一个抽象类InputFormat,InputFormat抽象类是MapReduce输入数据的顶层基类,这个抽象类中只定义了两个方法:
@InterfaceAudience.Public
@InterfaceStability.Stable
public abstract class InputFormat<K, V> {
//把输入文件的集合切分为InputSplit的集合
public abstract
List<InputSplit> getSplits(JobContext context
) throws IOException, InterruptedException;
//针对每一个InputSplit创建一个RecordReader,在split被使用之前,框架会先调用RecordReader的initialize初始化方法。
public abstract
RecordReader<K,V> createRecordReader(InputSplit split,
TaskAttemptContext context
) throws IOException,
InterruptedException;
- DBInputFormat是操作数据库的
- FileInputFormat是操作文件类型数据的
- DelegatingInputFormat是用在处理多个输入时使用的
四、OutputFormat
控制MapReduce输出的。
- OutputFormat:是输出数据的顶层基类
- FileOutputFormat:文件数据处理基类
- TextOutputFormat:默认文本文件处理类