hdfs小文件盘查与优化


背景

最近在任务优化的时候发现,简单的将mapreduce引擎换成spark-sql去执行,但是发现有些sql即使使用spark依然无法被优化,经过探索发现,是由于任务执行过程中产生的小文件过多导致。

小文件带来的影响

首先介绍一下什么是小文件,小文件是指文件size小于HDFS(分布式文件存储系统)上block大小的文件,这样的文件会给Hadoop的拓展性和性能带来严重问题,HDFS本身不适合存储小文件。在HDFS中,任何的block、文件或者目录在内存中均以对象的的形式存储,每个对象约占150byte,如果有1000000个小文件,每个文件占用一个block,则NameNode大约需要2G的内存空间。如果存储1亿个文件,大约需要20G内存,严重制约了集群的拓展。

其次,HDFS在设计时,文件系统块一般都是几千字节,而普通文件系统的磁盘块大约是512字节,远小于HDFS
中的块。这样设计的目的是最小化寻址开销,如果块足够大,从磁盘传输数据的时间会明显大于定位这个块开始
位置所需要的时间,把更多的时间用来传输数据而不是用来查找块所在的位置。

因此,处理大量小文件速度远远小于处理一个大文件的速度,每个小文件都要占用一个task,task的启动将耗费大量时间甚至大部分时间都耗费在启动task和释放task上。

小文件是如何产生的

  1. 数据源本身就含有大量小文件
  2. 使用spark执行任务的时候,并行度高,一个task处理一个逻辑分区的数据,每个task都会输出一个文件
  3. 经常使用sprak-sql去动态分区刷数据,没有Shuffle的情况下,输入端有多少个逻辑分片,对应的HadoopRDD就会产生多少个HadoopPartition,每个Partition对应于Spark作业的Task(个数为M),分区数为N。最好的情况就是(M=N) && (M中的数据也是根据N来预先打散的),那就刚好写N个文件;最差的情况下,每个Task中都有各个分区的记录,那文件数最终文件数将达到M * N个。这种情况下是极易产生小文的。

小文件如何排查

三种途径:

  1. 查看hive表某个分区下的文件数量
  1. 去HDFS中排查文件块的数量
1
hadoop fs -count -v /user/hive/warehouse/database_name.db/table_name
1
hadoop fs -ls /user/hive/warehouse/database_name.db/table_name/*/*/*/*part* | wc -l

例如文件块的数量为:112319 而表中的数据量大小只有45M左右

  1. 查看Spark运行时调度图

DAG

task数量很多,每个task都会产生小文件

如何处理小文件问题

  1. 使用spark-sql加入两个参数进行调整:
1
2
3
set spark.sql.adaptive.enabled=true;

set spark.sql.adaptive.shuffle.targetPostShuffleInputSize=67108864;

spark.sql.adaptive.enabled:是否开启调整partition功能,如果开启,spark.sql.shuffle.partitions设置的partition可能会被合并到一个reducer里运行。默认开启,同时强烈建议开启。理由:更好利用单个executor的性能,还能缓解小文件问题。

spark.sql.adaptive.shuffle.targetPostShuffleInputSize:和spark.sql.adaptive.enabled配合使用,当开启调整partition功能后,当mapper端两个partition的数据合并后数据量小于targetPostShuffleInputSize时,Spark会将两个partition进行合并到一个reducer端进行处理。平台默认为67108864(64M),用户可根据自身作业的情况酌情调整该值。

  1. 使用mapreduce时加入如下参数进行调整:

设置map端输出进行合并,默认为true

1
set hive.merge.mapfiles = true 

设置reduce端输出进行合并,


文章作者: Callable
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Callable !
评论
  目录