一、调度系统概述:增量计算背景、调度核心概念、系统设计目标

1.1 为什么需要增量计算?

先聊聊背景。我入行那会儿,大数据任务调度基本都是「全量计算」——每天凌晨把昨天的数据全部重跑一遍。听起来简单粗暴,对吧?但数据量一旦上去,问题就来了。

举个例子。你有一个用户画像系统,每天处理 10 亿条行为日志。全量跑一次,耗时 6 小时。如果只新增了 500 万条数据,你还得等 6 小时才能看到结果。这合理吗?

说白了,全量计算的核心痛点是:重复劳动太多。每次计算都把历史数据重新算一遍,而大部分数据根本没变化。

增量计算的核心思想:只处理变化的数据,保留历史计算结果,通过「变更 + 合并」的方式产出最新结果。

我在项目中遇到过这样一个场景:某电商平台的实时推荐系统,每天凌晨全量跑一次用户偏好模型,耗时 4 小时。但用户点击行为是实时的,等 4 小时再更新推荐结果,用户早就走了。后来我们改成增量调度——用户每产生一次点击,只更新该用户的偏好向量,整个系统延迟从 4 小时降到了 30 秒以内。

你想想看,增量计算本质上是在「算力」和「时效性」之间找平衡。不是所有场景都需要增量,但一旦数据量超过某个阈值,全量计算就扛不住了。

1.2 调度核心概念

聊调度之前,得先搞清楚几个基础概念。嗯,这里我按自己的理解来梳理一下。

1.2.1 任务(Task)

调度系统的最小执行单元。一个任务可以是一个 SQL 查询、一个 Spark 作业、一个 Python 脚本,甚至是一个 Shell 命令。

在增量场景下,任务通常分为两类:

  • 全量任务:首次运行或数据重建时使用,处理全部数据
  • 增量任务:日常运行,只处理变更数据

1.2.2 依赖(Dependency)

任务之间的上下游关系。比如任务 B 需要等任务 A 完成后才能启动。依赖关系决定了调度的执行顺序。

我记得有一次,团队里有人把依赖配反了——下游任务先跑,上游任务后跑。结果下游任务读到的全是旧数据,整个报表错了一整天。从那以后,我养成了一个习惯:依赖关系必须显式声明,并且要有循环检测。

1.2.3 触发(Trigger)

什么时候启动一个任务?常见触发方式有:

触发类型 说明 适用场景
时间触发 按固定时间间隔或 Cron 表达式触发 定时报表、离线批处理
事件触发 上游任务完成、数据到达、消息通知等 实时流处理、增量计算
手动触发 人工干预启动 调试、数据修复

1.2.4 状态(State)

每个任务在生命周期中会经历多个状态。我习惯把状态机画成一张图,方便团队理解:

任务状态机 PENDING RUNNING SUCCESS FAILED RETRYING SKIPPED 虚线箭头表示失败后自动重试,重试次数耗尽后进入 FAILED 状态

个人经验:状态机设计时,一定要考虑「人工干预」的入口。比如任务卡在 RUNNING 状态超过 30 分钟,应该允许运维手动置为 FAILED 并重新调度。否则一旦状态卡死,整个链路就断了。

1.3 系统设计目标

好,背景和概念都聊完了。那一个增量计算调度系统,到底要设计成什么样?我总结下来,核心目标就四个。

1.3.1 高吞吐与低延迟

增量场景下,任务数量可能比全量多一个数量级。全量一天跑 1000 个任务,增量可能一天跑 10 万个。调度系统必须能扛住这个量级。

  • 高吞吐:单位时间内能调度并执行的任务数
  • 低延迟:从任务触发到实际启动的时间间隔

我曾经遇到过一个调度系统,任务触发后要等 5 秒才能分配到执行器。增量场景下,5 秒的延迟累积起来,整个链路就慢了。后来我们优化了任务队列和调度算法,把延迟降到了 200 毫秒以内。

1.3.2 精确一次语义(Exactly-Once)

增量计算最怕什么?数据重复或数据丢失。调度系统必须保证:

  • 每个任务至少被执行一次(At-Least-Once)
  • 每个任务最多被执行一次(At-Most-Once)
  • 两者结合,就是 Exactly-Once

注意:Exactly-Once 在分布式环境下很难做到精确。实际工程中,我们通常通过「幂等性设计」来近似实现——任务重复执行不会产生副作用。

1.3.3 容错与自愈

分布式系统一定会出故障。调度系统必须能:

  1. 检测故障:任务超时、执行器宕机、网络分区
  2. 自动恢复:失败任务自动重试,或转移到其他执行器
  3. 人工兜底:提供手动重跑、跳过、暂停等运维能力

我记得有一次线上事故,某个执行器节点内存溢出,导致 200 个任务同时失败。幸好调度系统有自动重试机制,任务被重新分配到其他节点,最终全部成功。如果没有容错设计,那晚就得通宵加班了。

1.3.4 可观测性

调度系统不能是个黑盒。你需要知道:

观测维度 关键指标 工具
任务状态 成功率、失败率、重试次数 Prometheus + Grafana
执行延迟 调度延迟、执行耗时、排队时间 分布式追踪(Jaeger)
资源使用 CPU、内存、网络 IO 容器监控(cAdvisor)
数据质量 增量数据量、合并结果校验 自定义校验任务

我的建议:可观测性不是上线后才考虑的。设计阶段就要把 Metrics、Logs、Traces 三件套规划好。否则后期补,成本高得吓人。

1.4 增量调度 vs 全量调度

最后做个对比,帮你快速理解两者的差异:

维度 全量调度 增量调度
数据范围 全部数据 变更数据
执行频率 低(每天/每周) 高(分钟级/秒级)
任务数量 少 多
延迟要求 宽松(小时级) 严格(秒级)
容错难度 低(重跑即可) 高(需保证 Exactly-Once)
资源消耗 高 低

说白了,全量调度是「大力出奇迹」,增量调度是「四两拨千斤」。两者不是替代关系,而是互补关系。实际系统中,通常是全量 + 增量混合使用——首次用全量建立基线,后续用增量持续更新。

嗯,这一章就聊到这儿。核心就三件事:增量计算的背景、调度的核心概念、系统的设计目标。后面我们会一步步深入,把每个模块拆开来讲。