ITPub博客

首页 > > > HDFS小文件过多问题治理指南

HDFS小文件过多问题治理指南

作者:quyuehui 时间:2019-10-12 10:20:17 0 删除 编辑

HDFS小文件过多问题治理指南

目 录

1 目的 1

2 小文件产生的典型场景 2

3 小文件问题的危害 3

4 治理小文件问题的方案 4

4.1 监控 4

4.2 小文件产生过程优化控制 4

4.2.1 JAVA MapReduce: 4

4.2.2 HIVE 5

4.2.3 TEZ 6

4.2.4 SCALA 6

4.3 历史小文件合并归档优化 9

5 备注 11

1 目的

计划对Hadoop集群中的小文件问题进行重点整治,梳理问题背景、影响,以及对应的解决方案,以指导相关工作顺利推进。

2 小文件产生的典型场景

 数据源本身就包含大量的小文件。

 动态分区插入数据,产生大量的小文件,从而导致map数量剧增。

 reduce数量越多,小文件也越多(reduce的个数和输出文件是对应的)。

3 小文件问题的危害

 从Hive的角度看,小文件会开很多map,一个map开一个JVM去执行,所以这些任务的初始化,启动,执行会浪费大量的资源,严重影响性能。

 在HDFS中,每个小文件对象约占150byte,如果小文件过多会占用大量内存。这样NameNode内存容量严重制约了集群的扩展。

 任务计算过程中过多的读写请求对NameNode RPC产生巨大并发压力,极端情况下会对NN RPC请求产生风暴,导致NameNode无法响应其它请求,进而触发切换。

4 治理小文件问题的方案

4.1 监控

使用tableau,配置监控,查看对应业务线的DB、Table的小文件分布情况:

4.2 小文件产生过程优化控制

 使用Sequencefile(orc、parquet)作为表存储格式,不要用textfile,在一定程度上可以减少小文件。

 减少reduce的数量(可以使用参数进行控制)。

 少用动态分区,用时记得按distribute by分区。(注:DISTRIBUTE BY能够控制map的输出在reduce中如何划分。其可以按照指定的字段对数据进行划分到不同的输出reduce/文件中。distribute by与group by对比都是按key值划分数据都使用reduce操作唯一不同的是distribute by只是单纯的分散数据,而group by把相同key的数据聚集到一起,后续必须是聚合操作。)

参考链接:

https://www.iteblog.com/archives/1533.html

4.2.1 JAVA MapReduce:

1) job.setInputFormatClass(CombineTextInputFormat.class)

//指定SplitSize---可选项

CombineTextInputFormat.setMaxInputSplitSize(job, 512*1024*1024L);

//指定输入路径 ---多路径输入情况(1):

CombineTextInputFormat.addInputPath(job, new Path(args[0]));

CombineTextInputFormat.addInputPath(job, new Path(args[1]));

//指定输入路径,以及指定输入格式 ---多路径输入情况(2):

MultipleInputs.addInputPath(job, new Path(args[0]), CombineTextInputFormat.class);

MultipleInputs.addInputPath(job, new Path(args[1]), CombineTextInputFormat.class);

2) 关键参数:

//每个Map最大输入大小(这个值决定了合并后文件的数量)

set mapreduce.input.fileinputformat.split.maxsize=536870912;

//一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)

set mapreduce.input.fileinputformat.split.minsize.per.node=536870912;

//一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)

set mapreduce.input.fileinputformat.split.minsize.per.rack=536870912;

代码控制示例(注:Hadoop默认配置dfs.blocksize=134217728,代码控制分片,非特殊情况不能小于默认值):

final Long MAP_SPLIT_SIZE = 512 * 1024 * 1024L;

job.setConfiguration().set("mapreduce.input.fileinputformat.split.maxsize",MAP_SPLIT_SIZE.toString());

相关参考链接,如下:

http://www.cnblogs.com/skyl/p/4761662.html

4.2.2 HIVE

关键参数优化,如下:

设置map输入合并小文件的相关参数:

//每个Map最小输入大小(一般情况下无需显式设置该参数)

set mapreduce.input.fileinputformat.split.minsize=240000000

//每个Map最大输入大小(这个值决定了合并后文件的数量)

set mapreduce.input.fileinputformat.split.maxsize=536870912;

//一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)

set mapreduce.input.fileinputformat.split.minsize.per.node=536870912;

//一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)

set mapreduce.input.fileinputformat.split.minsize.per.rack=536870912;

//执行Map前进行小文件合并

set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

设置map输出和reduce输出进行合并的相关参数:

//设置map端输出进行合并,默认为true。

set hive.merge.mapfiles=true

//设置reduce端输出进行合并,默认为false。

set hive.merge.mapredfiles=true

//设置合并文件的大小 。

set hive.merge.size.per.task=536870912

//当输出文件的平均大小小于该值时,启动一个独立的MapReduce任务进行文件merge。

set hive.merge.smallfiles.avgsize=134217728

4.2.3 TEZ

//在Map-only的任务结束时合并小文件

set hive.merge.tezfiles=true

//TEZ任务分片最小限制

set tez.grouping.min-size=536870912

//TEZ任务分片最大限制

set tez.grouping.max-size= 1073741824

4.2.4 SCALA

1) 输入文件类型设置为 CombineTextInputFormat

val data = sc.newAPIHadoopFile(args(1),

classOf[CombineTextInputFormat],

classOf[LongWritable],

classOf[Text], hadoopConf)

.map { //TODO }

2) 根据具体情况将分区数设置为合适的值(注:对于小文件的控制及运行效率考虑,建议尽可能设置较小的值,默认情况下rdd根据dfs.blocksize=134217728(128M)进行分区):

Spark RDD 分区算子:coalesce、repartition,相关用法链接,说明如下:

http://lxw1234.com/archives/2015/07/341.htm

https://blog.csdn.net/u011981433/article/details/50035851

http://www.cnblogs.com/fillPv/p/5392186.html

3) Spark sql:

val conf = new SparkConf()

val spark = SparkSession.builder().appName("HiveDemo").config(conf).enableHiveSupport().getOrCreate()

import spark.implicits._

spark.sql("set mapreduce.input.fileinputformat.split.maxsize=536870912")

spark.sql("set mapreduce.input.fileinputformat.split.minsize.per.node=536870912")

spark.sql("set mapreduce.input.fileinputformat.split.minsize.per.rack=536870912")

spark.sql("set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat")

spark.sql("set hive.merge.mapfiles=true")

spark.sql("set hive.merge.mapredfiles=true")

spark.sql("set hive.merge.size.per.task=536870912")

spark.sql("set hive.merge.smallfiles.avgsize=134217728")

spark.stop()

根据实际情况指定:

spark.sql.shuffle.partitions的值(默认值:200)

spark.sql.files.maxPartitionBytes=536870912(默认:134217728 (128 MB))

spark sql自适应计划(这块儿要跟进):

https://www.iteblog.com/archives/2319.html

spark sql自适应patch(对dataset api和sql部分):

https://github.com/apache/spark/pull/20303

Spark SQL 2.0版本之前,reduce的个数是通过spark.default.parallelism和spark.sql.shuffle.partitions两个参数进行配置。如果配置过大,将会导致下游产生很多碎片化的Task,或者下游HDFS产生很多小文件。如果设置过小,将会导致单个ReduceTask计算负载过大:

1

2 、Spark SQL 2.0+支持通过spark.sql.adaptive.enabled来设置reduce大小自适应

、spark.sql.files.ignoreCorruptFiles

hive.exec.reducers.bytes.per.reducer=256 * 1000 * 1000

=>(匹配的spark sql参数,如下:)

spark.sql.adaptive.shuffle.targetPostShuffleInputSize=1073741824(默认值:64 * 1024 * 1024)

mapred.reduce.tasks(mapreduce.job.reduces)

=> (匹配的spark sql参数,如下:)

spark.sql.shuffle.partitions

hive.mapjoin.smalltable.filesize=25000000

=>(匹配的spark sql参数,如下:)

spark.sql.autoBroadcastJoinThreshold=26214400 (默认值:10485760,10L * 1024 * 1024,-1禁用)

transformation

输入小文件合并的改进 (增加支持自定义inputFormat类)

默认采用建表时指定的InpurFormat,如果是默认的TextInputFormat,当小文件比较多是可能会导致RDD的partition数太多,导致性能下降。

解决办法:

通过参数允许用户指定InputFormat,在TableReader中反射生成对应的InputFormat对象并传入到HadoopRDD的构造函数中。

使用方法:

set spark.sql.hive.inputformat=org.apache.hadoop.mapred.lib.CombineTextInputFormat;

输出小文件合并的改进 (增加自动合并结果文件)

当spark.sql.shuffle.partitions设置的比较大且结果数据集比较小时,会产生大量的小文件(文件数等同spark.sql.shuffle.partitions)。

解决办法:

在最后的执行计划中加入一个repartition transformation。通过参数控制最终的partitions数且不影响shuffle partition的数量。

使用方法:

set spark.sql.result.partitions=10;

• 支持yarn-cluster模式,减小client的负载默认的yarn-client模式下Scheduler会运行在client上,加重client机器的负载。

解决办法:

让sparkSQL工具支持yarn-cluster模式。

1)在Yarn集群上部署SparkSQL依赖的hive metastore jar包。

2)开通Yarn nodemanager节点访问Hive metastore数据库的权限。

3)解决“\”“转义问题。如 spark-hive –e “select * fromuser where name = \”张三””;在yarn-cluster模式中会触发两次command执行从而导致“\”被转义两次。

使用示例:

/opt/spark/bin/spark-submit --class org.trainning.spark.sql.hive.SparkSQLCLIDriver \

--master yarn \

--deploy-mode cluster \

--conf spark.dynamicAllocation.enabled=true \

--conf spark.shuffle.service.enabled=true \

--conf spark.dynamicAllocation.minExecutors=1 \

--conf spark.dynamicAllocation.maxExecutors=1000 \

--conf spark.sql.adaptive.enabled=true \

--driver-memory 3g \

--executor-memory 3g \

--name test-sparksql \

--queue default \

hdfs:///apps/hive/udf/SparkSQLCLIDriver.jar -e "select count(*) from saledata.tbldate"

4) groupByKey、reduceByKey、aggregatebykey、combinebykey根据场景区别使用:

注:尽量避免使用groupByKey,reduceByKey和groupByKey都会产生相同的答案,但reduceByKey示例在大型数据集上运行得更好。 这是因为Spark知道它可以在混洗数据之前将输出与每个分区上的公用密钥相结合。

相关参考链接,如下:

https://www.jianshu.com/p/0c6705724cff

https://stackoverflow.com/questions/43364432/spark-difference-between-reducebykey-vs-groupbykey-vs-aggregatebykey-vs-combineb

4.3 历史小文件合并归档优化

 使用hadoop archive命令把小文件进行归档。

参考链接:

https://hadoop.apache.org/docs/current/hadoop-archives/HadoopArchives.html

https://blog.csdn.net/lastsweetop/article/details/9123155

 对于历史的小文件表处理,参考如下(注:hive merge/split等相关参数在生产集群的控制平台都已调整,无需特殊处理):

例:

set hive.exec.dynamic.partition = true; --动态分区必设置参数

set hive.exec.dynamic.partition.mode=nonstrict; ---动态分区必设置参数

set hive.optimize.sort.dynamic.partition=false; ---分区内全局排序,默认是关闭,如果2级以上动态分区则建议开启。

set hive.exec.max.dynamic.partitions.pernode=20000;---根据具体情况来设置

set hive.exec.max.dynamic.partitions=20000; ---根据具体情况来设置

insert overwrite table tab_a partition(key) select a,……,key from tab_a

或者脚本处理:

small-file.sh(非分区表或非algo库的需改写脚本)

#!/bin/bash

srcTable=$1

tmpTable="${srcTable}_new"

cat <<EOF >.small.hql

use algo;

create table if not exists ${tmpTable} like ${srcTable};

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

set hive.exec.max.dynamic.partitions.pernode=100000;

set hive.exec.max.dynamic.partitions=100000;

set hive.exec.max.created.files=100000;

insert into ${tmpTable} partition(stat_date) select * from ${srcTable};

drop table ${srcTable};

ALTER TABLE ${tmpTable} RENAME TO ${srcTable};

EOF

5 备注

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69949006/viewspace-3128852/ ,如需转载,请注明出处,否则将追究法律责任。

上一篇: 没有了~
请登录后发表评论 登录
全部评论

注册时间:2019-10-10

  • 博文量
    7
  • 访问量
    0