小文件的定义
一般来说小于等于30M的文件,都叫小文件。 在HDFS中会将一个文件分成若干个block块(128M)存储,如:129M=128M+1M,就会生成2个block块,而在hadoop中的目录、文件、block都会以元数据的方式存储下来(200字节)。 在HDFS中,通常NN维护一个文件的名称,目录结构等大约是250字节。现实中,HDFS的小文件如果不做任何操作增长会很快,现在假设NN节点的内存为4G,差不多42亿字节,现在在HDFS上有一亿个小文件,那么需要250乘一亿大约是250亿字节,这样会将NN撑爆。小文件到达一定数目,就会将NN节点撑爆。就算NN能够存储,对于hive,spark计算时,小文件意味着需要更多的task和资源,同样也会将节点弄挂掉。
为什么会有小文件
- 批处理时造成过多的小文件
- 数据源有大量小文件,未做处理就直接拷贝到hadoop集群中
- MR运算时reduce输出没有做很好的设置
小文件给Hadoop集群带来的瓶颈问题
- 在处理数据时,读取数据会有进程开启和销毁的过程,小文件过多会导致大量的网络磁盘IO的消耗。
- 在MapReduce过程中,会开启大量的MR任务,也就是说会造成大量的shuffle过程。也会造成大量任务处于等待资源的状态。
小文件问题的解决(合并)
源头减少小文件的产生
如果是由数据源产生大量小文件并直接拷贝到Hadoop,可以调研了解数据源是否能生成一些大文件,或者从数据源到HDFS的数据抽取过程中进行数据处理合并小文件。如果每小时只抽取10MB的数据,考虑是否改为每天一次,这样创建1个240MB的文件而不是24个10MB的文件。但是,可能无法控制数据源的改动配合或业务对数据抽取间隔的需求,这样小文件问题无法避免,这时可能需要考虑其他的解决方案。
批量文件合并
当产生小文件是不可避免时,你可以定期运行一个MapReduce任务,读取某一个文件夹中的所有小文件,并将它们重写为较少数量的大文件。比如一个文件夹中有1000个文件,你可以在一个MapReduce任务中指定reduce的数量为5,这样1000个输入文件会被合并为5个文件。随后进行一些简单的HDFS文件/文件夹操作(将新文件覆盖回原目录),则可以将NameNode的内存使用减少到200分之1,并且可以提高以后MapReduce或其他计算引擎对同一数据处理的性能。批量合并文件的方法无法保留原始文件名,如果原始文件名对于你了解数据来源非常重要,则批量合并文件的方法也不适用。但一般来说,我们一般只会设计HDFS的各级目录的文件名,而不会细化到每个文件的名字,所以理论来说这种方法问题也不大。
使用Sequence文件
当需要维护原始文件名时,常见的方法是使用Sequence文件。 在此解决方案中,文件名作为key保存在sequence文件中,然后文件内容会作为value保存。如果一个sequence文件包含10000个小文件,则同时会包含10000个key在一个文件中。sequence文件支持块压缩,并且是可被拆分的。这样MapReduce作业在处理这个sequence文件时,只需要为每个128MB的block启动一个map任务,而不是每个小文件启动一个map任务。当你在同时抽取数百个或者数千个小文件,并且需要保留原始文件名时,这是非常不错的方案。
但是,如果你一次仅抽取少量的小文件到HDFS,则sequence文件的方法也不太可行,因为sequence文件是不可变的,无法追加。比如3个10MB文件将产生1个30MB的sequence文件,根据本文前面的定义,这仍然是一个小文件。另外一个问题是如果需要检索sequence文件中的文件名列表则需要遍历整个文件。
另外一个问题是Hive并不能较好的处理由该方法合并出来的sequence文件。Hive将value中的所有数据视为单行。这样会导致Hive查看这些数据不方便,因为以前小文件中的一行的所有数据也是Hive中的单行,即相当于只有一个字段。同时,Hive没办法访问这种sequence的key,即文件名。当然你可以自定义Hive serde来实现,不过这个超过了本文需要讨论的范围。
来源:oschina
链接:https://my.oschina.net/u/4427158/blog/3164589