Spark_Bili面试题

115 阅读5分钟

Spark内存分布

0.png

一、Spark处理⼩⽂件

1.⼩⽂件合并综述

1.1小文件表现

不论是Hive还是Spark SQL在使⽤过程中都可能会遇到⼩⽂件过多的问题。⼩⽂件过多最直接的表现是`任务执⾏时间长`,
查看`Spark log`会发现⼤量的数据移动的⽇志。我们可以查看log中展现的⽇志信息,去对应的路径下查看⽂件的⼤⼩和个数。
`hdfs dfs -count <path>` 
通过上述命令可以查看⽂件的个数以及⼤⼩。count查看出的⽂件⼤⼩单位是B,需要转换为MB。
在spark官⽅的推荐⽂档中,parquet格式的⽂件推荐⼤⼩是128MB,⼩于该⼤⼩的均可以称之为⼩⽂件,在实际的⼯作,
往往⼩⽂件的⼤⼩仅仅为⼏KB,表现为,可能⽂件⼤⼩为⼏百MB,但是⽂件个数可能到达了⼏⼗万个。
⼀般来说,我们可以通过简单相除获得⽂件的平均⼤⼩,如果⽂件数⽬不多,我们也可以通过下述命令获得每个⽂件的⼤⼩。
`hdfs dfs -du <path>`

1.2小文件的危害

1.任务执⾏时间长
2.真实的⽂件⼤⼩独占⼀个数据存储块,存放到DataNode节点中。同时 DataNode⼀般默认存三份副本,以保障数据安全。
同时该⽂件所存放的位置也写⼊到NameNode的内存中,如果有Secondary NameNode⾼可⽤节点,也可同时复制⼀份过去。
NameNode的内存数据将会存放到硬盘中,如果HDFS发⽣重启,将产⽣较长时间的元数据从硬盘读到内存的过程。
3.不论在Hive还是在Spark中,每⼀个存储块都对应⼀个Map程序,⼀个Map呈现就需要⼀个JVM,启动⼀个JVM去读取或者写
⼩⽂件是吃⼒不讨好的⾏为。在实际的⽣产中,为了更好的管理集群资源,⼀般会要求程序执⾏时限制Executor数量
和每个Executor的核⼼数量,需要频繁创建Executor来读取写⼊。
4. 其元数据会占⽤⼤量 namenode内存(⼀个元数据⼤概150字节),影响namenode性能
5.影响磁盘寻址时间

1.3 ⼩⽂件出现的原因

1. 启⽤了动态分区,往动态分区表插⼊数据时,会插⼊⼤量⼩⽂件
2. reduce的数量设置的较多,到reduce处理时,会分配到不同的reduce中,会产⽣⼤量的⼩⽂件
3. 源数据⽂件就存在⼤量的⼩⽂件

1.4 ⼩⽂件合并的通俗理解

1.⼩⽂件合并,本质上就是通过某种操作,将⼀系列⼩⽂件合并成⼤⽂件。我们知道,以MapReduce为代表的⼤数据系统,都习惯
⽤K-V键值对的形式来处理⽂件,最后⽂件落盘,也是⼀个reduce对应⼀个输出⽂件。所以直观上,我们可以减少reduce数量,
达到减少⽂件数量的⽬的。
2.MapReduce需要⼀个Shuffle过程,所以我们将⼩⽂件合并理解为通过⼀个Shuffle,合并⼩⽂件成⼀个⼤⽂件。基于这样
的思想,我们的策略可以分为两类:⼀类是原来的计算已经有Shuffle了,那么我们可以认为控制输出⽂件的数量;
⼆类是强制触发Shuffle,进⾏⼩⽂件合并

⼩⽂件合并⽅法策略

1.设置参数(⼀般⽤于Hive)

-- 每个Map最⼤输⼊⼤⼩(这个值决定了合并后⽂件的数量)
 set mapred.max.split.size=256000000;
 -- ⼀个节点上split的⾄少的⼤⼩(这个值决定了多个DataNode上的⽂件是否需要合并)
 set mapred.min.split.size.per.node=100000000;
 -- ⼀个交换机下split的⾄少的⼤⼩(这个值决定了多个交换机上的⽂件是否需要合并)
 set mapred.min.split.size.per.rack=100000000;
 -- 执⾏Map前进⾏⼩⽂件合并
 set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
 -- 每个Map最⼤输⼊⼤⼩(这个值决定了合并后⽂件的数量)
 set mapred.max.split.size=256000000;
 -- ⼀个节点上split的⾄少的⼤⼩(这个值决定了多个DataNode上的⽂件是否需要合并)
 set mapred.min.split.size.per.node=100000000;
 -- ⼀个交换机下split的⾄少的⼤⼩(这个值决定了多个交换机上的⽂件是否需要合并)
 set mapred.min.split.size.per.rack=100000000;
 -- 执⾏Map前进⾏⼩⽂件合并
 set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
 -- 设置map端输出进⾏合并,默认为true
 set hive.merge.mapfiles = true
 -- 设置reduce端输出进⾏合并,默认为false
 set hive.merge.mapredfiles = true
 -- 设置合并⽂件的⼤⼩
 set hive.merge.size.per.task = 256*1000*1000
 -- 当输出⽂件的平均⼤⼩⼩于该值时,启动⼀个独⽴的MapReduce任务进⾏⽂件merge。
 set hive.merge.smallfiles.avgsize=16000000

2-distribute by rand()

往动态分区插⼊数据时,在已经写好的SQL末尾加上distribute by rand()

SET hive.exec.dynamic.partition.mode=nonstrict;
 SET hive.mapred.mode=nonstrict;
 INSERT OVERWRITE TABLE hive_demo.demo_table PARTITION (dt)
 SELECT *
 FROM hive_demo.demo_table
 DISTRIBUTE BY RAND()

该算⼦只是起到打散的效果,但是我们还要设置⽂件的⼤⼩,以免打散后仍然有⼩⽂件。

set hive.exec.reducers.bytes.per.reducer=5120000000

表⽰每个reduce的⼤⼩,Hive可以数据总量,得到reduce个数,假设hive认为会有10个reduce,那么,这⾥rand()则会为 x % 10

3-group by

我们知道,group by算⼦会触发Shuffle,因此只要我们设置好Shuffle时的⽂件个数就好,在Spark SQL中,我们可以设置partition个数,因 为⼀个partition会对应⼀个⽂件。

select name, count(1)
from new_table
group by name

上述的操作,会触发shuffle,因此我们再设置partition个数

set spark.sql.shuffle.partition=10
则表⽰,shuffle后,只会产⽣10个partition

repartition | coalesce