FileINputFormat的切片机制
FileInputFormat是MapReduce中用于处理文件输入的基类,它定义了输入文件的切片规则,并提供了默认的切片实现。具体来说,FileInputFormat会根据输入文件的大小和块大小等因素计算出每个切片的起始位置和长度,然后将这些切片封装成InputSplit对象,并将它们作为Mapper的输入。
继承了FileInputFormat的类都使用了FileInputFormat的切片机制。
切片机制
- 简单按照文件的内容长度进行切片
- 集群模式下,切片大小默认=HDFS块大小
- 切片时不是非要把一个切片占满,而是根据文件进行单独切片(不管文件多小都会单独占用一个切片)
案例
输入目录下有3个文件:
a.txt 200MB
b.txt 120MB
c.txt 10MB
我们hadoop3.x默认的HDFS块大小=128MB,则切片如下:
a.txt.split1-- 0~128
a.txt.split2-- 128~200
b.txt.split1-- 0~120
c.txt.split1-- 0~10
TextInputFormat
TextInputFormat是MapReduce默认的实现类,也就是数据的默认的输入到map函数的格式。TextInputFormat是安航读取文件的内容。其中,键是存储每一行的字节偏移量,是LongWritable类型。值是这一行的文本内容(不包括换行符和回车符)。
案例
输入文件a.txt:
hello world
hello hadoop,java
I like java a lot
该文本文件对应的键值对如下:
key value
-------------
0 hello world
12 hello hadoop,java
30 I like java a lot
其中,0、12、13是每一行文本的键,而每一行的内容就是该键所对应的值。
实例:之前的worldcount、流量统计。
KeyValueTextInputFormat
每一行均为一条记录,被分隔符分隔为kry,value。默认的分隔符为 "\t" ,也就是Tab键。可以通过驱动类来设置默认的分隔符,如下:
conf.set(KeyValueLineRecordReader.KRY_VALUE_SEPERATOR,"\t");
分隔符之前的为key,分隔符之后的内容为value,它们都是Text类型。
案例
输入文件:a.txt
hello world
hello java
hello Hadoop,Java
该文本文件对应的键值对如下:
key value
-------------
hello world
hello java
hello Hadoop,Java
其中,每一行的key均为hello,其余内容为值。
NLineInputFormat
如果使用NLineInputFormat,代表每个map进程处理的InputSplit不再按Block块去划分,而是按照NLineInputFormat指定的行数N去划分。也就是说,文件的总行数/N=切片数,如果不能整除,则切片数=商+1。
案例
输入文件:a.txt
hello hadoop
hello world
hello java
hello spark
hello storm
比如N=2,则对应的分片为两个,同时将开启两个MapTask:
分片1:
key value
-------------
0 hello hadoop
13 hello world
25 hello java
分片2:
key value
-------------
35 hello java
46 hello storm
这里的键和TextInputFormat一致。
ConbineTextInputFormat
MapReduce默认的TextInputFormat的切片机制是按文件进行规划切片,集群模式下,默认的切片大小=HDFS块大小。但一般不管文件多小,都会给它分配一个MapTask去执行,这样如果有大量的小文件,就需要开启大量的MapTask,极大地耗费资源。
比如我们的输入目录下有多个文件:
a.txt 1MB
b.txt 1.2MB
c.txt 0.5MB
d.txt 4MB
显然不值得为每一个文件去开启一个MapTask,所以我们使用ConbineTextInputFormat来解决,他可以将多个小文件从逻辑上划分到一个切片当中(逻辑的意思就是并不是把这些小文件合并成一个文件),这样,多个文件就可以交给一个MapTask去执行。
虚拟存储值
ConbineTextINputFormat的切片机制:
虚拟存储过程:
将输入目录下的文件依次与设置的最大分片值进行比较。
- 如果文件大小<=分片最大值,则单独划分为一个虚拟存储块。
- 如果文件大小>分片最大值并且<2*分片最大值,则将文件一分为二,形成两个虚拟存储块。
- 如果文件大小>分片最大值并且>2*分片最大值时,以最大值切割一块。
切片过程:
- 判断虚拟存储文件的大小是否 >= 分片最大值,大于等于则单独形成一个切片。
- 如果不大于则和下一个虚拟存储我呢间共同进行合并,共同形成一个切片。
案例
输入4个文件:
a.txt 1.7MB
b.txt 2.0MB
c.txt 6.8MB
d.txt 5.2MB
虚拟存储过程:
a.txt(1.7MB) < 4MB 划分一块
b.txt(2.0MB) < 4MB 划分一块
c.txt(6.8MB) > 4MB 划分为两块各自3.4MB
d.txt(5.2MB) > 4MB 划分为两块各自2.6MB
最终的虚拟存储文件:
1.7MB
2.0MB
3.4MB
3.4MB
2.6MB
2.6MB
切片过程:首先根据文件名的字典序对文件进行排序,再对虚拟存储文件进行合并
切片1:1.7MB+2.0MB
切片2:3.4MB+3.4MB
切片3:2.6MB+2.6MB
最终切片数从4->3,仍然需要开启3个MapTask,依旧占用资源。这是因为我们 设置的切片最大值过小,如果我们设置切片最大值=20MB,则:
a.txt(1.7MB) < 20MB 划分一块
b.txt(2.0MB) < 20MB 划分一块
c.txt(6.8MB) < 20MB 划分一块
d.txt(5.2MB) < 20MB 划分一块
最终的虚拟存储文件
1.7MB
2.0MB
6.8MB
5.2MB
切片:
切片1:(1.7+2.0+6.8+5.2)
切片数:4->1,只需要开启1个MapTask。
设置数据输入格式
不需要修改代码,只需要在Driver类中修改Job的属性:
默认的输入格式为TextInputFormat,如果是多个小文件的情况,我们需要设置输入格式:
//设置切片规则-默认为TextInputFormat.class
job.setInputFormatClass(CombineTextInputFormat.class);
设置虚拟存储的最大值
//设置虚拟存储切片为4MB-根据实际需求灵活调整大小
// CombineTextInputFormat.setMaxInputSplitSize(job,4194304);
//设置虚拟存储切片为20MB
CombineTextInputFormat.setMaxInputSplitSize(job,20971520);