背景

1、许多Spark SQL用户都要求一种方法来控制Spark SQL中的输出文件数;

2、Scala/Java/Python代码中可以使用coalesce()和repartition()方法有效的控制Spark文件数量;

3、但用户需要在SparkSQL服务的SQL语句中使用提示;

4、建议在SparkSQL中添加以下Hive样式的COALESCE和REPARTITION提示。提示名称不区分大小写。

Spark2.4前合并小文件

1、方法(在连接SparkSQL后,增加相关参数或者添加到服务端的启动脚本中)

set spark.sql.shuffle.partitions=10;                                   //设置并行度为10
set spark.sql.adaptive.enabled=true;                                   //是否开启调整partition功能,如果开启,spark.sql.shuffle.partitions设置的partition可能会被合并到一个reducer里运行
set spark.sql.adaptive.shuffle.targetPostShuffleInputSize=128000000;   //设置每个 Reducer 读取的目标数据量,其单位是字节。默认64M,一般改成集群块大小
set spark.sql.adaptive.shuffle.targetPostShuffleRowCount=10000000;     //设置每个 Reducer 读取的目标记录数,其单位是条数。
set spark.sql.adaptive.minNumPostShufflePartitions=1;                  // 开启spark.sql.adaptive.enabled后,最小的分区数
set spark.sql.adaptive.maxNumPostShufflePartitions=100;                // 开启spark.sql.adaptive.enabled后,最大的分区数
如原SQL比较特殊,不存在shuffle,改写SQL,在后面加上:distribute by rand()以强制shuffle。

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确实会影响现有阶段的并行性,因为它不会添加新阶段;
d、支持多个插入查询和命名子查询。

3、案例

这个SQL只是将某些字段从老表插入到新表,在预先知道执行的结果只有不到128M的情况,可以直接提示COALESCE(1)。如果sql中使用了join建议使用REPARTITION

create table zdyx.tmp_md_succ_order_collect_d3_rhao as select /*+ COALESCE(1) */ day_id,latn_id,prd_inst_id,acct_id,market_case_busi_type_id,market_case_busi_type_name,type,dev_code,partner_developer_code from tmp_md_succ_order_collect_yd_d;

总结

1、对于原始数据进行按照分区字段进行shuffle,可以规避小文件问题。但有可能引入数据倾斜的问题;

2、可以通过distribute by ss_sold_date_sk, cast(rand() * N as int),N值可以在文件数量和倾斜度之间做权衡;

3、知道倾斜键的情况下,可以将原始数据分成几个部分处理,不倾斜的按照分区键shuffle,倾斜部分可以按照rand函数来shuffle;

4、活用Spark SQL自适应功能,目前Spark 的各版本的Release中其实也就两个参数,设spark.sql.adaptive.enabled=true即可开启该功能;

5、spark.sql.adaptive.shuffle.targetPostShuffleInputSize设置reduce任务处理文件的上限,配合结论3使用,解决小文件问题事半功倍。

参考网址

https://www.jianshu.com/p/ddd2382a738a

https://help.aliyun.com/document_detail/93157.html