1、spark的shuffle过程
- 父RDD中同一分区中的数据按照算子要求重新进入子RDD的不同分区中;
- 中间结果写入磁盘;
- 由子RDD拉取数据,而不是由父RDD推送;
- 默认情况下,shuffle不会改变分区数量。
2、spark的宽窄依赖
- 窄依赖:一个父RDD的分区被子RDD的一个分区使用。1个子RDD的分区对应于1个父RDD的分区,比如map,filter,union等算子。
- 宽依赖:一个父RDD的分区被子RDD的多个分区使用。宽依赖一般是对RDD进行groupByKey,reduceByKey,sortByKey等操作,就是对partition中的数据进行重分区(shuffle)。
3、spark中reduceByKey与groupByKey的区别
- reduceByKey:按照key进行聚合,在shuffle之前有combine(预聚合)操作,返回结果是RDD[k,v];
- groupByKey:按照key进行分组,直接进行shuffle;在实际开发过程中,reduceByKey比groupByKey性能好,更建议使用。
4、Spark提交作业的参数
- executor-cores —— 每个executor使用的内核数,默认为1,官方建议2-5个;
- num-executors —— 启动executors的数量,默认为2;
- executor-memory —— executor内存大小,默认1G;
- driver-cores —— driver使用内核数,默认为1;
- driver-memory —— driver内存大小,默认512M。
5、RDD的常见操作
(1)RDD支持两种操作:转化操作(Transformation算子)、行动操作(action算子)
- Transformantion:Map, Filter, FlatMap, Sample, GroupByKey, ReduceByKey, Union, Join, Cogroup, MapValues, Sort, PartionBy
- Action:Collect, Reduce, Lookup, Save (主要记住,结果不是 RDD 的就是 Action)
(2)区别
- 转化操作:返回一个新的RDD的操作,不会触发作业提交,是延迟计算,如map(),filter()
- 行动操作:向驱动器程序返回结果或把结果写入外部系统的操作,会触发实际计算,如count(),first()
6、RDD的持久化方式
(1)持久化级别:
- memory_only:只放在内存
- memory_only_ser:在内存存放序列化后的数据(减少内存开销,但需反序列化,因此会加大CPU开销)
- memory_and_disk:如果数据在内存中放不下,则溢写到磁盘上
- memory_and_disk_ser:如果数据在内存中放不下,则溢写到磁盘上,在内存中存放序列化后的数据
- disk_only:只放在磁盘
- xxx_2:如果尾部加了2的持久化级别,表示会将持久化数据存为两份,保存到其他节点
(2)memory_only若存储不了:
- 若缓存数据太多,内存放不下,spark会自动利用最近最少使用(LRU)的缓存策略把最老的分区从内存中移除;下一次要用到已经被移除的分区时,这些分区需要重新计算。
(3)memory_and_disk:
- 被移除的分区都会写入磁盘,不必担心作业因为缓存太多数据被打断。
7、Spark作业提交的流程
① spark-submit 提交代码,执行 new SparkContext(),在 SparkContext 里构造 DAGScheduler 和 TaskScheduler。 ② TaskScheduler 会通过后台的一个进程,连接 Master,向 Master 注册 Application。 ③ Master 接收到 Application 请求后,会使用相应的资源调度算法,在 Worker 上为这个 Application 启动多个 Executer。 ④ Executor 启动后,会自己反向注册到 TaskScheduler 中。所有 Executor 都注册到 Driver 上之后,SparkContext 结束初始化,接下来往下执行我们自己的代码。 ⑤ 每执行到一个 Action,就会创建一个 Job。Job 会提交给 DAGScheduler。 ⑥ DAGScheduler 会将 Job划分为多个 stage,然后每个 stage 创建一个 TaskSet。 ⑦ TaskScheduler 会把每一个 TaskSet 里的 Task,提交到 Executor 上执行。
Executor 上有线程池,每接收到一个 Task,就用 TaskRunner 封装,然后从线程池里取出一个线程执行这个 task。(TaskRunner 将我们编写的代码,拷贝,反序列化,执行 Task,每个 Task 执行 RDD 里的一个 partition)
|