Spark SQL小文件问题解决方案

Spark SQL小文件

小文件是指文件大小显著小于hdfs block块大小的的文件。过于繁多的小文件会给HDFS带来很严重的性能瓶颈,对任务的稳定和集群的维护会带来极大的挑战。

由于Spark本身并不支持小文件合并功能,小文件问题日益突出。

Spark为什么会产生小文件

Spark生成的文件数量直接取决于RDD里partition的数量和表分区数量。注意这里的两个分区概念并不相同,RDD的分区与任务并行度相关,而表分区则是Hive的分区数目。生成的文件数目一般是RDD分区数和表分区的乘积。因此,当任务并行度过高或者分区数目很大时,很容易产生很多的小文件。

因此,如果需要从参数调整来减少生成的文件数目,就只能通过减少最后一个阶段RDD的分区数来达到了(减少分区数目限制于历史数据和上下游关系,难以修改)

1、不含有Shuffle算子的简单静态分区SQL

这样的SQL比较简单,主要是filter上游表一部分数据写入到下游表,或者是两张表简单UNION起来的任务,这种任务的分区数目主要是由读取文件时Partition数目决定的。

  • 因为从Spark 2.4以来,对Hive orc表和parquet支持已经很不错了,为了加快运行速率,我们开启了将Hive orc/parquet表自动转为DataSource的参数。对于这种DataSource表的类型,partition数目主要是由如下三个参数其关系。
spark.sql.files.maxPartitionBates;
spark.sql.files.opencostinbytes;
spark.default.parallelism;

其关系如下所示,因此可以通过调整这三个参数来输入数据的分片进行调整:

-- defaultMaxSplitBytes: spark.sql.files.maxPartitionBates
-- openCostInBytes: spark.sql.files.opencostinbytes
-- bytesPreCore: totalBytes(数据总大小) / spark.default.parallelism

val maxSplitBytes = Math.min(defaultMaxSplitBytes, Math.max(openCostInBytes, bytesPreCore))
  • 而非DataSource表,使用CombineInputFormat来读取数据,因此主要是通过MR参数来进行分片调整:
mapreduce.input.fileinputformat.split.minsize

虽然我们可以通过调整输入数据的分片来对最终文件数量进行调整,但是这样的调整是不稳定的,上游数据大小发生一些轻微的变化,就可能带来参数的重新适配。

为了简单粗暴的解决这个问题,我们对这样的SQL加了repartition 或者 coalesce 的hint,引入了新的shuffle,保证文件数量是一个固定值。

/*reprtition(10)*//*coalesce(10)*/

2、带有Shuffle算子的静态分区任务

在ISSUE SPARK-9858中,引入了一个新的参数:

spark.sql.adaptive.shuffle.targetPostShuffleInputSize

后期基于spark adaptive又对这个参数做了进一步增强,可以动态的调整partition数量,尽可能保证每个task处理targetPostShuffleInputSize大小的数据,因此这个参数我们也可以用来在一定程度上控制生成的文件数量。

Spark2.4后合并小文件

1、方法(加入HINT)

SELECT /*+ COALESCE(3) */                  * FROM t
SELECT /*+ REPARTITION(3) */               * FROM t
SELECT /*+ REPARTITION(c) */               * FROM t
SELECT /*+ REPARTITION(3, c) */            * FROM t
SELECT /*+ REPARTITION_BY_RANGE(c) */      * FROM t
SELECT /*+ REPARTITION_BY_RANGE(3, c) */   * FROM t
注:c为重新分区的分区键

2、原理

a、COALESCE提示减少了分区数。它仅合并分区,因此最大程度地减少了数据移动;
b、REPARTITION提示可以增加或减少分区数量。它执行数据的完全混洗,并确保数据平均分配;
c、REPARTITION增加了一个新阶段,因此它不会影响现有阶段的并行性。相反,COALESCE确实会影响现有阶段的并行性,因为它不会添加新阶段;

3、动态分区任务

3.1、方式1

动态分区任务因为存在着分区这一变量,单纯调整rdd这边的partition数目很难把控整体的文件数量。

在hive里,我们可以通过设置hive.optimize.sort.dynamic.partition来缓解动态分区产生文件过多导致任务执行时task节点经常oom的状况。这样的参数会引入新的的shuffle,来对数据进行重排序,将相同的partition分给同一个task处理,从而避免了一个task同时持有多个文件句柄。

因此,我们可以借助这样的思想,使用distribute by语句来修改sql,从而控制文件数量。一般而言,假设我们想对于每个分区生成不超过N个文件,则可以在SQL末尾增加DISTRIBUTE BY [动态分区列],ceil(rand() * N)。

3.2、方式2

开发类似hive的合并小文件的功能

总结

以上操作是否生效,需要判断自己任务的执行情况,根据任务分析产生小文件的原因,进行相应的参数调整,来达到减少小文件的目的。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雾岛与鲸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值