1 影响
- hdfs设计目的就不是为了存储小文件,而是高吞的文件系统,如果小文件过多会导致nn的元数据信息太多,会导致nn的内存吃紧
- 处理文件的时候,小文件特别小,但是打开时间却要很久,读取元数据信息的时间需要很久,会造成资源浪费,nn访问文件需要不断的寻址
- 小文件的计算成本也会很高,引擎原因可能会开启多个map
2 产生
说到小文件那就得说说小文件怎么来的,什么操作会产生小文件,hdfs内的小文件产生的原因,我个人总结了以下三点
2.1 insert ino直接向表中插入数据,
- 如果是分区表,有目标分区,就追加,不会产生小文件.但是如果分区表无目标分区就会产生新的文件,甚至是小文件
- 如果是非分区表,那就直接追加到文件末尾不会产生小文件
- 但是以上两点都受制于几个hive的小文件合并参数,有可能每次都会新增新的小文件
2.2 load加载数据
- hive load数据有两种形式,一种覆写一种插入
- 这一点也会受制于几个hive的小文件合并参数,可能每次都会新增新的小文件
2.3 日常的select查询插入
2.3.1 不说tez
2.3.2 hive
2.3.2.1 没有shuffle
insert into table tmp.tbname values ('col1_value', 1),('col1_value', 2);
$ hadoop fs -ls /user/hive/warehouse/tmp.db/tbname/day=2018-12-12
Found 2 items
-rwxrwxrwx 3 hadoop supergroup 26 2019-12-20 15:56 hdfs://sdg/user/hive/warehouse/ttmpdb/tbname/day=2018-12-12/000000_0
--结论:一个单独的插入语句会启动一个mr任务,回生成一个对应的结果文件
2.3.2.2 有shuffle
hive> insert into table tmp.czc_hive_game select count(*), game_id from ttmpsource_table group by game_id;
Hadoop job information for Stage-1: number of mappers: 62; number of reducers: 1
--结论:groupby的时候按照key字段进行shuffle, 最后启动了62个map和一个reduce,最后由reduce聚合输出,所以只有一个
2.3.2.3 hive如何决定写文件的个数
reduce个数决定任务的最终效果,不单独设置的情况下,hive会默认生成一个reduce
相关参数是
hive.exec.reducers.bytes.per.reducer
-- reduce处理数据量大小,默认1g
hive.exec.reducers.max
--reduce接受的数据量的参数,那么只会有一个reduce任务;
2.3.3 spark
2.3.3.2 没有shuffle
spark.sql("insert into table tmp.czc_spark_test_write values ('col1_value', 1),('col1_value', 2)")
$ hadoop fs -ls /user/hive/warehouse/ttmpdb/czc_spark_test_write
Found 2 items
-rwxrwxrwx 3 hadoop supergroup 13 2019-12-20 17:01 /user/hive/warehouse/tetmpb/czc_spark_test_write/part-00000-0db5ce49-fb85-4c15-bac8-fa2213a03203-c000
-rwxrwxrwx 3 hadoop supergroup 13 2019-12-20 17:01 /user/hive/warehouse/temtmp/czc_spark_test_write/part-00001-0db5ce49-fb85-4c15-bac8-fa2213a03203-c000
$ hadoop fs -cat /user/hive/warehouse/temptmpczc_spark_test_write/part-00000-0db5ce49-fb85-4c15-bac8-fa2213a03203-c000
col1_value 1
$ hadoop fs -cat /user/hive/warehouse/temp.tmpzc_spark_test_write/part-00001-0db5ce49-fb85-4c15-bac8-fa2213a03203-c000
col1_value 2
--结论:就算是一个简单的插入语句也会启动两个并行分区并产生两个文件
2.3.3.3 有shuffle
scala> spark.sql("insert into table tmp.tbname select count(*), game_id from ttmpsource_table group by game_id");
res1: org.apache.spark.sql.DataFrame = []
hive> select count(distinct game_id) from tetmpource_table;
OK
26
$ hadoop fs -ls hdfs://sdg/user/hive/warehouse/temtmp/tbname
Found 26 items
-rwxrwxrwx 3 hadoop supergroup 0 2019-12-20 14:46 hdfs://sdg/user/hive/warehouse/temptmptbname/part-00000-2f2213b2-a5b7-4c20-83da-dd25c4b018b9-c000
-rwxrwxrwx 3 hadoop supergroup 8 2019-12-20 14:46 hdfs://sdg/user/hive/warehouse/temp.tmpzc_spark_game/part-00007-2f2213b2-a5b7-4c20-83da-dd25c4b018b9-c000
-rwxrwxrwx 3 hadoop supergroup 7 2019-12-20 14:46 hdfs://sdg/user/hive/warehouse/temp.dtmpc_spark_game/part-00010-2f2213b2-a5b7-4c20-83da-dd25c4b018b9-c000
-rwxrwxrwx 3 hadoop supergroup 9 2019-12-20 14:46 hdfs://sdg/user/hive/warehouse/temp.dbtmp_spark_game/part-00011-2f2213b2-a5b7-4c20-83da-dd25c4b018b9-c000
-rwxrwxrwx 3 hadoop supergroup 9 2019-12-20 14:46 hdfs://sdg/user/hive/warehouse/temp.db/tmpspark_game/part-00012-2f2213b2-a5b7-4c20-83da-dd25c4b018b9-c000
-rwxrwxrwx 3 hadoop supergroup 9 2019-12-20 14:46 hdfs://sdg/user/hive/warehouse/temp.db/ctmppark_game/part-00032-2f2213b2-a5b7-4c20-83da-dd25c4b018b9-c000
-rwxrwxrwx 3 hadoop supergroup 14 2019-12-20 14:46
.........
--结论:每个key一个shuffle分区,一共26个文件
2.3.3.3 spark的文件数量
不是由spark框架直接决定的,而是由输入数据的文件系统或存储系统决定的,spark读取文件来获取数据,然后分布式运行,一般都是按照文件的个数直接决定分区数, 每个文件被视为一个输入分区,所以如果希望增加这里的并行可以试试orc相关的读参数或者是map的split参数,或者是pyspark的一些支持分区的算子
2.4 动态分区
动态分区如果没有事现调配好分区参数,也会产生很多小文件,甚至溢出执行引擎
3 避免/处理
3.1 hive/mr
--调整map参数
--map合并小文件
--map输出和reduce输入
--控制在数据加载和查询过程中是否合并MapReduce任务的输出文件
hive.merge.mapfiles
hive.merge.smallfiles.avgsize
--调整reduce参数
--每个reduce任务处理的数据量,默认1g
hive.exec.reducers.bytes.per.reducer
--如果reduce的输入, 也就是map的输出,大小不超过1g,就只会有一个reduce
hive.exec.reducers.max
--调整mr参数
mapred.max.split.size
mapreduce.job.reduces
3.2 spark
有些网上的参数可以适用
-- 动态规划shuffle的过程
-- 并不是所有版本的hive通用,可以用set测试一下
set spark.sql.adaptive.enabled=true
-- 这些需要调配
spark.sql.shuffle.partitions;
set spark.yarn.driver.memory=16g;
set spark.executor.memory=8g;
set spark.executor.instances=256;
set spark.executor.cores=6;
--sparksql还可以搭配di's't'ru'b
3.3 hdfs
- 如果是orc优先使用lzo压缩
- 直接用concatenate快速合并
- ALTER TABLE/PARTITION XXX CONCATENATE;hive自带的合并文件的命令
- 使用hadoop命令archive
- 先进行unarchive
3.4 其他
- 情况有时候是多重复杂的,可以配置crontab脚本定期进行小文件合并操作