Spark 採用兩層調度架構:DAGScheduler 切分 Stage、TaskScheduler 分發 Task。TaskScheduler 內部通過 SchedulingAlgorithm 接口實現 TaskSet/Pool 的優先級排序。本文深度解析 FIFO 与 Fair 兩種算法的比較器邏輯、資源分配模型與調優參數。
private[spark] trait SchedulingAlgorithm {
def comparator(s1: Schedulable, s2: Schedulable): Boolean
}
兩個實現類:FIFOSchedulingAlgorithm、FairSchedulingAlgorithm。
class FIFOSchedulingAlgorithm extends SchedulingAlgorithm {
override def comparator(s1: Schedulable, s2: Schedulable): Boolean = {
val p1 = s1.priority
val p2 = s2.priority
var res = math.signum(p1 - p2) // 優先級數值越小優先級越高
if (res == 0) {
val st1 = s1.stageId
val st2 = s2.stageId
res = math.signum(st1 - st2) // 同優先級按 StageId 先來先服務
}
res < 0
}
}
spark.scheduler.mode=FIFO(默認)。class FairSchedulingAlgorithm extends SchedulingAlgorithm {
override def comparator(s1: Schedulable, s2: Schedulable): Boolean = {
val minShare1 = s1.minShare
val minShare2 = s2.minShare
val running1 = s1.runningTasks
val running2 = s2.runningTasks
val needy1 = running1 < minShare1 // 是否處於「飢餓」狀態
val needy2 = running2 < minShare2
// 1. 飢餓者優先
if (needy1 && !needy2) return true
if (!needy1 && needy2) return false
// 2. 同為飢餓/同為飽滿:比較資源占用比 或 權重比
if (needy1 && needy2) {
// 都飢餓:minShareRatio = runningTasks / minShare,比例小的優先(貪心策略)
val ratio1 = running1.toDouble / math.max(minShare1, 1.0)
val ratio2 = running2.toDouble / math.max(minShare2, 1.0)
return ratio1.compareTo(ratio2) < 0
} else {
// 都飽滿:taskToWeightRatio = runningTasks / weight,比例低的優先(權重大的優先)
val ratio1 = running1.toDouble / s1.weight
val ratio2 = running2.toDouble / s2.weight
val cmp = ratio1.compareTo(ratio2)
if (cmp != 0) return cmp < 0
}
// 3. 完全相同:按名字字典序
s1.name < s2.name
}
}
| 屬性 | 含義 | 配置途徑 |
|---|---|---|
| minShare | 最小資源保障(CPU 核數),低於此值視為飢餓 | fairscheduler.xml → pool.minShare |
| weight | 權重,決定飽滿狀態下的資源分配比例 | fairscheduler.xml → pool.weight |
| schedulingMode | Pool 內部 TaskSet 排序模式:FIFO/FAIR | fairscheduler.xml → pool.schedulingMode |
| 問題 | 原因 | 調優動作 |
|---|---|---|
| 重要 Job 長期等待 | 低優先級 Job 佔用資源(FIFO)或權重過低(Fair) | 提高 priority / weight,設置 minShare 保障底線資源 |
| 小 Job 被大 Job 卡死 | Fair 模式下大 Job 佔滿核心,小 Job 進入飢餓 | 增加小 Job Pool minShare,或啟用動態分配 spark.dynamicAllocation.enabled=true |
| 資源利用率低 | minShare 總和 > 集群總核心,導致部分 Pool 永遠飢餓 | 審計 minShare 總和 ≤ 總核心 × 0.8,預留 20% 彈性 |
| 同 Pool 內 TaskSet 爭搶 | schedulingMode=FIFO 導致後提交 Stage 餓死 | 改為 FAIR,或拆分為子 Pool |