这是我参与「第四届青训营 」笔记创作活动的第4天
01 大数据处理引擎Spark介绍
1.1 大数据处理技术栈
1.2 常见大数据处理链路
1.3 开源大数据处理引擎
1.4 什么是Spark?
(有兴趣的伙伴可以去官网看看)
github:github.com/apache/spar…
Apache Spark特点:是一个用于大规模数据处理的统一分析引擎
Apache Spark解释:是一个用于在单节点机器或集群上执行数据工程、数据科学和机器学习的多语言引擎
Spark版本演进
Spark生态 & 特点
-
统一引擎,支持多种分布式场景
-
多语言支持
-
可读写丰富数据源
-
丰富灵活的API/算子
-
支持K8S/YARN/Mesos资源调度
1.5 Spark特点
多语言支持
- SQL:
SELECT
name.first AS first_name,
name.last AS last_name,
age
FROM json.'logs.json'
WHERE age > 21;
- Java
Dataset df = spark.read().json("logs.json");
df.where("age > 21")
.select("name.first").show();
- Scala
Val df = spark.read.json("logs.json")
df.where("age > 21")
.select("name.first").show()
- R
df <- read.json(path = "logs.json")
df <- filter(df,df$age > 21)
head(select(df,df$name.first))
- Python
df = spark.read.json("logs.json")
df.where("age > 21").select("name.first").show()
丰富数据源
内置DataSource:
-
Text
-
Parquet/ORC
-
JSON/CSV
-
JDBC
自定义DataSource:
-
实现DataSourceV1/V2 API
-
HBase/Mongo/ElasticSearch/...
-
Apache Spark第三方软件包的社区索引:spark-packages.org
丰富的的API/算子
SparkCore -> RDD
-
map / filter / flatMap / mapPartitions
-
repartition / groupBy / reduceBy / join / aggregate
-
foreach / foreachPartition
-
count / max / min
...
SparkSQL -> DataFrame
-
select / filter / groupBy / agg / join / union / orderBy / ...
-
Hive UDF / 自定义UDF
1.6 Spark运行架构 & 部署方式
√ Spark Local Mode
- 本地测试/单进程多线程模式
spark-sql --master local[*] ...
Spark Standalone Mode
- 需要启动Spark的Standalone集群的Master/Worker
spark-sql --master spark://${master_ip}:${port} ...
on YARN/K8S
- 依赖外部资源调度器(YARN/K8S)
spark-sql --master yarn ...
spark-sql --master k8s://https://<k8s-apiserver-host>:<k8s-apiserver-port> ...
1.7 Spark下载编译
Spark包
编译:
git clone -b master https://github.com/apache/spark.git
cd spark
./dev/make-distribution.sh --name custom-spark --pip --r --tgz -Psparkr -Phive -Phive-thriftserver
-Pmesos -Pyarn
编译参数可选,
详见官网buliding-spark(spark.apache.org/docs/latest…
编译完后会在目录下生成一个tgz包
下载:
官网download -->
1.8 Spark包概览
1.9 Spark提交命令
环境变量
spark-shell
spark-sql
pyspark
1.10 提交一个简单任务
SparkPi scala代码
编译成jar包之后,使用spark-submit提交
1.11 Spark UI
1.12 Spark性能benchmark
TPC-DS / TPC-H benchmark
02 SparkCore原理解析
2.1 SparkCore
2.2 什么是RDD?
RDD(Resilient Distributed Dataset):表示可以并行操作的不可变分区元素集合
abstract class RDD{
def getPartitions:Array[Partition]...
def compute(split:Partition...
def getDependencies:Seq[Dependency[_]]...
val partitioner:Option[Partitioner]...
def getPreferredLocations(split:Partition)...
//算子
def map(f:T => U):RDD[U]
def filter(f:T => Boolean):RDD[T]
...
def count():Long
def cache() //缓存
def persist() //缓存
}
- 描述RDD的五要素
2.2.1 如何创建RDD?
√ 内置RDD
-
ShuffleRDD / HadoopRDD / JDBCRDD
/KafkaRDD / UnionRDD / MapPartitionsRDD / ...
√ 自定义RDD
class CustomRDD(...) extends RDD{}
实现五要素对应的函数
2.2.2 RDD算子
两类RDD算子
√ Transform算子:生成一个新的RDD
map / filter / flatMap / groupByKey / reduceByKey / ...
√ Action算子:触发Job提交
collect / count / take / saveAsTextFile
2.2.3 RDD依赖
RDD依赖:描述父子RDD之间的依赖关系(lineage)
窄依赖: 父RDD的每个partition至多对应一个子RDD分区
√ NarrowDependency
def getParents(partitionld:Int):Seq[Int]
√ OneToOneDependency
override def getParents(partitionld:Int):List[Int]=List(partitionld)
√ RangeDependency
override def getParents(partitionld:Int):List[Int]=
if(partitionld >= outStart && partitionld < outStart + length)
List(partitionld - outStart + inStart)
√ PruneDependency
宽依赖: 父RDD的每个partition都可能对应多个子RDD分区
√ ShuffleDependency
2.2.4 RDD执行流程
2.3 调度器
2.4 内存管理
Executor内存主要有两类:Storage、Execution
UnifiedMemoryManager统一管理Storage/Execution内存
Storage和Execution内存使用是动态调整,可以相互借用
当Storage空闲,Execution可以借用Storage内存使用,可以减少spill等操作,Execution使用的内存不能被Storage驱逐
当Execution空闲,Storage可以借用Execution内存使用,当Execution需要内存时,可以驱逐被Storage借用的内存,直到spark.memory.storageFraction边界
UnifiedMemoryManager统一管理多个并发Task的内存分配
每个Task获取的内存区间为1/(2*N)~1/N,N为当前Executor中正在并发运行的task数量
2.5 Shuffle
spark.shuffle.manager -> sort
trait ShuffleManager{
def registerShuffle...
def getWriter...
def getReader...
def unregisterShuffle...
}
class SortShuffleManager extends ShuffleManager
2.5.1 SortShuffleManager
每个MapTask生成一个Shuffle数据文件和一个index文件
2.5.2 External Shuffle Service
External Shuffle Service + Dynamic Resource Allocation(DRA)
fetch partition-1 from MapTask-1's shuffle dataFile
shuffle write的文件被NodeManager中的Shuffle Service托管,供后续ReduceTask进行shuffle fetch,如果Executor空闲,DRA可以进行回收
03 SparkSQL原理解析
3.1 Catalyst优化器
Catalyst优化器 - RBO
Rule Based Optimizer(RBO)
Batch执行策略: Once -> 只执行一次 FixedPoint -> 重复执行,直到plan不再改变,或者执行达到固定次数(默认100次)
Rule Based Optimizer(RBO)
ConstantFolding / PushDownPredicates / ColumnPruning / DynamicFilterPruning / SimplifyConditionals / ...
object PushDownPredicates extends Rule{
def apply(plan:LogicalPlan):LogicalPlan = plan transformDown{
...
}
}
transformDown:先序遍历树进行规则匹配
transformUp:后序遍历树进行规则匹配
select * from t x join t1 y on x.a=y.c where x.a>1 and y.c = 2
Cost Based Optimizer(CBO)
采集表的statistics
ANALYZE TABLE...COMPUTE STATISTICS...FOR COLUMNS C1,C2
TableStat从ANALTZE TABLE获取
后续的算子的Stat通过对应的Estimation进行估算
打开参数spark.sql.cbo.enabled->true
JoinReorder
JoinSelection
-
Broadcast Hash Join(BHJ)
-
Shuffle Hash Join(SHJ)
-
Sort Merge Join(SMJ)
3.2 Adaptive Query Execution(AQE)
AQE - Coalescing Shuffle Partitions
Partition合并(coalescing shuffle partition)
问题:
spark.sql.shuffle.partition作业粒度参数,一个作业中所有Stage都一样,但是每个Stage实际处理的数据不一样,可能某些Stage的性能比较差
比如:
-
partition参数对某个Stage过大,则可能单个partition的大小比较小,而且Task个数会比较多,shuffle fetch阶段产生大量的小块随机读,影响性能
-
partition参数对某个Stage过小,则可能单个partition的大小比较大,会产生更多的spill或者OOM
作业运行过程中,根据前面运行完的Stage的MapStatus中实际的partition大小信息,可以将多个相邻的较小的partition进行动态合并,由一个Task读取进行处理
spark.sql.adaptive.coalescePartitions.enabled
spark.sql.adaptive.coalescePartitions.initialPartitionNum
spark.sql.adaptive.advisoryPartitionSizeInBytes
...
Switching Join Strategies
SortMergeJoin(SMJ) -> BroadcastHashJoin(BHJ)
问题:
Catalyst Optimizer优化阶段,算子的statistics估算不准确,生成的执行计划并不是最优
AQE运行过程中动态获取准确Join的leftChild/rightChild的实际大小,将SMJ转换为BHJ
Optimizing Skew Joins
AQE根据MapStatus信息自动检测是否有倾斜
将大的partition拆分成多个Task进行Join
spark.sql.adaptive.skewJoin.enable
spark.sql.adaptive.skewJoin.skewedPartitionFactor
spark.sql.adaptive.skewJoin.skewedPartitionThresholdlnBytres
3.3 Runtime Filter
3.4 Bloom Runtime Filter
tpcds/q16.sql:连接
AND cs1.cs_call_center_sk = cc_call_center_sk
3.5 Codegen - Expression
表达式(Expression)
动态生成代码,Janino即时编译执行
WholeStageCodegen
算子/WholeStageCodegen
动态生成代码,Janino即时编译执行
算子/WholeStageCodegen
04 业界挑战与实践
4.1 Shuffle稳定性问题
在大规模作业下,开源ExternalShuffleService(ESS)的实现机制容易带来大量随机读导致的磁盘IOPS瓶颈、Fetch请求积压等问题,进而导致运算过程中经常会出现Stage重算甚至作业失败,继而引起资源使用的恶性循环,严重影响SLA
Shuffle稳定性解决方案
4.2 SQL执行性能问题
压榨CPU资源
CPU流水线/分支预测/乱序执行/SIMD/CPU缓存友好/...
Vectorized / Codegen?
C++ / Java?
4.2 SQL执行性能解决方向
Photon:C++实现的向量化执行引擎
Intel:OAP/gazelle_plugin
4.3 参数推荐/作业诊断
Spark参数很多,资源类/Shuffle/Join/Agg/...调参难度大
参数不合理的作业,对资源利用率/Shuffle稳定性/性能有非常大影响
同时,线上作业失败/运行慢,用户排查难度大
👇
自动参数推荐。作业诊断
05 总结
1、大数据处理常见链路,Spark特点
2、SparkCore中核心概念RDD调度机制,内存管理机制,shuffle机制
3、SQL在Spark引擎中执行的详细流程及优化方案
4、了解目前业界遇到的挑战,解决方案