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 {
...
}