一、hdfs 如何处理小文件
小文件问题的危害
小文件问题对HDFS的性能和稳定性产生显著影响,主要包括:
- 占用过多的存储空间:每个小文件都会占用一个独立的Block,导致存储资源的浪费。
- 降低数据处理效率:HDFS是为处理大文件而设计的,小文件会导致大量的Map任务启动,增加处理时间和资源消耗。
- NameNode内存压力增大:NameNode需要维护所有文件和目录的元数据信息,小文件过多会导致NameNode内存过载,影响系统稳定性。
小文件治理方法
针对小文件问题,我们可以采取以下治理方法:
1. 合并小文件
将多个小文件合并成一个大文件,减少文件数量。这种方法可以通过Hadoop自带的合并工具或Hive的重写方式实现。例如,使用Hive的CombineHiveInputFormat
可以合并多个小文件为一个Map任务处理,从而减少Map任务的数量。
2. 压缩文件
将多个小文件压缩为一个压缩包,减少存储空间。Hadoop提供了多种压缩工具,如gzip、bzip2等,可以根据需要选择合适的压缩算法。压缩后的文件在HDFS上存储,不仅可以减少存储空间,还可以减少文件数量,提高处理效率。
3. 删除无用文件
定期删除不再需要的小文件,释放存储空间。可以使用Hadoop自带的命令如hadoop fs -rm
来删除文件,或者设置定时任务脚本自动删除过期文件。
4. 设置文件过期时间
对于不再需要的文件,可以设置其过期时间,自动删除过期文件。使用hadoop fs -touchz
命令可以设置文件的访问时间,并结合定时任务实现文件的自动删除。
5. 使用SequenceFile
将多个小文件合并到一个SequenceFile中,以减少文件数量,提高处理效率。SequenceFile是Hadoop自带的一种二进制文件格式,适合存储大量小文件。使用SequenceFile.Writer
类可以将多个小文件写入SequenceFile中。
6、 使用Hadoop Archive (HAR):将多个小文件打包成一个单独的文件,减少占用的存储空间和元数据开销。
7、 数据库存储:对于一些小文件,可以考虑使用非HDFS的存储方式,如关系型数据库或NoSQL数据库,使数据存储更加灵活和高效。
实践案例
以日志文件的处理为例,我们可以使用Hive的重写方式来合并小文件。通过设置Hive的相关参数,如hive.merge.mapfiles
、hive.merge.mapredfiles
等,可以实现Map端和Reduce端文件的合并。具体参数设置可以参考Hive的官方文档。
结论
小文件问题是HDFS性能优化的重要议题。通过合并、压缩、删除、设置过期时间等方法,我们可以有效地治理小文件问题,提高HDFS的存储效率和数据处理能力。在实际应用中,我们应根据具体场景和需求选择合适的治理方法,并结合监控和调优手段不断优化系统性能。
二、hive 合并小文件
hive分区下,有很多小文件,例如一个分区有1000个文件,但每个文件大小是10k,数仓大量这种小文件。
小文件太多,需要消耗hdfs存储资源,mr,spark计算的任务数。
为了处理小文件,需要对它们进行合并。
通过 sql 进行合并:
1、concatenate方法
#对于非分区表
alter table tablename concatenate;
#对于分区表
alter table tablename partition(dt=20201224) concatenate;
优点: 使用方便
缺点: concatenate 命令只支持 RCFILE 和 ORC 文件类型,需要执行多次,才能把文件合并为1个。
2、insert overwrite方法
insert overwrite table tableName partition(dt=2022031100)
select
column1,column2
from
tableName
where dt=2022031100
缺点: select 的字段需要自己拼起来,select * 的话,由于带有dt字段,无法写入新分区。
优点: 支持所有数据类型
3、insert overwrite select * 用法
从select * 中去掉一列的方法:
insert overwrite tableA select (name)?+.+ from test;
hive> set hive.cli.print.header=true;
hive> select * from test;
hook status=true,operation=QUERY
OK
name friends children address
songsong ["bingbing","lili"] {"xiao song":18,"xiaoxiao song":19} {"street":"hui long guan","city":"beijing"}
yangyang ["caicai","susu"] {"xiao yang":18,"xiaoxiao yang":19} {"street":"chao yang","city":"beijing"}
Time taken: 0.14 seconds, Fetched: 2 row(s)
从select * 中去掉列 address
hive> select `(address)?+.+` from test;
hook status=true,operation=QUERY
OK
name friends children
songsong ["bingbing","lili"] {"xiao song":18,"xiaoxiao song":19}
yangyang ["caicai","susu"] {"xiao yang":18,"xiaoxiao yang":19}
Time taken: 0.144 seconds, Fetched: 2 row(s)
用这个方法就能去掉分区表的日期字段
注意,语法生效,需要设置
hive> set hive.support.quoted.identifiers=none;
也可以通过参数合并:
在Map输入的时候, 把小文件合并。
-- 每个Map最大输入大小,决定合并后的文件数
set mapred.max.split.size=256000000;
-- 一个节点上split的至少的大小 ,决定了多个data node上的文件是否需要合并
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;
在Reduce输出的时候, 把小文件合并。
-- 在map-only job后合并文件,默认true
set hive.merge.mapfiles = true;
-- 在map-reduce job后合并文件,默认false
set hive.merge.mapredfiles = true;
-- 合并后每个文件的大小,默认256000000
set hive.merge.size.per.task = 256000000;
-- 平均文件大小,是决定是否执行合并操作的阈值,默认16000000
set hive.merge.smallfiles.avgsize = 100000000;
Hadoop的归档文件格式。
set hive.archive.enabled= true ;
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' );