写 wiki 感觉不太好, 直接写个人博客. 其中掺杂了太多个人理解, 不保证正确性. 但是感觉网上的都是官样文章, 而且都是抄来抄去, 真真叫没意思.
新手村
推荐一个极好的博客 https://github.com/JerryLead/SparkInternals , 最好是一边看能够一遍动手验证, 看看 scala 的函数是怎样转化成不同的 stage 和 task 的, 看看 spark-sql 是怎么划分的, 理解比较深入
相关原理
job 划分和 stage 划分
凡是有 action 的地方会划分为一个 job, 个人理解 action 就是一种 "落地", 之前做的所有计算都只是生成 dag, 都是空中楼阁, 没有真正进行计算, 而 action 会真正得到一个结果, 真正的 scala 的数据结构 array, 可以进行普通的非 spark 的计算
宽依赖和窄依赖
凡是遇到 shuffleDependency 都会划分为两个 stage,shuffleDependency 大白话讲就是要计算下一个 rdd 的任何一个 partition, 都必须把上一个 rdd 的所有 partition 都计算出来, 这个时候把两个 rdd 划分在同一个 stage 是不合适的, 因为必须先把上一个 rdd 完全计算出来以后才能进行下一步的计算, 所以就不必计较分区了.
所谓划分在一个 stage, 就是说下一个 rdd 的分区永远只依赖上一个 rdd 的部分分区, 所以一直往前推, 最后一个 rdd 的分区都只依赖这一个 stage 最早的 rdd 的部分分区, 所以可以按照这个依赖关系, 最后一个 rdd 的每个分区都划分为一个任务 (task), 这个分区的所有计算都在这个 task 内完成, 依赖从后往前推算, 而计算的流程是从前往后, 后一个 rdd 的分区计算完成以后, 前面的依赖分区就可以扔掉了 (当然某些情况下比如笛卡尔积应该还是需要缓存一下?). 这样的话, 就不需要每次计算完一个 rdd 就全部存储起来 (无论是内存还是硬盘), 而是根据数据流, 计算完下一个 rdd 的某个分区, 它依赖的上一个 rdd 的分区就可以不用了,"按需计算"
driver 和 executor
可以认为 driver 只有一个, 但是 executor 有多个, driver 处理 rdd 等, executor 是具体执行的单位, 所以是无法在 map 的函数中执行 spark.sql 的, 因为执行 map 中函数的操作是在 executor 上进行的, 而'解析'rdd 只能在 driver 上做
cache 机制
具体编程
spark 调试
一开始以为 print 操作只会在各个 executor 上执行, 所以并没有打印出来, 其实只是因为 map 是 transformation 操作, 没有调用 action 时是不会执行的, 调用 collect 方法以后会发现 rdd 中的 print 操作都执行了.
rdd.map 和 rdd.foreach 的区别:
(1)map 有返回值, foreach 不会返回任何值 (调用过 foreach 后 rdd 会变为 None)
(2)map 操作不会立即执行, 但 foreach 操作会
(3) 理论上来说 foreach 适用于某种操作, 如调用 restful API 等, map 适用于对数据做映射,
spark 的广播
虽然 rdd 和 map 函数都定义在同一个 python 文件中, 但是实际上 map 函数是在不同的 excutor 上执行的, 可以通过闭包的方式将变量传递给各个 rdd(在 py 文件顶层定义变量, 在 map 的函数中使用), 但这种方式, 每次 executor 遇到不属于 rdd 的变量时都会请求一次 driver, 而广播变量则是一开始申请一次并缓存起来, 后面每次都使用这个缓存.
来源: http://www.bubuko.com/infodetail-3235937.html