对 Vercel 的每个请求都会经过我们的 CDN,该 CDN 平均每秒执行超过 8000 万条路由指令。其中的一部分工作是查找元数据,以确定哪些路径存在以及如何提供这些路径。当这些元数据未被缓存时,CDN 必须先获取它,然后才能提供响应。
我们过去一次只获取和缓存一个路径的元数据,仅检索每次查找所需的内容。这在当时看起来是高效的,但大型部署可能包含数十万个路径,每个路径都有自己的缓存条目。每次新部署都会引入新的元数据,使得缓存未命中成为频繁部署的大型站点反复出现的成本。
一次获取更多元数据使这些查找更快。通过将路径分组,每次获取可以为许多后续查找填充缓存。但获取过多也带来了自身的成本。通过生产环境实验,我们找到了一个平衡点,将 P99 元数据查找延迟降低了 91%,并在此过程中使部署更快。
复制标题链接CDN 如何找到正确的路由
请求中的路径并不总是与提供它的内容或函数的路径匹配。框架路由规则将两者连接起来。例如,对 /blog/hello-world
的请求可能解析为动态路由 /blog/[slug]
。对该页面 React Server Component 负载的请求可能解析为 /blog/[slug].rsc
。当 CDN 应用这些规则时,它可能需要检查多个目标路径以找到正确的响应。
框架在构建期间描述这些路由。借助框架定义的基础设施,应用程序代码声明哪些输出是静态的、哪些需要函数,以及哪些响应可以被缓存或重新生成。框架将该意图转换为 Build Output API 输出,Vercel 生成 CDN 读取的路由元数据。
在请求时,CDN 需要确定哪些目标路径存在并检索它们的元数据。我们添加到全局路由的 Bloom 过滤器排除了肯定不存在的路径。剩余的路径需要精确的元数据查找。
最初,我们将该元数据存储为每个目标路径的单独对象。CDN 独立地获取和缓存每个对象。这对于部署频率较低的小型项目效果很好。但每次新部署都会创建一组新的缓存键,因此每个路径的首次查找都会错过缓存。
复制标题链接按组获取元数据
为了减少缓存未命中,我们将许多路径的元数据分组到称为分片的文件中。我们限制了每个分片的大小,以控制一次查找必须获取多少数据。获取一个分片会将其所有路径的元数据带入缓存,因此对这些路径的后续查找可以重用它。


每个分片还包含一个索引,使 CDN 无需解码、解压缩或解析其他条目即可定位单个路径的元数据。每次获取会预热多个路径,而每次查找仍然只解析它所需的记录。


复制标题链接获取更多而不解析更多
我们使用 Vercel 其他大型路由数据集已采用的 JSONL 布局来构建分片。JSONL 每行存储一个 JSON 值。我们借用了 Bulk Redirects 中排序的交替键值记录,以及我们最初为 Bloom 过滤器构建的可直接寻址的 Base64 数据结构。
我们将数据布局与基于其上的查找结构分开,以便每个工作负载可以选择其所需的属性:
可检查、排序的键值 JSONL 记录,支持随机访问
可选的嵌入式索引,用于低开销的二分查找
嵌入的 Base64 数据,支持基于偏移量的解码
有界分片,控制传输和缓存成本
每个分片将排序后的目标路径及其元数据存储为交替的 JSONL 记录。内联索引记录每个条目的起始位置,因此路由进程可以直接跳转到该位置。这些位置存储为固定宽度的指针。每个指针是整数个六位 Base64 字符,因此任何单个指针都可以就地解码,而无需先将索引行解析为 JSON 或解码完整的 Base64 字符串。


与 Bulk Redirects 一样,部署的首次元数据获取已经确定了给定路径位于哪个分片,因此选择分片不会增加额外的往返。
在该分片内,路由进程使用索引指针对编码路径进行二分查找。查找一个路径需要 O(log n) 次指针读取和字符串比较。一旦找到匹配项,它就会解析下一行的元数据值。分片的其余部分保持未解析状态。
复制标题链接在生产环境中找到合适的分片大小
我们最初希望大多数部署的路径元数据能容纳在单个分片中。由于索引和二分查找使解析成本保持低廉,我们从数兆字节的分片开始。
大分片只有在靠近请求处保持缓存时才有意义。每个路由进程在内存中维护一个近期分片的最近最少使用(LRU)小缓存,位于该区域内所有进程共享的更大缓存之前。由于每次部署只有少量分片,我们预期大多数情况下会命中 LRU,从而抵消传输较大分片的成本。
在测试中,区域缓存命中率很高,但 LRU 命中率很低,因为请求分散在每个区域的许多进程中。传输数兆字节的分片也比我们预期的成本更高。


我们最终确定分片大小约为 200 KB,这保持了较高的区域缓存命中率,并使 LRU 未命中的填充成本很低。
生产环境测量显示平均查找延迟和 P99 查找延迟均有所降低:
基于 2026 年 8 月 5 日至 12 日的生产流量测量。
复制标题链接进一步缩小分片不值得为此进行发布
在每个分片中放入更少的条目降低了传输成本,但增加了分片数量。我们还评估了在不增加分片数量的情况下缩小分片,方法是将相同的条目编码得更紧凑。我们测试了三种方法:
对排序后的路径进行前端编码,使每个路径只存储与上一个路径不同的部分。
将分片拆分为去重元数据的 JSONL 文档。
使用更紧凑的自定义序列化格式。
离线模拟测量了编码后的大小和查找成本。
每种方法都产生了小得多的分片,但模拟预测延迟收益有限。我们认为额外的编码、兼容性和发布工作对于这次迁移不值得。当另一个工作负载使更多的索引或压缩值得付出努力时,它将进入共享库。
复制标题链接安全地更改路由
CDN 对每个部署的请求都运行此查找。如果分片元数据与按路径的元数据不一致,我们就会提供过时的路由、错误的状态码,或对存在的路径返回 404。
我们首先离线检查了这种不一致,然后在生产环境中检查。离线时,我们构建了测试部署,并在其上运行了一个测试框架,以两种方式查找每个路径并比较结果。
在生产环境中,在一个功能标志之后,路由系统对随机抽样的请求执行两种查找,同时仍然提供旧结果。它在后台将新结果与旧结果进行比较,我们观察了数周的不匹配情况,而没有减慢生产请求。我们称之为影子模式。
影子模式发现了差异,而且这些差异极为罕见。其中一个是旧编码中的 bug,它把路径打包进 RFC 2047 编码词,以便将非 ASCII 文本塞进仅支持 ASCII 的字段,只有当某个 emoji 被拆分到两个词中时才会显现。新格式将路径存储为纯 UTF-8,因此该 bug 在那里不会发生。捕获到这个边缘情况增强了我们对比较结果的信心,于是我们开始让这些分片承载生产流量。
复制标题链接更快的构建
一旦这些分片开始承载生产流量,我们便回到构建流水线,移除它们已经使其变得多余的工作:
跳过逐路径元数据上传可节省约 9.7 秒。
将路由组元数据写入清单可节省约 4.5 秒。
不上传这两项改动后变为空的文件可节省约 2.4 秒。
这些加起来大约节省 16.6 秒。在所有部署中,部署步骤大约快了 10%,而对于元数据繁重的部署——这些步骤在其中占主导地位——我们估计接近 25%。
复制标题链接为你下一次部署提供更快的路由
更快的元数据查找整体上改善了路由解析。对于大型站点,P99 路由解析现在大约快了一倍。在我们自己的营销和文档站点上,P99 元数据查找延迟从 203 毫秒降至 31 毫秒。中位数元数据查找延迟保持在约 0.7 毫秒。


应用程序的 Build Output API 契约保持不变。框架仍然描述应用程序需要什么,而 Vercel 改进 CDN 如何提供它。
2026 年 7 月 17 日之后构建的部署已经使用新的元数据分片。对于较旧的部署,重新部署你的项目以采用更快的查找。
