Hadoop小文件影响及解决

一、小文件产生的原因

   当文件的大小远远小于HDFS block块的大小(hadoop2:128m)就可以当作是一个小文件;具体产生的原因包括一下:

    1)实时计算时,如果窗口开的小,在hdfs上会产生很多小文件

    2)离线计算,批处理时,在spark或者mr时,没有设置好partition或者reduce的个数,会产生小文件

    3)Flume采集数据时,没有配置好具体的滚动策略,会产生小文件

    4)数据源本身存在很多小文件

 

二、小文件的影响

  1)元数据影响:namenode将文件系统的元数据存放在内存中,因此存储的文件数目受限于 namenode的内存大小。HDFS中每个文件、目录、数据块 占用150Bytes。如果存放的文件数目过多的话会占用很大的内存甚至撑爆内存;

     2)mr任务影响:在mapreduce中,对每个文件都会启动一个map task,如果小文件太多,影响性能;

  3)在hdfs的读流程里,如果小文件越多,寻址花费的时间越多

 

三、如何处理小文件

  • 在向hdfs写入数据前合并,包括map端合并小文件和reduce端合并小文件
    #设置map输入合并小文件的相关参数:
    
    //每个Map最大输入大小(这个值决定了合并后文件的数量,调大可以减小Mapper数),默认128M
    set mapred.max.split.size=1024*1024*512;  
    
    //一个节点上split的至少的大小(小于这个值会进行合并,这个值决定了多个DataNode上的文件是否需要合并)
    set mapred.min.split.size.per.node=1024*1024*512;
    
    //一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)  
    set mapred.min.split.size.per.rack=100000000;
    
    //设置hive输入端进行小文件合并
    set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
    
    # 减少reduce的数量
    -- 除非知道数据量的分布,一般不直接设置
    set mapred.reduce.tasks=10;
    
    -- 每个reduce处理的数据量,实际上可能每个reduce 处理的数据量会超过它,默认1G 增加该参数,可以减小reduce数量
    set hive.exec.reducers.bytes.per.reducer=1073741824 
    
    
    # 设置map输出和reduce输出进行合并的相关参数:
    //设置map端输出进行合并,默认为true
    set hive.merge.mapfiles = true
    
    //设置reduce端输出进行合并,默认为false
    set hive.merge.mapredfiles = true
    
    //设置合并文件的大小
    set hive.merge.size.per.task = 256*1000*1000
    
    //当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge。
    set hive.merge.smallfiles.avgsize=16000000
  • 对hdfs上的已存在的小文件进行合并

    1)Hadoop Archive小文件归档:

      hadoop archive -archiveName event.har -p /app/event -r 3  sub_path sub_path2 /app/har

      event.har :归档文件的名称

      /app/event:需要归档文件的父目录

      sub_path: 子目录,可以是多个,中间用空格分隔

      /app/har:存储的目录

      

      在归档结束之后,需要手动删除归档后的路径:

      hdfs dfs -rmr /app/event/sub_path

    2)CombineFileInputFormat<K,V> 小文件合并

    3)手动合并,先将hdfs上的文件copy到本地,本地合并后在上传到hdfs

#!/bin/bash
bizdate=$1
path=$2
for (( i = 0; i < 10; i++ )); do
    bizhour="0${i}"
    echo "${bizdate} ${bizhour}"
    hdfs  dfs  -test -e /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=access/
    if [ $? -ne 1 ];
       then
         echo "进行hdfs操作"
         hdfs  dfs  -cat /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=access/part-00* | hdfs  dfs  -copyFromLocal - /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=access/part-11
         hdfs  dfs  -rmr /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=access/part-00*
    fi
    hdfs  dfs  -test -e /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=action/
    if [ $? -ne 1 ];
      then
        echo "进行hdfs操作"
        hdfs  dfs  -cat /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=action/part-00* | hdfs  dfs  -copyFromLocal - /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=action/part-11
        hdfs  dfs  -rmr /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=action/part-00*
    fi
done
for (( i = 10; i < 24; i++ )); do
    bizhour=${i}
    echo "${bizdate} ${bizhour}"
    hdfs  dfs  -test -e /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=access/
    if [ $? -ne 1 ];
       then
         echo "进行hdfs操作"
         hdfs  dfs  -cat /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=access/part-00* | hdfs  dfs  -copyFromLocal - /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=access/part-11
         hdfs  dfs  -rmr /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=access/part-00*
    fi
    hdfs  dfs  -test -e /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=action/
    if [ $? -ne 1 ];
      then
        echo "进行hdfs操作"
        hdfs  dfs  -cat /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=action/part-00* | hdfs  dfs  -copyFromLocal - /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=action/part-11
        hdfs  dfs  -rmr /${path}/bizdate=${bizdate}/bizhour=${bizhour}/log_type=action/part-00*
    fi
done

 

      

    

 

    

posted @ 2021-11-26 15:14  Shydow  阅读(1169)  评论(0编辑  收藏  举报