概述
hdfs小文件合并可减少namenode中保存的元数据信息,减轻namenode压力,并且提升数据查询效率。
但要注意到,我们一般只合并n天前的数据,比如3天前,所以基本上我们是在合并冷数据,合并热数据可能出现问题,一般不推荐合并。
小文件合并工具大致可分为 小文件目录扫描 和 小文件合并 两个阶段,下面予以介绍。
小文件扫描1.0
对于直接给定的hdfs目录,我们并不能很好地判断该目录能否进行合并,比如将两个配置文件合并就是不对的,因此小文件合并主要针对hive表进行。
扫描的输入是一系列的hive表,输出是一系列满足合并条件待合并的目录及其对应文件格式,目前主要针对parquet、orc、text这几种文件格式。对于给定的一个hive表,我们首先通过请求hive metastore获取表的根目录,然后递归遍历该目录,并收集那些满足合并条件的目录。
-
合并条件:修改时间在n天前且平均文件大小低于小文件阈值的非空叶子目录,所谓非空叶子目录,即该目录下只含有文件不含有子目录。一方面,叶子目录可避免重复合并。另一方面,由于spark不会读取一个目录的子目录下面的文件,所以不能对文件和子目录并存的目录进行合并,虽然,采用基于表的方式一般不会出现目录下文件和子目录并存的情况,但程序中要避免这种逻辑性的错误。
-
无效目录和文件过滤:对于以
.
和_
开头的目录和文件为无效的,如.hive-staging...
和_SUCCESS
,在扫描时给予排除。 -
保存扫描状态:当前程序会在每次启动时重新扫描指定的表的所有目录(不过貌似采用多线程去扫描也不会耗费太长时间),并不会排除掉前面已经合并过的目录,可考虑优化一下,比如将扫描和合并的状态信息存储到mysql方便做一些其他事情。hdfs和linux下文件修改都是会引起直接父目录的修改时间变动的,但是,修改时间并不会递归向上传递,因此不能基于此进行剪枝。
小文件扫描2.0
目前提供两种小文件合并方式net.qutoutiao.bigdata.smallfile.scan.FileScanner 和 net.qutoutiao.bigdata.smallfile.scan.MetadataScanner。通过配置项smallfile.scanner.class来配置
FileScanner
FileScanner既为小文件扫描1.0,通过smallfile.scan.included.tables来配置要扫描的表
MetadataScanner
MetadataScanner是通过扫描hive元数据来获取小文件较多的表。目前hive元数据已实时同步到tidb。涉及元数据表PARTITIONS、PARTITION_KEY_VALS、PARTITION_KEYS、PARTITION_PARAMS、SDS、TBLS、DBS。
具体查询如下:
val query = s"""
|insert into di_data_merge.smallfile_partitions(part_id,db_name,t