任务调度基础:核心概念与分布式模型
聊到大规模向量检索,很多人第一反应是“索引怎么建”、“向量怎么算距离”。但我在几个生产项目里踩过坑之后,才真正意识到——调度才是那个让系统稳定跑起来的隐形骨架。你想想看,几千个检索请求同时涌进来,谁先处理?哪个Worker干活?任务失败了怎么办?这些事儿,都得靠调度系统来管。
调度核心三要素:队列、Worker、调度器
说白了,一个调度系统就三个东西:队列存任务,Worker干活,调度器做决策。我习惯把它们比作餐厅后厨——队列是待做菜单,Worker是厨师,调度器就是那个喊“三号桌的菜好了”的传菜员。
队列(Queue)
队列不只是个先进先出的列表。在大规模场景下,队列的设计直接影响系统吞吐。我个人常用的队列类型有这几种:
| 队列类型 | 适用场景 | 注意事项 |
|---|---|---|
| 内存队列 | 低延迟、小规模 | 宕机丢数据,需配合持久化 |
| Redis队列 | 中等规模、需要去重 | 内存占用大,注意OOM |
| Kafka/RocketMQ | 大规模、高吞吐 | 引入消息中间件,运维成本高 |
| 数据库表队列 | 需要事务保证 | 性能瓶颈明显,不推荐高频场景 |
Worker(工作节点)
Worker就是实际干活的进程或线程。每个Worker通常维护一个任务循环:从队列拉任务、执行、上报结果、再拉下一个。这里有个关键点——Worker的并发数不是越多越好。
我记得有个项目,团队把Worker线程数设成了CPU核心数的4倍,结果上下文切换开销比实际计算还大,吞吐反而下降了。后来我们压测发现,对于向量检索这种CPU密集型任务,Worker数等于CPU核心数是最优的。如果是I/O密集型(比如从远端拉向量),可以适当多配一些。
调度器(Scheduler)
调度器是大脑。它决定:
- 任务该进哪个队列
- 哪个Worker来执行这个任务
- 任务超时了怎么办
- Worker挂了怎么重新分配
我见过不少团队把调度器做得太重,什么逻辑都往里塞。结果调度器本身成了瓶颈。我的建议是——调度器只做决策,不做执行。决策逻辑要轻量,最好能在毫秒级完成。
分布式调度模型
单机调度好做,分布式才是真挑战。这里我重点讲两种最常见的模型。
Master-Worker模型
这是最经典的分布式调度模型。一个Master节点负责任务分配和Worker管理,多个Worker节点执行任务。结构清晰,容易理解。
这个模型最大的问题是单点故障。Master挂了,整个集群就瘫痪了。我在一个线上系统里遇到过——凌晨三点Master节点OOM,所有任务卡住,检索服务全部超时。后来我们加了etcd做选主,备Master自动顶上,才解决了这个问题。
DAG调度模型
当任务之间有依赖关系时,Master-Worker就不够用了。比如向量检索里,先要建索引、再加载索引、最后才能执行检索——这就是个典型的DAG(有向无环图)。
DAG调度里,每个任务是一个节点,依赖关系是边。调度器按拓扑顺序执行:只有所有前置任务完成,后续任务才能启动。
调度策略:不只是排队
有了队列和Worker,怎么分配任务?这就是调度策略的事儿了。我见过不少团队一开始只用FIFO,后来发现不行——高优先级的检索请求被低优先级的批量任务堵住了。
FIFO(先进先出)
最简单,也最公平。但公平不等于合理。如果有个大任务卡在队头,后面所有小任务都得等着。这就是所谓的队头阻塞。
优先级调度
给任务打上优先级标签。高优先级的任务可以插队。实现方式有两种:
- 多队列:每个优先级一个队列,调度器优先处理高优先级队列
- 优先级队列:用堆(Heap)实现,每次取优先级最高的任务
负载均衡
这是分布式调度里最头疼的问题。Worker的能力不一样(有的机器好,有的差),任务的大小也不一样。怎么分配才能让所有Worker都“吃饱”又不“撑到”?
我常用的策略:
| 策略 | 原理 | 优缺点 |
|---|---|---|
| 轮询(Round Robin) | 依次分配给每个Worker | 简单,但不考虑Worker负载差异 |
| 最少连接(Least Connections) | 分配给当前任务数最少的Worker | 适合任务执行时间相近的场景 |
| 加权分配(Weighted) | 根据Worker性能分配权重 | 需要手动配置权重,不够自适应 |
| 自适应负载均衡 | 实时监控Worker的CPU/内存/队列长度 | 效果好,但实现复杂 |
我个人最推荐自适应负载均衡。虽然实现起来麻烦点,但效果确实好。我记得有个项目,一开始用轮询,结果一台老机器总是超时,拖慢了整个集群。换成自适应后,调度器自动把大任务分配给性能好的Worker,老机器只处理小任务,整体吞吐提升了30%以上。
小结
调度这事儿,看着简单,做起来全是细节。队列选型、Worker管理、调度策略——每个环节都可能成为瓶颈。我见过太多系统,算法模型很牛,但调度做得稀烂,线上跑起来各种问题。所以,别轻视调度,它才是系统稳定性的基石。