采集任务也分急诊和体检:任务优先级的工程实践
有天早上排名日报迟到了。查原因:一个 10 万关键词的历史补采任务正在跑,把 worker 全占了,日报任务排在后面干等。「所有任务平等」就是问题本身——日报有人盯着等,补采早跑晚跑没人管。这篇记录采集任务怎么做优先级。
第一步:给任务分级
按「谁在等、等多久能接受」分三级:
| 级别 | 任务类型 | 新鲜度要求 | 例子 |
|---|---|---|---|
| P0 急诊 | 告警、日报 | 分钟级 | 排名跌破告警、早报数据 |
| P1 门诊 | 常规刷新 | 小时/天级 | 常规关键词采集 |
| P2 体检 | 补采、归档、验证 | 天/周级 | 历史补采、数据校验 |
分级的依据不是「你觉得哪个重要」,是「谁在等这个数据」。有人等的进 P0,没人等的进 P2。
第二步:选实现方式
方式一:按级别分队列(推荐起步)
不同级别独立队列、独立 worker:
QUEUES = {"p0": Queue(), "p1": Queue(), "p2": Queue()}
# worker 分配:P0 两个、P1 三个、P2 一个
简单、隔离彻底——P2 跑爆不影响 P0。缺点是容量固定,利用率有浪费。
方式二:单队列 + 优先级字段
任务表加 priority,worker 认领时排序:
UPDATE tasks
SET status = 'running', started_at = now()
WHERE id = (
SELECT id FROM tasks
WHERE status = 'pending'
ORDER BY priority DESC, created_at ASC
LIMIT 1
FOR UPDATE SKIP LOCKED
)
RETURNING *;
priority DESC 保证 P0 先出,created_at ASC 同级先来先服务,SKIP LOCKED 让多 worker 并发认领不打架(配合幂等篇的状态机用)。
方式三:容量保底 + 加权(更大规模)
P0 永远保底 30% 并发,其余级别分剩余容量。复杂度上来了,一般到方式二就够用。
关键问题:防饥饿
纯按优先级排,P2 可能永远排不上——P0/P1 源源不断。解法是老化(aging):等待越久,优先级越高。
ORDER BY
priority + LEAST(EXTRACT(EPOCH FROM now() - created_at) / 3600, 5) DESC,
created_at ASC
每小时给任务加 1 点优先级,最多加 5——补采等久了会插队,但封顶,翻不了天。再配上「P2 保留一个 worker」的保底,基本不会饿死。
第三步:和限流、成本配合
- 优先级决定先花谁的额度:P0 先消耗限流配额,P1/P2 用剩下的
- 成本按级分开统计:P0 花了多少、P2 花了多少
- P2 单独设预算上限,别让补采悄悄吃光预算
成本分级的原料是每笔请求的计费记录——接口响应的 credits_charged 字段正好干这个,SerpBase 官方文档 的计费说明里写得很清楚。
踩坑记录
坑 1:所有任务都是 P0。 谁都觉得自己重要,优先级直接失效。定死策略:新任务默认 P2,升 P0 要写「谁在等、等多久」。
坑 2:忘了防饥饿。 补采任务排了一周没跑,最后还是业务来催。老化机制必加。
坑 3:只在提交端标了优先级,认领端没排序。 worker 还是随机取任务。认领 SQL 必须 ORDER BY priority。
坑 4:没有分级指标。 出事不知道是哪个级别堵了。按级别记录队列深度、等待时长。
工程清单
- 按「谁在等 + 等多久」分级
- 分队列或优先级字段 + 认领排序
- 老化防饥饿(带上限)
- 优先级决定限流额度和预算分配
- 按级别看队列指标;新任务默认最低级
优先级调度的本质,是把「什么先做」从「谁先提交谁先跑」换成「谁在等谁先跑」。急诊先看、体检排队、等久了升号——这套挂号逻辑,采集系统一样适用。
任务状态机加优先级排序的完整认领逻辑,可以对照接口每次请求带 request_id 的设计来做审计,字段说明见 SerpBase 官方文档。你们的采集任务分级了吗?评论区聊聊事故现场。

浙公网安备 33010602011771号