hdfs小文件合并

大数据平台小文件统计及合并

一、小文件统计

1. 统计方法

将namenode 的镜像文件导入hive表,通过sql查询即可统计小文件数量情况。

查看镜像文件: oiv

hdfs oiv -p 文件类型 -i 镜像文件 -o 转换后文件输出路径

1.导出镜像文件:

 hdfs dfsadmin -fetchImage /home/bigdata/fsimage/fsimage_$date

2.解析镜像文件为 "," 逗号分割的csv文件

hdfs oiv -p Delimited -delimiter , -t temporaryDir -i /home/bigdata/fsimage/fsimage_$date -o /home/bigdata/fsimage/fsimage_$date.csv

3.删除csv文件第一行表头信息: Path,Replication,ModificationTime,AccessTime,PreferredBlockSize,BlocksCount,FileSize,NSQUOTA,DSQUOTA,Permission,UserName,GroupName

sed -i '1d' /home/bigdata/fsimage/fsimage_$date.csv

4.导入hive表

4-1. 创建hive分区表(按时间分区):按照导出的表头创建hive表
CREATE TABLE default.fsimage_info_csv(
path string ,
replication int,
modificationtime string,
accesstime string,
preferredblocksize bigint,
blockscount int,
filesize bigint,
nsquota string,
dsquota string,
permission string,
username string,
groupname string)
partitioned by (createdate string)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
WITH SERDEPROPERTIES ( 'field.delim'=',' , 'serialization.format'=',' )
STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'; 
4-2. 将镜像文件数据导入hive表:
sql="load data local inpath '/home/bigdata/fsimage/fsimage_$date.csv' into table default.fsimage_info_csv partition(createdate='$date')"
hive -e $sql

hdfs小文件统计参考链接

2. 统计

1.统计hdfs镜像记录中小于60M的文件

通过统计小文件,发现主要是hive表落盘数据小文件较多;有的表达到每日增量4000多小文件;

2.测试目录数据删除( /tmp目录和特殊目录 )

/tmp 目录下 48000个文件,占用空间4211220.19 M(4T)

FSIMG_FILE_STATIC.LEVELFSIMG_FILE_STATIC.PATHFSIMG_FILE_STATIC.SMALL_FILE_NUMFSIMG_FILE_STATIC.FILE_NUMFSIMG_FILE_STATIC.SUM_SMALL_FILE_SIZEFSIMG_FILE_STATIC.SUM_FILE_SIZEFSIMG_FILE_STATIC.CREATEDATE
3/apps/hive/warehouse110769791147938714698785.20102354870.3820220818
3/app-logs/root/logs-ifile304869304984153710.04281023.6620220818
3/data/monitor/source1031053650642111568.5090465815.0420220818
3/data/monitor/checkpoint4915549155598.46598.4620220818

二、小文件合并策略调研

小文件影响:

在HDFS中,任何一个文件,目录或者block在NameNode节点的内存中均以一个对象表示(元数据),而这受到NameNode物理内存容量的限制。每个元数据对象约占150byte,所以如果有1千万个小文件,每个文件占用一个block,则NameNode大约需要15G空间。如果存储1亿个文件,则NameNode需要150G空间,这毫无疑问1亿个小文件是不可取的.

小文件合并方案:

hdfs文件系统中的文件分为下面几类:

  1. 数据中心落盘文件

  2. hive 数据文件

  3. 系统应用文件

经过技术调研发现均可以用har对hdfs上的文件进行归档。 以下是对小文件合并做的一些实验操作。

实验1. 文件追加合并小文件

1.将hdfs /tmp/test/目录下的part-0-7.gz追加到 part-1-8.gz 文件:(appendToFile)

执行命令:

hdfs dfs -cat /tmp/test/part-0-7.gz  | hdfs dfs -appendToFile - /tmp/test/part-1-8.gz

2.追加命令执行成功,part-1-8.gz文件大小为28.1M(16.7M+11.3M两个文件之和 )

3.下载该part-1-8.gz文件并解压到本地,查看内容:

hdfs dfs -cat /tmp/test/part-1-8.gz | zcat > append_m3_xian.txt

追击后的文件行数与两个文件行数之和一致。

适用范围:

  1. 适合hdfs 普通文件或压缩文件;

  2. hive orc格式的不支持(追加后hive查询会报错)

  3. 操作不够简单

实验2. har 归档方式

在HDFS之上构建一个分层文件系统。通过执行hadoop archive 命令可以创建一个HAR文件。hadoop Archive是一个高效地将小文件放入HDFS块中的文件存档文件格式,它通过将多个小文件打包成一个后缀为.har文件来减少HDFS中的文件数量。

上图为HAR文件的文件结构,访问一个指定的小文件需要访问两层索引文件才能获取小文件在HAR文件中的存储位置,因此,访问一个HAR文件的效率可能会比直接访问HDFS文件要低。

对于一个mapreduce任务来说,如果使用HAR文件作为其输入,仍旧是其中每个小文件对应一个map task,效率低下。所以,HAR files最好是用于文件归档。

上图为Hadoop存档目录,其中包含元数据(以index和masterindex的形式)和数据(part- *)文件。 _index文件包含作为归档文件一部分的文件名以及这些文件内的位置。

归档命令会转换成一个mapreduce 任务;

1)归档特点:

1、存档文件的源文件目录以及源文件都不会自动删除需要手动删除
2、存档的过程实际是一个mapreduce过程,所以需要需要hadoop的mapreduce的支持
3、存档文件本身不支持压缩
4、存档文件一旦创建便不可修改,要想从中删除或者增加文件,必须重新建立存档文件
5、创建存档文件会创建原始文件的副本,所以至少需要有与存档文件容量相同的磁盘空间
6、要归档的文件名中不能有空格,否则会抛出异常,可以将空格用其他符号替换(使用-Dhar.space.replacement.enable=true 和-Dhar.space.replacement参数)。
7、对于一个mapreduce任务来说,如果使用HAR文件作为其输入,仍旧是其中每个小文件对应一个map task,效率低下。所以,HAR files最好是用于文件归档。

2)归档操作说明:

1.归档命令:

归档文件:
hadoop archive -archiveName  m3_monitor.har -p /tmp/test/archive_test/m3_monitor/20220809 /tmp/test/archive
删除数据源目录:
hdfs dfs -rm -r /tmp/test/archive_test/m3_monitor/20220809
​
查看归档文件:
hdfs dfs -ls -R  har:///tmp/test/archive/m3_monitor.har
​
解归档:将归档文件内容拷贝到另一个目录:
hdfs dfs -cp har:///tmp/test/archive/m3_monitor.har/part-1-7.gz  /tmp/test/

参考链接:

归档操作说明

归档说明链接

hive表使用归档文件

实验3. hive 表使用 hive har 进行小文件合并

1)使用appendToFile合并hive orc文件(不支持合并操作)--不可用

hdfs dfs -cat /tmp/test/hive/000203_0 | hdfs dfs -appendToFile - /tmp/test/hive/000204_0

加载进入hive后: 执行查询操作报错;

hive> select * from ods.tb_bu_dc_monitor_day_bak1;
OK
Failed with exception java.io.IOException:java.lang.IllegalArgumentException: Buffer size too small. size = 262144 needed = 1206453
Time taken: 0.166 seconds

orc 格式的文件不支持 getmerge合并操作

hive 文件合并参考链接

2)使用hive concatenate (外部表不可用,内部表可用,orc可用)

使用方式:

#1.设置文件最小大小(需要设置,否则合并操作可能会不理想):
SET mapreduce.input.fileinputformat.split.maxsize=256
#对于非分区表
alter table A concatenate;
#2.对于分区表
alter table B partition(day=20201224) concatenate;

使用限制:

  1. 不支持外部表,只支持内部表;

  2. concatenate 命令只支持 RCFILE 和 ORC 文件类型。

  3. 使用concatenate命令合并小文件时不能指定合并后的文件数量,但可以多次执行该命令参考链接

  4. 当多次使用concatenate后文件数量不再变化,这个跟参数 mapreduce.input.fileinputformat.split.minsize=256mb 的设置有关,可设定每个文件的最小size。

3)使用hive har 归档命令

#用来控制归档是否可用
set hive.archive.enabled=true;
#通知Hive在创建归档时是否可以设置父目录
set hive.archive.har.parentdir.settable=true;
#控制需要归档文件的大小
set har.partfile.size=1099511627776;
#使用以下命令进行归档
ALTER TABLE A ARCHIVE PARTITION(dt='2022-02-24', hr='12');
#对已归档的分区恢复为原文件
ALTER TABLE A UNARCHIVE PARTITION(dt='2022-02-24', hr='12');

ALTER TABLE default.tb_bu_dc_monitor_day_bak_manage ARCHIVE partition(dc_code='wgq_monitor',data_date='20220820');

ALTER TABLE default.tb_bu_dc_monitor_day_bak_manage UNARCHIVE PARTITION(dc_code='wgq_monitor',data_date='20220820');

归档后文件: 分区数据目录下只有一个dat.har文件,原来的数据文件都被删除。

解归档后:分区目录下的dat.har文件删除,新增了原来的数据文件。

4.网上的脚本做小文件合并的:

hdfs小文件使用fsimage分析实例_Direction_Wind的博客-CSDN博客_hdfs小文件分析

filecrush/bin at master · asdaraujo/filecrush · GitHub 开源代码;

三、小文件合并方案总结


目前大数据平台hdfs数据存储情况:

数据仓库中的表:
1.stg层是把flink应用程序写入的数据load进入的;
2.ods层表名称中包含ai的表是从mysql导入的; 
/_SCRATCH0 这些目录是sqoop 把mysql数据导入hive时生成的临时目录,可以删除。
3.stg 和 ods 的 tb_bu_dc_monitor_day 是外部表,其他层的表都是内部表;
4.stg层是原始数据,没有经过压缩处理,ods及以后其他各层都是orc格式
flink程序落盘数据路径:
/data/monitor/source 是各个数据中心指标的落盘数据;

1.hive表使用hive归档命令来进行压缩;

2.flink落盘数据 使用har归档;

四、小文件优化:

hive表数据mapreduce操作落盘做参数优化,对落盘文件大小进行限制。

目前hive使用的是默认的16M

hive小文件参数调优

SET hive.exec.dynamic.partition=true; 
#使用动态分区
SET hive.exec.dynamic.partition.mode=nonstrict;
#默认值为srticat,nonstrict模式表示允许所有分区字段都可以使用动态分区
SET hive.exec.max.dynamic.partitions=3000;
#在所有执行MR的节点上,共可以创建多少个动态分区
SET  hive.exec.max.dynamic.partitions.pernode=500;
#在执行MR的单节点上,最大可以创建多少个分区
SET hive.merge.tezfiles=true;
#tez任务结束时合并小文件
SET hive.merge.smallfiles.avgsize=1280000000;
#当输出文件平均大小小于该值时。启用独立的TEZ任务进行文件合并
SET hive.merge.size.per.task=1280000000;
#合并文件大小128M
————————————————
版权声明:本文为CSDN博主「Impl_Sunny」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u011487470/article/details/120970962

  • 0
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用Hadoop MapReduce框架实现HDFS文件合并的Java代码: package com.example.hadoop; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Reducer; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import java.io.IOException; public class HDFSFileMerging { public static class FileMapper extends Mapper<Object, Text, Text, Text> { private Text filenameKey = new Text(); @Override protected void map(Object key, Text value, Context context) throws IOException, InterruptedException { String filename = value.toString(); filenameKey.set(filename); context.write(filenameKey, new Text("")); } } public static class FileReducer extends Reducer<Text, Text, Text, Text> { private FileSystem fs; private Path tempDir; private Path outputDir; @Override protected void setup(Context context) throws IOException, InterruptedException { Configuration conf = context.getConfiguration(); fs = FileSystem.get(conf); tempDir = new Path(conf.get("tempDir")); outputDir = new Path(conf.get("outputDir")); if (fs.exists(outputDir)) { fs.delete(outputDir, true); } fs.mkdirs(tempDir); } @Override protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException { String filename = key.toString(); Path inputFile = new Path(filename); Path tempFile = new Path(tempDir, filename); fs.copyFromLocalFile(inputFile, tempFile); } @Override protected void cleanup(Context context) throws IOException, InterruptedException { Path[] tempFiles = fs.listStatus(tempDir); for (Path tempFile : tempFiles) { Path outputFile = new Path(outputDir, tempFile.getName()); fs.rename(tempFile, outputFile); } fs.delete(tempDir, true); } } public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "HDFS File Merging"); job.setJarByClass(HDFSFileMerging.class); job.setMapperClass(FileMapper.class); job.setReducerClass(FileReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(Text.class); FileInputFormat.addInputPath(job, new Path(args[0])); Path outputDir = new Path(args[1]); job.getConfiguration().set("tempDir", outputDir + "/temp"); job.getConfiguration().set("outputDir", outputDir.toString()); FileOutputFormat.setOutputPath(job, outputDir); System.exit(job.waitForCompletion(true) ? 0 : 1); } } 在这个例子中,我们定义了一个FileMapper类来读取HDFS上的小文件名并将其作为键传递给FileReducer。在FileReducer中,我们创建一个临时目录来存储所有小文件,并将它们从HDFS上的输入路径中复制到临时目录中。最后,我们将所有临时文件移动到输出目录中,并删除临时目录。请注意,这个例子中的输出目录必须是空的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值