在以hdfs为底层存储的大数据架构下,hive表底层文件数的多少直接影响hdfs的nameNode的稳定,以及拉取数据的效率。而以目前云厂商支持的对象存储而言,小文件的个数在一定程度上并不影响存储集群的稳定,只是对数据的拉取有一定的影响,文件读取的IO降低spark的效率。所以目前来讲小文件的合并还是有一定的意义的。

在sparkJar任务重,我们可以通过repatition, Coalesce的方式,去做分区的合并。而在sparkSql中,只能在高版本中使用hint来做重分区,而对于低版本而言,做小文件的分区合并其实就比较麻烦了。

在我们日常运维中关于小文件的问题一般是这样解决的:

  • 监控spark写入表文件的数目,
  • 针对不同的版本/任务类型采用不用的方式去合并
  • sparkJar 做repatition, Coalesce
  • SparkSql 通过改写源代码的方式,做orc文件的合并/代码中做repatition.

关于spark写入表文件的数目,大小的监控可以参考FileFormatWriter类,可以直接获。

关于文件的合并可以根据不同的写入方式做repatition. 比如在没有矢量化读取的要求下,可以参考HiveAnalysis在不同场景下的插入方式,在sparksql分析层做一次repatition. 如果配置了矢量化读取,可能就要把repatition进行提前,因为这个时候应景转换成文件读了,可以提前的relation转换(RelationConversions)的过程中,增加一层repatition的操作。

关于repatition这个有一个很不好的点是,你必须要明确需要分区的数目。然后才能知道怎么进行重分区,这个可能给用户增加一些麻烦。所以可以在driver端做一层合并orc文件的操作,比如在写入完成之后,获取到对应的表的路径,把表的文件先写入到临时的目录,合并之后再写入到对应的表分区目录。这个也是一种做法,不过这种限制了表的类型,因为不同类型的文件合并的方式其实也不一样。我们曾经做过orc文件的合并,其实也挺麻烦的。有更好的方案的同学可以交流一下。