导读:① 起点案例 · ② 全链路五段拆解(含机制+成因,最详细)· ③ 拐点实证 · ④ 慢路损失多大 · ⑤ 总根因(僵尸 tier-1)· ⑥ 提升点 · ⑦ 数据快照(含 cron 3→2 复核)· ⑧ 队列三问(Leg B 入队 / BC 极限 / 快车道 100 条)· ⑨ 2026-06-16 B/C 落地成效 + 真尾巴 + pri 体系(最新)
输入是一条抖音视频链接 douyin.com/video/7648553920499141739。反查得到:
| 项 | 值 |
|---|---|
| 账号 | 歆笛的装修日记(知识号) |
| 分层 | Tier-1 优质号 |
| 累计评论 / 高意向 | 184 / 15 (整号 29 条视频累计;非单条视频) |
| 近 7 天高意向 | 11 条(全压在近 7 天,是个正在爆量的号) |
| 高意向触达状态 | 15 条里 13 条还没触达(analyzed) |
逐条看这 15 条的「发言→抓到」延迟,发现一个反常:同样是 Tier-1,有的 1 分钟抓到,有的 29–48 分钟,还有的拖到 几天。这就是整条调查的起点。
| # | 用户 | 发言 | 抓到 | 延迟 | 评论摘要(意向分) |
|---|---|---|---|---|---|
| 1 | 大海 | 05-19 07:32 | 05-21 13:38 | 2.3 天 | 帮忙发一份吧 (85) |
| 2 | @~#~? | 05-19 09:39 | 05-21 13:38 | 2.2 天 | 要一份 (85) |
| 3 | 小肥猫 | 05-19 09:47 | 05-21 13:38 | 2.2 天 | 我要一份 (85) |
| 4 | 风少 | 05-20 20:50 | 05-21 13:38 | 16.8 时 | 100平15万预算求方案 (93) |
| 5 | ,,, | 06-09 22:32 | 06-14 07:49 | 4.4 天 | 下个月准备装修,真的需要 (85) |
| 6 | 🍒奶黄包 | 06-12 00:29 | 06-14 01:24 | 2.0 天 | 我也需要,准备装修 (85) |
| 7 | 英儿 | 06-12 08:33 | 06-14 01:24 | 40.9 时 | 来一套资料 (85) |
| 8 | 安安 | 06-12 11:35 | 06-14 01:24 | 37.8 时 | 258平大概多少钱 (93) |
| 9 | 燕飞狐 | 06-13 11:05 | 06-14 04:37 | 17.5 时 | 最近准备装修,求份资料 (85) |
| 10 | 双子娃娃 | 06-13 23:52 | 06-14 01:24 | 1.5 时 | 71平学区房,求资料 (93) |
| 11 | 龍凤呈祥 | 06-14 08:08 | 06-14 08:37 | 29 分 | 一百平小三室,求资料 (100) |
| 12 | 用户5080 | 06-14 09:44 | 06-14 10:14 | 30 分 | 下月要装修了 (85) |
| 13 | 古悠然 | 06-14 15:49 | 06-14 15:51 | 1 分 | 准备装修,求全套资料 (85) |
| 14 | 信仰 已触达 | 06-14 16:49 | 06-14 16:57 | 7 分 | 多少平米的定制 (85) |
| 15 | 二宝妈 已触达 | 06-15 00:35 | 06-15 00:37 | 2 分 | 求资料,毛坯房 (85) |
汇总:中位 17.5 时 · 最快 1 分 · 最慢 4.4 天。趋势——越往后(6-14 下午起)越快;5 月那几条 2 天级、6-14 凌晨 01:24 那批是「断档恢复一次性补抓」的积压。这个「越来越快」的拐点,③ 坐实。
一条评论 T0 用户发出 → 进我们库,走完五道关。先看每段实测耗时,再把最关键的 B、C 两段彻底讲透(抓取机制、冷视频成因都在这一节,不用往后翻):
用户点「发送」后,这条评论要先在抖音自己的系统里能被 API 查到。我们管不着,但很快——用最快的端到端样本(1 分钟)倒推,这段只占几秒。
"抓这条视频" = worker(干抓取活的程序)去这条视频底下把评论捞一遍。它什么时候被抓到,完全由我们的抓取机制决定 —— 有两条路,下面讲全。
| 路 | 怎么触发 | 覆盖谁 | 节奏 |
|---|---|---|---|
| 🐢 整号慢路 | 这条视频所在的「号」被整体抓一遍时,顺带把它主页前 20 条视频都抓了 | 所有号(按 tier 分快慢) | tier-1 号 ≥30 分 / tier-0 号 ≥7 时 |
| ⚡ 视频快车道 | 不看号,直接挑「正在冒新评论的 top 100 视频」单独抓 | 够格的热视频 | 每 2 分钟挑一批 |
号分两层,走两条 cron(cron = 定时任务,到点自动触发的"闹钟")。最容易被绕晕的是「闹钟频率 ≠ 每个号实际多久被抓一遍」:
| tier-0 普通号 | tier-1 优质号 | |
|---|---|---|
| 闹钟多久响(cron) | 每 1 小时(原 4h) | 每 2 分钟(原 5min→3min→今天 2min) |
| 每次派多少 | 挑 150 个最久没抓的号 | 所有 tier-1(受下面节流) |
| 同一个号多久能再抓 | 无硬闸,靠 150 名额轮 | ≥30 分钟(节流闸,今天从 45 改 30) |
| 队列优先级 | pri=5 慢道 | pri=1 快道 |
| 每个号实际间隔 | ≥7 小时(1019 个号挤) | ≥30 分钟(且超产能更长) |
由同事 tuan19960216 在 2026-06-14 11:03 上线(PR #340)。藏在 tier-1 的闹钟里,每 2 分钟独立跑一轮(下文把「闹钟响一次 / 跑一轮」叫「1 个 tick」,现 cron */3 = 每 3 分钟一个 tick):
pri=1),不分 tier、不受 30 分钟号节流约束;近 2.5 天 274 条走慢路(>30 分)的高/中意向评论,按「评论来时它那条视频前 24h 有没有上过快车道」分类:
| 成因 | 条数 | 占比 |
|---|---|---|
| 🥶 冷视频鸡生蛋(从没上过快车道) | 265 | 97% |
| 📉 掉车(上过又没赶上) | 9 | 3% |
| 其中深夜掉车 | 1 | — |
就算 Leg B 把活派进了队列,worker 也不是立刻抓。实测中位 276 秒、p99 327 秒。机制如下:
pri=1:tier-1 整号 + 视频快车道)和慢道(pri=5:tier-0 整号 + lead 资料)。pri=1 永远插在 pri=5 前面。/queue/stats 报 pending=2 是假象(只数最近 1000 条窗口),真实 pending=640(pri1 103 / pri5 537);8 槽真忙(claim 78/分、running 常态 8 个无僵尸)。Leg C 的真因 = pri=1 队列常年积压 ~100 条,新活按"同级先到先得"排在后面——实测一个 23:06:53 的 tick 灌的批次,全等到 ~23:11:35 才被领走(285 秒)。排不空因为:① 快车道昼夜不停每 2 分钟灌一批(深夜 313s ≈ 午间 276s,无低峰);② 慢整号活(2+ 分钟)混在同条 pri=1 道占槽;③ 还有 poison 活(一条 38 小时前的 pri1 整号活重试到顶卡死、永远 pending)。轮到后,worker 调抖音评论 API:抖音新评论排最前,增量抓只取首页、整页无新即早停,通常 1 个请求 ~5 秒搞定;写库亚秒。所以抓取本身不慢,慢的全在 B、C 两段等待。
B-3 说快车道 6-14 11:03 上线。歆笛这条视频的延迟正好在那之后断崖式变好——这就是 B 两条路切换的铁证:
| 发言 | 抓到 | 等待 | 走哪条路 | 谁 |
|---|---|---|---|---|
| 06-14 08:08 | 08:37 | 29 分 | 🐢 整号慢路 | 龍凤呈祥 ⭐ |
| 06-14 09:44 | 10:14 | 30 分 | 🐢 整号慢路 | 用户508 ⭐ |
| ───── 11:07 这视频首次挤上快车道 ↓ ───── | ||||
| 06-14 15:49 | 15:51 | 1 分 | ⚡ 快车道 | 古悠然 ⭐ |
| 06-14 16:49 | 16:57 | 7 分 | ⚡ 快车道 | 信仰 ⭐ |
| 06-15 00:35 | 00:37 | 2 分 | ⚡ 快车道 | 二宝妈 ⭐ |
| 06-14 23:29 | 06-15 14:25 | 14.9 时 | 🐢 掉回慢路 | 🐟ᐝ |
全库高/中意向评论按延迟分桶(口径分三层,避免被历史积压误导):
| 口径 | 样本 | 中位延迟 | ≤5 分 | >30 分(走慢路) |
|---|---|---|---|---|
| 全部历史 | 13,842 | 2344 分 | 4.5% | 86% (陈年积压污染,忽略) |
| 近 7 天 | 2,830 | 261 分 | 13.7% | 70% (含 6-14 断档尾巴) |
| 近 2 天(真常态) | 444 | 10 分 | 41% | 37% |
顺着 B-2/B-4 往上追:冷视频之所以这么多,是因为大量 tier-0 号被饿死、长期不被抓。而 tier-0 被饿死,又是因为 tier-1 太多——
auto-tier1 每天把「近 3 天高意向 ≥5」的活跃号升 tier-1,但系统没有任何降级逻辑——只升不降。于是 tier-1 越攒越多:
| 活跃 | 不活跃 | |
|---|---|---|
| tier-1 优质号 | 176 | 10 |
| tier-0 普通号 | 1019 | 1828 |
| 近 7 天高意向 | 个数 | |
|---|---|---|
| 🟢 仍达标(≥5) | 46 | |
| 🟡 不够格(1–4) | 68 | |
| 🔴 挂零(0) | 62 |
| 原因(为什么要做这刀) | 现有内容(做了什么 · 状态) |
|---|---|
| P0-a 断总根因:tier-1 只升不降攒到 176、74% 僵尸,挤爆抓取产能、把 1019 个 tier-0 饿死 → 冷视频多、慢尾多。 | ✅ 已落地:6-16 手动砍 27 个真死号(高+中=0)176→149;#365 对称 auto-demote(自动降级、断只升不降)已合并部署。下一步可按 mid lead 价值再砍到 ~46。单这一刀解大半。 |
| P0-b 治 Leg B 冷视频:97% 慢路是「冷视频鸡生蛋」——冷视频赶不上 60min 新鲜门槛、永远进不了快车道(冷视频定义见 B-4)。 | ✅ 已落地:#363 破鸡生蛋——任一视频近 6h 一冒高/中意向评论,立刻临时拉进快车道(pri=0 插最前),不等它够 60min 门槛。(注入空扫待补「去重」,见 ⑨-D) |
P0-c 治 Leg C 排队:慢整号活(23–211s)和快活(5s)挤同一条 pri=1 道 → 排队 4.6 分;且快车道空跑洪水(多数复扫 found=0)白占位。 |
✅ 已落地:#363 快慢分道把整号活降 pri=3 让快活先走(尾延迟 p90 −61%/p99 −75%);#366 空跑退避(连 3 次 0 新 → 9→18→30 分钟退避)。直接压那 4.6 分钟。 |
| P0-d 清 poison + pri=1 减负:38h 前重试到顶仍卡 pending 的僵尸活白占;慢整号活也还堵在 pri=1 道。 | ✅ 已落地:6-16 已删那个 59h poison 墓碑活;并随 P0-c 把慢整号活挪出 pri=1。(「自动清 poison」还可补成常态 job) |
| P1 怀疑「并发不够、要扩机」。 | 有效并发实测 ~8–11 槽(非瓶颈),无需盲目扩机;真瓶颈是 pri=1 队列结构(已由 P0-c/d 治)。 |
| 不要做 想靠「提 cron 频率」提速(闹钟 2min→1min)。 | 别做——会让快道空跑翻倍、Leg C 更糟。(#346 已验证 cron 不是瓶颈;甚至 */2→*/3 回退反而更好,见 ⑦) |
理论地板:治好 B、C 两段后,整条链可压到 10–30 秒。今天那几条 1 分钟抓到的,就是低峰不拥堵时跑出来的——目标是把「1 分钟内」从偶发变常态。
今日改动(PR #346,已上线):scrape-hot 闹钟 */3 → */2 + 源节流 45 → 30 分。但实测发现 cron 不是瓶颈(真瓶颈是 Leg B/C),所以原计划第二步 */1 已取消。
scrape_video 快活 88,455 条,其中 77% 复扫 0 新评论(空跑);快车道每 2 分钟灌 ~67 条(dedup 后)。*/3→*/2 把这股空跑洪水抬高了 50%,而它灌的全是 pri=1 活——正是堵 Leg C 的那条队。所以:改回 */3 反而少 33% 空跑、减轻 pri=1 积压,只换来热视频复扫间隔从 ~2 分变 ~3 分(次要)。真正起作用的是同批的源节流 45→30(让 tier-1 更早可重抓),不是 cron tick。结论:tick 是二阶旋钮,回 */3 没坏处;真问题仍是 P0-a 降级 + P0-c 队列双层优先级。cron tick=2分 ✓Leg C 中位 276s / p99 327sLeg D 中位 4.9s快车道命中率 18%worker 实测有效并发 ~11 · 总吞吐 ~3000 活/htier-1 149(6-16降27真死号)tier-0 1046慢路 37%(近2天)冷视频 97%
fly status 实查坐实,不是推测)——akke-worker-prod 现 2 台 started、都在东京(nrt),auto_stop=off 常驻不缩,不会因为 pri=1 洪水自动多起第三台(Fly 没有"按负载自动扩容"这回事;早先写「瞬时 >2 台 / deploy token 读不到机器数」是错的,现已实查更正)。要加机器只能手动 fly scale count=N。② "槽"分两层,别混(这就是之前 8 / 11 打架的根源):工位(job slot)= 2 台 × WORKER_CONCURRENCY=3 = 6 个,是"同一时刻最多领几个活"的硬上限;但一个"整号活"内部还能同时拉 2 条视频(MAX_VIDEOS_PER_SOURCE_CONCURRENT=2)→ 真正"同时在飞的抓取请求"能到 ~6–12 条。Little's law 反推的 ~11,量的是后者(在飞的请求数),不是工位数;running 行 14 里还含 ~3 个 zombie 虚高。总吞吐 ~3000 活/h。③ 已执行 P0-a 第一刀:把「近7天 高+中意向产出=0」的 27 个真死号降回 tier-0,active tier-1 176→149(回滚清单存盘)。scrape_jobs 表抢活(调 claim_pending_scrape_jobs_batch,靠 Postgres FOR UPDATE SKIP LOCKED)。这把锁保证同一条活绝不会被两台同时领——谁手头空了谁去抢下一条,天然就负载均衡,没有主从、不用分工、不会撞车。所以"两台如何配合"的答案是:它们根本不需要互相知道对方存在。fly scale count=2→3 能线性加工位(每台 +3),但 ⑥-P1 已实测并发不是瓶颈——这 6 个工位有近 9 成时间在空跑(复扫 0 条新评论)。盲目加机器只会让"空跑"跑得更快,治不了 Leg B/C 的结构病。纵向把单台 WORKER_CONCURRENCY 3→5 也不行,会撞 shared-cpu-1x 的 CPU 墙(代码注释明写 5+ hits CPU wall)。Leg B 的"入队" = 把这条视频 / 号写成一行 scrape_jobs(Postgres 表,不是 Redis / 内存队列)。两类行:
| job_type | 是什么活 | priority |
|---|---|---|
scrape_source | 整号活:去主页捞 ≤20 条视频逐条抓 | tier-0 = 5 / tier-1 = 1 |
scrape_video | 单视频快活:快车道增量复扫 | 1 |
scrape_jobs 表。 worker 的 8 个 slot 各自轮询,调 claim_pending_scrape_jobs_batch RPC(Postgres SKIP LOCKED + ORDER BY priority ASC)领活。Leg B = 等 cron 把活写进表("何时轮到抓它");Leg C = 活已在表里、按 priority 排队等某个 slot 领走。| 段 | 现状 | 极限地板 | 怎么做 |
|---|---|---|---|
| Leg B | 冷视频几十分~几小时 | ≤2 分(常态 1–2 分) | P0-b 破鸡生蛋:任一视频一冒高意向评论立刻强行注入快车道,不等它自己够 60min 门槛 → 压到 ≤1 个 tick(2 分) |
| Leg C | ≈ 4.6 分 | 亚秒~数秒 | P0-c 双层优先级(带新评论的活提 pri=0 插队、慢整号活移出 pri=1)+ P0-d 清 poison → 活到达空队列 + 有空 slot 立即领 |
| B+C 合计 | — | ≈ 10–30 秒 | = ⑥ 末尾「理论地板」。今天偶发「1 分钟抓到」就是低峰不拥堵时已摸到这个地板 |
| GOOD | BAD / 风险 |
|---|---|
| ① 高意向触达从分钟~小时 → 1–2 分,抢客户热度窗、转化↑ ② tier-0 解放、冷视频减少、漏抓减少 ③ 队列不积压、poison 自清、系统更稳 | ① P0-b 强注入若不 capped 会重蹈 6-13「洪水偷产能饿死 tier-0」——必须配 P0-a 先释放产能 + 集合 capped ② 提扫描频率 / req-min 触发反爬、伤号(硬上限) ③ P0-a 降级可能误杀暂时低产的优质号(需回升机制) ④ 工程复杂度↑、更多队列状态要监控 |
journeys-fanny#pt)的「技术机制」区。⑥ 列的 P0 提升点今天全部落地(多个 PR 已合并部署)。但实测下来(全天 24h 复核):Leg C 把典型延迟砍了 2/3、但极端尾巴被注入洪水拖坏;Leg B 喜忧参半;而"评论拉取还是很久"的真尾巴另有其因——不是排队、是老视频覆盖盲区。逐项说清。
⑨ 段提到的每个 #编号 改动,先在这张表里一句话说清「它是什么 / 治什么病 / 现在什么状态」,下面 B、C 再展开。比喻统一用「果园摘果」:视频 = 果树,评论 = 果子,高意向评论 = 熟透值钱的果(lead),抓评论 = 派人去摘。
| 改动 | 一句话(果园比喻) | 治什么病 | 状态 |
|---|---|---|---|
| #363 · Leg B 破鸡生蛋 | 看到偏远老树刚冒熟果,破例先抱上"重点照看名单"、还排最前,不等它够格 | 冷视频上的高意向评论,第一条几乎必慢 | 已上线 6-16 |
| #363 · Leg C 快慢分道 | "摘单棵树"的快活和"巡整片园子"的慢活分开排队,让快的先走 | 慢整号活堵在快活前面,快活干等 | 已上线 6-16 |
| #366 · 空跑退避 | 一棵树连去 3 次都空手就少去(间隔 9→18→30 分钟),一冒新果立刻恢复勤去 | 视频被反复复扫却抓 0 新、白占工位 | 已上线 6-16 |
| #365 · tier-1 自动降级 | 不再活跃的优质号自动降回普通档(对称于自动升档) | tier-1 只升不降、撑爆产能、饿死 tier-0 | 已上线 6-16 |
| #377 · cold-intent 回扫 | 近 14 天还在冒高意向的老视频,每 20 分钟低频回扫一遍 | 常青老视频上的新高意向,没人定期扫(老视频盲区) | 已上线 6-16 |
| #381 · 注入去重 | 破鸡生蛋的注入活也纳入退避(去掉原本豁免),别每 tick 重派同一棵树 | pri=0 注入洪水:同一视频 80 次/6h、100% 抓 0 新、拖坏 p99 | 已上线 6-17 |
| #413 · 早停修复 | 增量抓评论别"一页全老就停",要连续 N 页全老才停(抖音按热度排、新评论埋后面) | 快道判"空"里 33% 是假空、漏了真评论(含 lead) | 已部署 6-18 · 当晚预览假空 33%→8% |
1)「Leg」是什么?=一条评论从"被发出"到"我们抓进库",中间分好几段路(② 段把它拆成五段)。本段只关心其中相邻两段:
· Leg B = 「派活」那段:评论已经存在了,但系统还没派出一个活去抓它("菜做好了,没人来端")。
· Leg C = 「排队执行」那段:活已经派进队列了,等 worker 领走真正去抓("菜端到打饭窗口,等阿姨打饭")。
2)「地基①②」不等于「Leg B / C」。地基是底盘(系统本来就这么转);Leg B / C 是在底盘上做的改动招式。下面先讲底盘,再讲招式。
以下「地基」是纯现状(系统现在怎么转,不含本轮改动);改动一律见上方 A0 一览表。
| 活的类型 | 抓什么 | 快慢 |
|---|---|---|
单视频快活scrape_video | 只抓一条具体视频(快车道按规则点名的那条,job 里带它的 url——通常是「正在冒新评论的热视频」或「近 6h 冒过高意向的视频」)底下的评论,且是增量(只取比上次新的) | 快 · ~5 秒/条 |
慢整号活scrape_source | 不点名具体视频,给的是一个号:去它主页捞最近 ≤20 条视频,再逐条把每条的评论都抓一遍(抓哪几条由号当下决定) | 慢 · 中位 23 秒、最慢 211 秒 |
| 高意向注入活 | 本质也是单视频快活,但因「这视频近期冒过高意向」被特批插队(近期 = 近 6 小时,为什么是 6h 见 B 段;机制见 B) | 快 · ~5 秒/条 |
aweme_id 点名抓那一条。谁来点名 = 快车道 / 高意向注入 / UI「抓这条」,它们把一个 aweme_id 写成一行 scrape_video 活。所以"单视频快活"永远只盯一条视频,不碰这个号底下别的视频。auto-tier1 cron 每天把"近 3 天高意向 ≥5 条"的活跃号自动升上来(对称的自动降级见下方 ⑤/⑦ #365);tier-0 是默认档、绝大多数号都在这。每个活贴一张「号码牌」priority,worker 严格从小号往大号领,同号牌内先到先得。越急、越快、越该趁鲜的,贴越小号:
| 号码牌 | 谁贴这号(入队来源) | 含义 |
|---|---|---|
0 | 高意向注入 · vip-rescan · UI「抓这条」(运营在后台仪表盘上手动点「立即抓取这条」的按钮) | 最优先,最该趁鲜抢 |
1 | 常规单视频快活(fresh-catch 快车道) | 快,先放走 |
3 | tier-1 慢整号活 · rescan-recent | 慢,让到快活后面 |
5 | tier-0 每小时轮抓 · lead 资料(给某个潜在客户建档时顺带触发的抓取,scrape_lead_profile,没指定 pri 就吃默认 5) | 最次,量大不急 |
pri=0 高意向注入:插最前,空队列时秒级 ~ 1 个 tick(2–3 分)就被领走。pri=1 常规快车道单视频:执行~5 秒/条;排队中位 0、尾巴(p90) ~2 分。pri=3 tier-1 慢整号:执行中位 23 秒、最慢 211 秒/个;让到快活后面,实测 pending 最老 ~9 分。pri=5 tier-0 每小时 + lead 资料:只在上面三档全空时才轮到 → 实测能积压到 12 小时没人抓(就是 ⑤ 总根因里被饿死那批)。scrape_lead_profile:识别出一个潜在客户后,系统顺手排一条活去抓这个用户的主页/视频补全画像,它没单独设优先级,就吃了默认 pri=5。)| 号码牌 | 6-16 改动前 | 现在(#363 后) |
|---|---|---|
0 | vip-rescan、UI 手动 | + 高意向注入 ← 新增 |
1 | 常规快活 + tier-1 整号活(挤同一道) | 只剩常规快活 |
3 | 基本空着 | tier-1 整号活(从 1 降来)、rescan-recent |
5 | tier-0 轮抓、lead 资料 | 不变 |
就两处变化:① tier-1 整号活:pri 1 → 3(原来和快活挤 pri=1 同一道、堵快活,这就是 Leg C 快慢分道);② 新增 pri=0 高意向注入(原来没有,这就是 Leg B 破鸡生蛋)。其余不变。
↑ worker 永远先把 pri=0 领光、再 1、再 3,上面三档全空了才轮到 pri=5 → tier-0 被饿死(⑤ 总根因的机制)。
DEFAULT 5),要改得改代码走 PR。worker(~30s/轮)调认领 RPC,核心 = WHERE pending AND scheduled_at≤now AND attempts<3 ORDER BY priority ASC, scheduled_at ASC FOR UPDATE SKIP LOCKED(严格 pri 小→大、同级先到先得、并发安全)。领到即开抓;卡死(>8min)被扫回重排;attempts 到 3 被排除=永久 pending 墓碑(P0-d 清的就是它)。先回顾:Leg B = 「派活」那段路 —— 评论已经存在了,但系统迟迟没派出一个活去抓它。本段治的就是冷视频上这个"迟迟没人派活"。
【问题】一条视频要先进「快车道」才会被频繁单独复扫;而进快车道的门槛是——它的评论得在 60 分钟内被我们新鲜抓到过一次。可冷视频(老视频 / 不活跃号底下的)只能等慢整号活几十分钟~几小时扫一遍,永远赶不上 60 分钟 → 永远进不了快车道 → 它上面新冒的高意向评论第一条几乎必慢。死循环:要被快抓得先进快车道,要进快车道得先被快抓(「鸡生蛋」)。
【改动】不再死等门槛。只要一条视频近 6 小时内冒过高/中意向评论,就直接把它塞进快车道、还插在最前面(pri=0)优先抓——「破鸡生蛋」= 直接抱它上车,不等它自己够 60 分钟的格。
6h 的由来:注入要圈"刚冒过高/中意向的视频",6h 是个"近期"窗口——够宽,半夜冒的意向到白天还能补上。
实测发现它的毛病是"太长":高意向视频天生是老·常青视频(实查"曾产高意向、还活着"的视频全部 >4 天龄)。一条视频 6h 内只要有过一条高意向评论,就被每个 tick 重选注入一次 → 6h ≈ 120–180 个 tick → 同一条被反复抓 80 次、100% 抓 0 新。
但核心病不在 6h 长短,在"每个 tick 都重注一遍"。缩到 2h 只是按比例砍空跑(治标);根治靠"注入去重"(同一视频注入过就别每 tick 重派)——这已由 #381 落地(A0 清单里那条)。所以 6h 维持不动。
empty_streak):连抓 3 次都 0 新才开始拉长间隔(9→18→30 分钟),一冒新评论立刻清零、恢复每 tick 勤抓。所以真在不断冒评论的活跃视频永远不空手、永不退避、频率不降;被降频的只有"冒过一波后安静下来"的(实测那反复 80 次正是这批,100% 空手)——对它们降频一条 lead 不少抓、只省白跑。后续旋钮:可给高意向视频设更短的退避上限(如封顶 10 分钟)。src/app/api/cron/scrape-hot/route.ts → runVideoFastLane。WINDOW_H=48h 内、被 ≤FRESH_CATCH_MIN=60min 新鲜抓到的评论数」top MAX=100,全 pri=1(即 B-3 那套)。HIGH_INTENT_HOURS=6h 内 intent_label∈{高,中} 的评论」对应视频,免 60min 门槛并入、priority=0、截断时置顶不被截。开关 HOT_FAST_LANE_HIGH_INTENT_HOURS=6(=0 关)。先回顾:Leg C = 「排队执行」那段路 —— 活已经派进队列了,等 worker 领走真正去抓。本段治的就是这个排队:别让慢活堵快活、别反复空扫白占位。
【问题①·排队】活派进队列后 worker 一个个领。原来慢整号活(23~211 秒)和单视频快活(5 秒)挤在同一条号牌道(都 pri=1)——前面卡一个慢整号,后面快活全得干等,实测排队中位 ~4.6 分钟。
【改动① · #363 快慢分道】把慢整号活降一档(pri 1→3),让 5 秒的快活先走、别被慢活堵。实测典型延迟 median −67%(见 D);但极端尾巴 p99 反被注入洪水拖坏 → 已由 #381「注入去重」补上(见下方改动③)。
【问题②·空扫】很多视频被反复复扫却抓不到新评论(实测近 24h 复扫 88,455 条、77% 抓 0 新),白占 pri=1 的位置、加重排队。
【改动② · #366 空跑退避】一条视频连抓 3 次都 0 条新评论,就把它下次复扫往后推(9→18→30 分钟封顶);一旦又冒新评论立刻清零、恢复勤抓。
empty_streak=连续抓 0 新几次、next_fast_lane_at=推到几点才允许再进快车道。9×2ⁿ:9→18→30,第三步本该 36 被 30 封顶)。FAST_LANE_BACKOFF_BASE_MIN=9 / _CAP_MIN=30,嫌慢调小、嫌费调大)。runTier1SourceEnqueue):tier-1 整号活 priority 1→3;同批 vercel.json cron */2→*/3(⑦ 已验 cron 非瓶颈、回 */3 反少 33% 空跑)。20260616114802 给 videos 加 empty_streak/next_fast_lane_at+partial index;worker _run_scrape_video 仅对 hot_fast_lane && !high_intent 的活按 new_comments_inserted 记账、退避间隔 min(30, 9×2^(streak−3));选视频端 runVideoFastLane 在截断前过滤 next_fast_lane_at>now(pri=0 注入原不退避;2026-06-17 #381 已去掉此豁免、注入也退避,见改动③)。开关 HOT_FAST_LANE_BACKOFF / FAST_LANE_BACKOFF_START_STREAK=3 / _BASE_MIN=9 / _CAP_MIN=30。【为什么要补】#366 退避当初故意豁免 pri=0 注入(怕压住破鸡生蛋该抢的活)。全天复核坐实这豁免错了——注入洪水(80 次/6h、0 新、把 p99 拖到 80 分钟)的实测拆解见上方 D 段,此处不重复。结论:豁免要去掉。
【改动】去掉那条豁免,让高意向注入跟普通快活一样退避——注意是「自适应」,不是粗暴的「摘过一次就再也不去」。
videos.empty_streak),任何一条路摘到这树的新果都清零 → 真在不断结果的好树永不空手、永不退避、频率一点不降;被拉长的只有"冒过一波后安静下来"的树(注入空跑的正是这批)。所以它不会把"我们关注的活跃视频"抓慢。scrape_jobs / videos)下表时间都是 2026-06-17 当天;Z = UTC(0 时区),+8 小时 = 北京时间,已在每行标出北京时间。
.order 重测后修正为下面的渐进衰减——这条曲线才和"连 3 次空扫才起退避≈9 分钟"的机制自洽。)| 时刻 | pri=0 注入创建率(每 10min) | worker 运行槽(瞬时) | tier-0(pri=5)积压 |
|---|---|---|---|
| 部署前 · 11:43 (03:43Z) | ~180(≈18/min 持续灌,对得上全天 26995) | pri=0 霸占 6/11 槽 | 353 个 · 最老 13.0h |
| +12min · 12:00 (04:00Z) | 65(−64%) | — | 352 · 12.8h |
| +22min · 12:10 (04:10Z) | ~22(−87%,仍在降) | pri=0 = 0;pri=5 拿 10/16 槽(62%) | 350 · 12.7h |
empty_streak=3/4 那两档在涨)。起因:运营反馈中高 leads 变少。循线去查——"快道判 0 新的视频,后来全量轮有没有补到、抓那一刻就已经存在的评论"。
结果(近 12h 抽 120 个判空视频):
· 67% 是真空:确实没新评论可抓(结论不变)。
· 33% 是假空:抓时评论已存在、却被快道漏掉,晚 50~553 分钟才由最慢的全量轮补到,漏的里含中意向 lead。
根因:快道增量抓取「整页全老就停 + 只看前 50 条」,假设抖音按时间倒序返回;但抖音 web 评论接口默认按热度排,新评论被埋在后面够不到。
要更正前文一处口径:退避省的是浪费没错,但"0 新 = 纯空跑、不丢 lead"只对那 67%;剩下 33% 的真评论被推给最慢的全量轮 —— 这才是"中高 leads 变少/变慢"的直接根因。
退避只是治标,这个早停 bug 要单独修 —— 就是 A0 清单里的 #413(PR 待合;修好后这些视频抓得到东西、empty_streak 清零、自然不再退避)。验证脚本 _verify-fastlane-earlystop-bug.ts。
16bcfc233 / Fly worker v306(已 /health 探活)。改动:scrape-hot/route.ts(hiIds 纳入 next_fast_lane_at 退避过滤)+ worker/queue.py(去掉 high_intent 豁免)。无 migration。急停开关 HOT_FAST_LANE_BACKOFF=0(全关退避)。scheduled_at 落在窗口内、started_at 非空(已认领)统计。| scrape_video 入队→被认领等待 | median | p90(最慢 10%) | p99(最惨 1%) |
|---|---|---|---|
| ① 06-15 部署前 (n=26544) | 233.7s | 454s | 1151s |
| ② 06-16 注入洪水峰·#381 前 (n=51170) | 75.5s | 486s | 4596s |
| ③ 06-17→18 #381 后 ~21h (n=27269) | 57.8s | 1138s | 2191s |
pri=1 跟快视频抢道,部署后整号活搬到 pri=3(3160)、pri=1 里的整号只剩 112。但 p90 基本没动、p99 反而从 20 分钟炸到 80 分钟。为啥?看同一份数据的另一个数:pri=0 高意向注入活从全天 905 个暴涨到 26995 个(30 倍)——破鸡生蛋的注入洪水(每 tick 重注,见下)在 pri=1 前面又垒了一座 pri=0 大山,典型活照样快,但挤在洪水里的那 1% 倒霉蛋等得比以前更久。started_at 取最后一次认领造成的虚高;但方向——"尾巴没改善、注入洪水是主因"——跟全部其它证据一致。)· ①→②(#363 快慢分道):median −67%(典型大赢);但注入洪水把 p99 从 ~19 分钟炸到 ~77 分钟(尾巴翻车)。
· ②→③(#381 注入去重,6-17 上线):洪水退了(活数 51170→27269)、median 史上最好(57.8s)、p99 从洪水峰 4596 砍到 2191(−52%)、高+中 leads 回到 446(占比 8.3%)。
⚠️ 但尾巴还没回到基线:p99 仍 ~2× 基线(2191 vs 1151)、p90 反而 454→1138 变差。最可能的残余原因 = 早停 bug——#381 退避更狠了,而那 33%「假空」(真评论被漏,见下方 🔬)被退避推给最慢的全量轮、拖长了尾巴。
→ #413 早停修复已于 2026-06-18 合并部署,正是治这条尾巴;效果待下一个全天窗口复核(届时再更新本表 ③ 上方的 p90/p99)。
先界定这次只看了什么:只复测了上面 🔬 那个「假空率」指标(抓时已有评论却被早停漏掉的比例),没看 p90/p99 尾巴——尾巴要等满 24h 全天窗口才不被低峰美化(同 8.5h 假赢教训)。
结果:假空率 33% → 8%(post-fix 全程 10h 窗口、11044 个快道完成活、抽 120 个判空视频复验)。早停这半明显起效——单页全老就停导致的漏抓基本治住。
残留 8% 长什么样(埋给终判的线索,非定论):样本里还在漏的多是「评论已存在 3~6 天、被热度排序埋到前 50 名之外」那种——这不是早停能治的,是 max_comments=50 截断(另一半 B 方案的靶子)。所以残留偏向「截断型」而非「早停型」。
下一步:明天北京 12:00 后 ④ 全天窗口闭合,复核 p90/p99 是否回基线(454/1151)、并据残留性质定 B 方案(前 50→150)做不做。届时更新本表。
这段窗口里,pri=0 高意向注入活跑完 9039 个(= scrape_jobs 表中 priority=0、标 high_intent、在此窗口完成的单视频活条数;全天口径为 26995 个)。但这 9039 个活只摊在 112 个不同视频上 —— 平均每条被重复抓 80.7 次(9039 ÷ 112 ≈ 80.7)。三点说清这洪水的性质:
pri=1 前垒出一座 pri=0 大山、把极端尾巴 p99 从 20 分钟拖到 80 分钟。| 窗口 | 入库评论总数 | 高意向 | 中意向 | 高+中 | 高+中占比 |
|---|---|---|---|---|---|
| ① 06-15 全天 (部署前) | 4080 | 74 | 265 | 339 | 8.3% |
| ② 06-16 全天 (#381前) | 5291 | 93 | 280 | 373 | 7.0% |
| ③ 06-17 全天 (#381后) | 5361 | 97 | 349 | 446 | 8.3% |
| ①→③ 变化 | +31% | +31% | +32% | +32% | 8.3%→8.3% |
端到端「高意向评论 comment_time → 入库」实测(近 36h,n=124):median 34 分(典型还行),但 >1h 占 47%、>6h 占 42%、>12h 占 38%——这条重尾就是日报里那批标 expired_at_claim 被跳过没触达的 lead。按 source tier 拆开,真因和直觉相反:
| source tier | n | >6h 慢尾占比 | median 时延 |
|---|---|---|---|
| tier-0(pri=5 每小时) | 6 | 17% | 4 分 ← 居然最快 |
| tier-1(pri≤3 快车道) | 110 | 42% | 34 分 |
| 无源(孤儿视频) | 8 | 63% | 99 时 |
E 段那条"38% 老视频慢尾"怎么治?验证过 4 个方向,下面逐个说人话、再标现在的真实状态(不是计划、是查过库的实况):
① 把快车道的 48 小时窗口拉大到 72 / 96 小时 → 不做(已否决)
什么意思:快车道只盯"近 48 小时内冒过新评论"的视频。直觉会想:那把 48 拉到 96,不就把老视频也圈进来了?
为什么不做:模拟过——窗口拉到 96h,候选池涨 46%,但多出来的全是已经冷下去的老视频(大半还被退避挡在外面),真正救回的慢尾只有 0–1 条。窗口管的是"往回看多久",可这些视频的病是"过去那段压根没人去扫",看得再远也没用。方向错,不做。
② 给"老的高意向视频"单开一条低频回扫 → ✅ 已上线在跑,最对症
什么意思:专门挑那些"以前出过高/中意向、但现在掉出了 48h 快车道"的老视频(数量不多,是 Pareto 钓点),单独给它们排一条慢班车——每条视频最多 2 小时扫一次,把"几小时~7 天才被扫到"的盲区压成"保底 2 小时一扫"。
现在跑得怎么样:已上线(PR #377,cron /api/cron/cold-intent-rescan,每 20 分钟一轮,走 pri=3)。6-17 首份快照实测:它的空跑率 65%,是所有回扫通道里最低的——反过来说 35% 的轮次真抓到了新评论,是 4 条通道里唯一"打中"的,对症。代价:它走 pri=3,把 pri3 积压从 96 顶到了 384(所以下面 ④ 变急)。
③ 修"孤儿视频" → 暂没单独做(② 可能已顺手覆盖)
什么意思:库里有一批视频认不出是谁发的(source_account_id 是空的),任何"按号抓"的活都覆盖不到它们,只能烂在那——E 段那 8 个孤儿视频中位慢到 99 小时就是这种。
现在状态:没单独动手。因为 ② 是"按视频"撒网、不按号,孤儿视频天然就被 ② 网进去了 → 先看 ② 跑一段够不够,不够再单独按作者把来源回填上。
④ pri 防饿死(治另一个病:tier-0 被饿死、积压 12 小时) → 没做 · 已变急,该上了
什么意思:worker 严格"小号牌先领",pri=5 的 tier-0 活只在前面全空时才轮到 → 能积压 12 小时没人碰。两个治法:aging(一个活等太久就自动升号牌往前插)或配额(每批留 X% 名额给 pri≥5,不让它彻底饿死)。
为什么变急:② 那条 cold-intent 走 pri=3 上线后,又往 tier-0 头上压了一层——6-17 快照实锤 pri3 积压 96→384、tier-0 最老活仍卡 12 小时。所以 ④ 从"以后再说"提到了"该上了";要是 ④ 一时上不了,就得先把 ② 的 COLD_INTENT_RESCAN_MAX 调小、给 tier-0 泄压。
/queue/stats + Lark 采集快照卡。诚实边界:① 每日 tier 数变化库里无历史表、不可严格重建,本页用当前快照近似;② Leg C 的「8 槽悖论」已于 23:00 实查坐实=不是闲置,是 pri=1 队列常年积压 ~100 条 + 慢整号活占槽 + poison 活(见 Leg C);fly machines API 因 deploy token 无读权限未取到机器数,但 running 常态 8 + 78/分吞吐,有效并发 ~8;③ 僵尸判定用 7 天 / 高意向≥5(对齐升级线)。