Spark 不同的 Locality Level
- PROCESS_LOCAL: 数据和 task 在同一个executor jvm 中,最好的就是这种 locality。
- NODE_LOCAL: 数据在同一个节点上。比如数据在同一个节点的另一个 executor上;或在 HDFS 上,恰好有 block 在同一个节点上。速度比 PROCESS_LOCAL 稍慢,因为数据需要在不同进程之间传递或从文件中读取
- NO_PREF: 数据从哪里访问都一样快,不需要位置优先
- RACK_LOCAL: 数据在同一机架的不同节点上。需要通过网络传输数据及文件 IO,比 NODE_LOCAL 慢
ANY: 数据在非同一机架的网络上,速度最慢。
Spark PreferredLocations (的位置优先策略)
数据传输对内网带宽和性能有极大的损耗,所以要千方百计的最大程度的满足 更高级别的本地性,从优到差排, PROCESS_LOCAL > NODE_LOCAL > NO_PREF > RACK_LOCAL。
根据输入数据源的不同,RDD 可能具有不同的优先位置,通过 RDD 的以下方法可以返回指定 partition 的最优先位置:
protected def getPreferredLocations(split: Partition): Seq[String]
返回类型为 Seq[String]
,其实对应的是 Seq[TaskLocation]
,在返回前都会执行 TaskLocation#toString
方法。TaskLocation 是一个 trait,共有以三种实现,分别代表数据存储在不同的位置:
/** * 代表数据存储在 executor 的内存中,也就是这个 partition 被 cache到内存了 */ private [spark] case class ExecutorCacheTaskLocation(override val host: String, executorId: String) extends TaskLocation { override def toString: String = s"${TaskLocation.executorLocationTag}${host}_$executorId" } /** * 代表数据存储在 host 这个节点的磁盘上 */ private [spark] case class HostTaskLocation(override val host: String) extends TaskLocation { override def toString: String = host } /** * 代表数据存储在 hdfs 上 */ private [spark] case class HDFSCacheTaskLocation(override val host: String) extends TaskLocation { override def toString: String = TaskLocation.inMemoryLocationTag + host }
DAGScheduler 生成 taskSet
DAGScheduler 通过调用 submitStage 来提交一个 stage 对应的 tasks,submitStage 会调用submitMissingTasks,submitMissingTasks 会以下代码来确定每个需要计算的 task 的preferredLocations,这里调用到了 RDD#getPreferredLocs,getPreferredLocs返回的 partition 的优先位置,就是这个 partition 对应的 task 的优先位置
val taskIdToLocations = try { stage match { case s: ShuffleMapStage => partitionsToCompute.map { id => (id, getPreferredLocs(stage.rdd, id))}.toMap case s: ResultStage => val job = s.resultOfJob.get partitionsToCompute.map { id => val p = job.partitions(id) (id, getPreferredLocs(stage.rdd, p)) }.toMap } } catch { ... }