hdfs 多个文件合并_处理 HDFS 上的过多小文件的问题?


最近检查发现生产环境 HDFS 上文件和目录数量已经有 70w+ 个,对 namenode 的压力也越来越大,需要对 HDFS 上的小文件进行合并处理,但并非所有文件都可以合并,一般是针对 Hive 表映射在 HDFS 的文件进行合并来减少文件数量,下面整理出来的 3 个处理方法:

方法一:parquet文件

cdh 默认安装路径:/opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/bin/parquet-tools

参考:使用parquet-tools工具查看parquet文件 - 简书

测试本地合并成功

使用官方工具 parquet-tools 合并指定的 parquet 文件

使用 parquet-tools 对多个 parquet 文件进行合并,使用方法:

# 合并 HDFS 上的 parquet 文件

hadoop jar parquet-tools-1.9.0.jar merge /tmp/a.parquet /tmp/b.parquet

# 合并本地的 parquet 文件

java -jar parquet-tools-1.9.0.jar merge /tmp/a.parquet /tmp/b.parquet

参考文档2中也明确地提到:

we strongly recommend *not* to use parquet-tools merge unless you really know what you’re doing. It is known to cause some pretty bad performance problems in some cases. The problem is that it takes the row groups from the existing file and moves them unmodified into a new file – it does *not* merge the row groups from the different files. This can actually give you the worst of both worlds – you lose parallelism because the files are big, but you have all the performance overhead of processing many small row groups.

因此使用parquet-tools并不可取现在一个矛盾,系统有很多小文件,理想方案控制写入parquet 时控制rowgroup 。

参考网站:[PARQUET-460] Parquet files concat tool - ASF JIRA

                  Parquet文件读写与合并小Parquet文件 - 博客 - 编程圈

方法二:parquet文件-python 方式

python读取hdfs上的parquet文件方式_python_得牛网

方法三:hive 参数方式

Create Table As Select (CTAS),即用 hive 把数据从源表(含大量小文件)查出并插入到一张临时表,所有数据插入到临时表后,源表和临时表的表名互换即可。注意,你需要给 hive 会话添加下面的配置来控制小文件合并的条件:

# 控制每个任务合并小文件后的文件大小(默认256000000,256MB):

set hive.merge.size.per.task=2048000000;

# 告诉 hive 什么样的文件属于小文件(默认16000000,小于16MB):

set hive.merge.smallfiles.avgsize=512000000;

# 是否合并Map的输出文件(默认true):

set hive.merge.mapfiles=true;

# 是否合并Reduce的输出文件(默认false):

set hive.merge.mapredfiles=true;

另外,如果 hive 表是分区表,你可以写脚本每个分区遍历合并,但是每个分区合并都需要提交一个 mr job 明显效率不高。更好的做法是在 hive 会话开启动态分区,一次读取多个分区或者全表进行合并(视表的记录数量和集群内存选择吧),下面是开启动态分区的配置:

set hive.exec.dynamici.partition=true;

set hive.exec.dynamic.partition.mode=nonstrict;

目前使用的 hive 版本为:1.1.0

说明:感觉这个方面效果不大好,大于阈值文件其实还很小

方法四:orc 文件

如果 hive 表使用 orc 格式,则可以使用 hql 进行小文件合并。看看官方的原文描述:bash ALTER TABLE table_name [PARTITION (partition_key = 'partition_value' [, ...])] CONCATENATE; If the table or partition contains many small RCFiles or ORC files, then the above command will merge them into larger files. In case of RCFile the merge happens at block level whereas for ORC files the merge happens at stripe level thereby avoiding the overhead of decompressing and decoding the data.

目前产品环境的 hive 表没有使用 orc 格式,因为 impala 2.12.0 不支持 orc 格式,不过 impala 3.1 开始就支持 orc 了。

另外提醒,

sqoop 的增量同步会产生非常多的小文件,最好把方法二的“控制小文件合并的条件”的配置加上。
 

方法五:hdfs 命令方式1

hdfs下的多个文件合并:不适合parquet文件
我现在需要把如下图所示的HDFS里的part-00000、part-000001、part-00002、part-00003合并到part-00004

hdfs dfs -cat /trajectory/test/data/3-25/part-0000* | hdfs dfs -copyFromLocal - /trajectory/test/data/3-25/part-00004

方法六:hdfs 命令方式2

 appendToFile get merage 等也可以,自己测试过也是适合parquet文件格式

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 好的,以下是一个Java代码示例,可以使用Flink从HDFS读取多个文件: ``` import org.apache.flink.api.java.ExecutionEnvironment; import org.apache.flink.api.java.io.TextInputFormat; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.core.fs.Path; public class HDFSReader { public static void main(String[] args) throws Exception { // 创建Flink执行环境 ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); // 设置HDFS文件路径 Path filePath1 = new Path("hdfs://localhost:9000/path/to/first/file"); Path filePath2 = new Path("hdfs://localhost:9000/path/to/second/file"); // 使用TextInputFormat从HDFS读取文件内容 TextInputFormat inputFormat = new TextInputFormat(filePath1); TextInputFormat inputFormat2 = new TextInputFormat(filePath2); // 读取文件内容 env.readFile(inputFormat, filePath1.toString()) .union(env.readFile(inputFormat2, filePath2.toString())) .flatMap((String line, Collector<Tuple2<String, Integer>> out) -> { // 对每行数据进行处理 String[] tokens = line.toLowerCase().split("\\W+"); for (String token : tokens) { if (token.length() > 0) { out.collect(new Tuple2<>(token, 1)); } } }) .groupBy(0) .sum(1) .print(); } } ``` 在这个示例中,我们创建了一个`ExecutionEnvironment`对象,该对象是Flink程序的核心部分,用于执行计算。我们然后指定了多个HDFS文件的路径,并使用`TextInputFormat`来读取文件内容。然后我们使用`readFile`方法从每个文件中读取数据,使用`union`方法将所有数据合并为一个数据集,然后使用`flatMap`方法将每行数据转换为一个元组,其中元组包含单词和计数器1。然后我们使用`groupBy`方法按单词对数据进行分组,并使用`sum`方法计算每个单词出现的次数。最后,我们使用`print`方法打印结果。 ### 回答2: 使用Flink读取HDFS多个文件的Java代码如下: ```java import org.apache.flink.api.java.ExecutionEnvironment; import org.apache.flink.core.fs.Path; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.api.java.DataSet; public class ReadMultipleFilesFromHDFS { public static void main(String[] args) throws Exception { // 创建环境 ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); // 设置Hadoop配置,指定HDFS路径 env.getConfig().setGlobalJobParameters(org.apache.flink.configuration.Configuration.fromArgs(args)); // 从HDFS上读取多个文件 DataSet<String> text = env.readTextFile("hdfs://path/to/directory/*.txt"); // 对读取的数据进行处理 DataSet<String> result = text.flatMap((String line, Collector<String> out) -> { String[] words = line.split(" "); for (String word : words) { out.collect(word); } }).groupBy("word").sum(1); // 打印结果 result.print(); } } ``` 在代码中,`ExecutionEnvironment`用于批处理,而`StreamExecutionEnvironment`用于流处理。根据实际需求选择适合的环境。`readTextFile`方法用于从HDFS上读取文本文件,可以使用通配符来处理多个文件。读取的文件内容会存储在`DataSet`中,按行处理后可以对数据进行各种操作,如拆分、过滤、聚合等。最后,通过调用`print`方法将结果输出。当需要将结果存储到HDFS中时,可以使用`writeAsTextFile`等方法。 ### 回答3: 使用Flink读取HDFS上的多个文件可以使用`TextInputFormat`和`readFile`方法来实现。下面是一个示例的Java代码: ``` import org.apache.flink.api.java.io.TextInputFormat; import org.apache.flink.core.fs.Path; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; public class FlinkReadHDFSFiles { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); // 设置Hadoop配置,指定HDFS文件系统 env.getConfig().setBoolean("fs.hdfs.hadoopconf", true); // 设置要读取的HDFS文件路径 String hdfsPath = "hdfs://localhost:9000/path/to/files/"; // 创建TextInputFormat,并指定要读取的文件路径 TextInputFormat inputFormat = new TextInputFormat(new Path(hdfsPath)); // 使用readFile方法读取HDFS上的多个文件,返回一个DataStream DataStream<String> dataStream = env.readFile(inputFormat, hdfsPath); // 对DataStream进行相应的操作,如打印结果等 dataStream.print(); // 执行Flink任务 env.execute("Flink Read HDFS Files"); } } ``` 注意事项: 1. 需要将`hadoop-common`和`hadoop-hdfs`的依赖添加到项目的`pom.xml`文件中。 2. 需要根据实际情况修改HDFS的配置信息,如HDFS的地址和待读取的文件路径。 3. 可以根据具体需求对`dataStream`进行相应的操作,例如进一步处理数据或将结果输出到其他存储系统。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值