数仓运维:小文件问题解决思路

小文件过多问题产生原因

  • 源头的数据文件数目本身就很多
  • 一般使用动态分区会产生很多小文件(动态分区是根据某个key进行划分分区)
  • reduce 个数越多,小文件数目越多。

小文件过多造成的后果

  • 从 Hive 的角度看,小文件会开很多 map,一个 map 开一个 JVM 去执行,所以这些任务的初始化、启动、执行会浪费大量的资源,严重影响性能;
  • HDFS 存储太多小文件, 会导致 namenode 元数据特别大, 占用太多内存, 制约了集群的扩展;

解决小文件问题思路

  • 通过调整参数进行合并

    • 每个Map最大输入大小(这个值决定了合并后文件的数量)
      • set mapred.max.split.size=256000000;
    • 一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)
      • set mapred.min.split.size.per.node=100000000;
    • 一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)
      • set mapred.min.split.size.per.rack=100000000;
    • 执行Map前进行小文件合并
      • set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
    • 设置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
  • 针对按分区插入数据的时候产生大量的小文件的问题, 可以使用 DISTRIBUTE BY rand() 将数据随机分配给 Reduce,这样可以使得每个 Reduce 处理的数据大体一致.

    • 设置每个reducer处理的大小为5个G set hive.exec.reducers.bytes.per.reducer=5120000000;

    • 使用distribute by rand()将数据随机分配给reduce, 避免出现有的文件特别大, 有的文件特别小 insert overwrite table test partition(dt) select * from iteblog_tmp DISTRIBUTE BY rand();
    • 使用 Sequencefile 作为表存储格式,不要用 textfile,在一定程度上可以减少小文件
  • 使用 hadoop 的 archive 归档

    • Hadoop Archive或者HAR,是一个高效地将小文件放入HDFS块中的文件存档工具,它能够将多个小文件打包成一个HAR文件,这样在减少namenode内存使用的同时,仍然允许对文件进行透明的访问。

    • 用来控制归档是否可用
      • set hive.archive.enabled=true;
    • 通知Hive在创建归档时是否可以设置父目录
      • set hive.archive.har.parentdir.settable=true;
    • 控制需要归档文件的大小
      • set har.partfile.size=1099511627776;
    • 使用以下命令进行归档
      • ALTER TABLE srcpart ARCHIVE PARTITION(ds='2008-04-08', hr='12');
    • 对已归档的分区恢复为原文件
      • ALTER TABLE srcpart UNARCHIVE PARTITION(ds='2008-04-08', hr='12');
    • 注意,归档的分区不能够 INSERT OVERWRITE,必须先 unarchive