三分之一的Common Crawl索引查询无响应:一次长期监控的教训

开发者五个月监控数据揭示:Common Crawl索引32%查询无响应,短重试无法应对小时级宕机。
一位开发者用Cloudflare Worker每天检查自己网站是否被Common Crawl收录,五个月内1,889次查询中605次(32%)完全无响应,错误类型以HTTP 503/504/521为主。04:00 UTC定时触发的任务失败率约37%,高于其他时段的21%,但存在手动重跑带来的选择偏差。10月连续三天的故障复盘暴露了两个关键设计问题:集合列表接口缺少重试机制;以及秒级间隔的重试对持续数小时的上游宕机毫无帮助。作者最重要的设计决策是将失败记为`error`而非"未收录",从而保证收录率指标不被无响应查询污染——这一原则对任何依赖外部数据源做轮询的系统都具有普遍意义。
一位开发者用一个运行在 Cloudflare Worker 上的小脚本,每天向 Common Crawl 索引发起一次例行查询,检查自己网站的十来个 URL 是否被收录进当前爬取快照。这本该是一个廉价、枯燥的健康检查,但五个月的数据揭示出一个比"收录与否"更值得关注的问题——索引服务本身的可靠性。
一个无聊脚本跑出的意外发现
整个检查流程非常简单:先请求一次集合(collections)列表,再对每个 URL 做一次 CDX 查询,最后把结果写进一张表。作者原本关心的是收录率,结果发现"失败"比"答案"更有意思。

从 5 月 15 日开始累积,173 个快照共产生 1,889 次查询,其中 605 次完全没有拿到答案,占全部查询的 32%。失败类型分布如下:
- 344 次 HTTP 503
- 103 次 HTTP 504
- 80 次 HTTP 521
- 52 次被作者自己的超时中断
- 18 次 502、7 次 500、1 次 520
更极端的是,在 173 个快照中,有 11 个快照里的每一次查询都失败了。十月是目前为止最糟糕的月份,132 次查询中有 79 次失败。
Common Crawl 是一个非营利组织,自2008年起持续对互联网进行大规模爬取,每月产出PB级别的网页存档数据,免费向研究者和开发者开放。CDX API(Capture Index API)是访问其索引元数据的标准接口:通过提交一个URL,可查询该URL是否被收录、收录时间及HTTP状态等信息,但不返回页面内容本身。Common Crawl的服务器资源有限,属于公益性质,因此在全球流量高峰时段容易出现过载。HTTP 521是Cloudflare特有的错误码,表示源服务器拒绝了连接,通常意味着源站宕机或拒绝响应,属于比503/504更底层的故障信号。
04:00 UTC 为何是最糟的时段
作者的定时任务固定在 04:00 UTC 触发。按时段拆分数据后,差异相当明显:
- 03:00–06:00 UTC 之间:1,339 次查询失败 491 次,失败率约 37%
- 其他时段:550 次查询失败 114 次,失败率约 21%
不过作者本人对这个对比保持了克制。他坦言其中存在选择偏差:大多数非整点的查询其实是手动重跑,而他通常是在看到早晨失败之后才去重跑——也就是说重跑发生得更晚,而那时上游可能已经恢复。这种自我诚实值得称道,但他也补充,这个方向性结论在五个月里一直成立。
连续三个早晨的故障复盘
真正暴露系统设计缺陷的,是 10 月的连续三天。
10 日和 11 日,脚本甚至没能走到 URL 查询这一步。请求集合列表时直接返回 521,而这个调用没有重试机制,于是整个阶段失败、什么都没写入。相比之下,URL 查询从 8 月起就加了重试,集合列表却被遗漏了。
作者随后给集合列表调用加了三次尝试,间隔 10 秒和 20 秒。12 日这一步终于通过了——然后紧接着的九次 URL 查询全部返回 521。
两次早晨故障后,他都在下午重跑了一遍:10 日 12 次查询全部成功,11 日 12 次成功 10 次。同样的代码、同样的 URL,只是晚了几个小时,结果就完全不同。写这篇记录时,大约 06:10 UTC,一个普通的 curl 拿到了集合列表的 200,但某个 URL 查询仍是 504。
关键教训:短重试治不了长宕机
这段经历给出了两个实用结论。
重试的间隔要匹配故障的时间尺度
间隔几秒的重试,只能救回那种偶发丢包式的请求失败;对于一个连续宕机数小时的索引,它毫无意义。如果上游进入了一个糟糕的时间窗口,正确的修复是"几小时后再试一次",而不是"20 秒后再试第三次"。这也是作者为这个阶段规划的下一步改动。
这一原则在可靠性工程中对应退避策略(backoff strategy)的设计思想。常见的指数退避(exponential backoff)适合处理瞬时网络抖动,而对于服务整体不可用的"褐色故障"(brownout)或"黑色故障"(blackout),更合理的模式是熔断器(circuit breaker):当检测到连续失败超过阈值时,直接停止尝试并等待一个较长的冷却周期,而不是按固定或指数间隔反复撞墙。作者规划的"几小时后再试一次"本质上是一种手动熔断——将重试窗口从秒级拉长到小时级,以匹配上游故障的实际恢复时间尺度。
失败必须记为错误,而不是"未收录"
另一个作者庆幸自己早做对的设计是:一次失败的查询被存为 error,而不是 "not included"。收录率只在真正拿到答案的 URL 上计算,当应答数量太少时宁可留空,也不显示一个数字。
这个区分在 10 月格外重要。11 日的真实情况是"10 个应答的 URL 里有 7 个被收录",而不是"12 个 URL 里有 7 个被收录"——只有前者是真的。如果把错误混入"未收录",10 月的数据会看起来像网站正在被 Common Crawl 剔除,而实际上只是索引没接电话。
对数据监控者的启示
这篇看似琐碎的运维笔记,其实提炼出一个被普遍忽视的原则:在看查询返回了什么之前,先看有多少查询真的返回了。
对于任何依赖外部数据源做定时轮询的系统——无论是爬虫索引、第三方 API 还是公共数据集——把"无响应"和"否定答案"混为一谈,都会污染后续的所有分析。一个 32% 的失败率如果被静默吞掉,产出的指标将是彻底误导性的。作者把全部运行快照公开在了自己网站的研究面板上,这种可复现、可审计的透明度,恰恰是数据工程中最该坚持的习惯。
相关推荐

CSS锚点定位:告别JavaScript的UI定位新方案
深入解析CSS锚点定位:从anchor-name、position-anchor到anchor()函数、position-area简写、position-try-fallbacks回退机制,以及anchor-size和即将到来的anchor queries。了解这一替代JavaScript定位方案的核心能力与当前限制。

AI图表交互新玩法:在对话中连续追问与可视化
探索AI对话中连续生成图表的新交互方式,通过/visualize命令在同一聊天窗口追问并可视化答案,提升数据分析效率。

Cursor并入X订阅:付费计划共享额度,Grok模型编程用量翻倍
Cursor正式并入The X Subscription,付费计划共享统一用量池,Grok模型编程用量翻倍,第三方模型转为API定价。现有Cursor用户可保留原计划或迁移,Premium、Super、Ultra额外包含Grok Bot。