← 文章 / 云原生与基础设施
Hacker News 2小时前 · 2026-09-03 16:28:38 · 0 阅读

使用 Zstandard 和 Pingora 可节省 PB 级缓存存储

内存成本正在急剧攀升。过去一年里,RAM 和硬盘的价格都大幅上涨。在 Cloudflare,我们运营着多个大规模分布式存储产品(包括著名的 CDN),依赖于高效利用已部署的内存,才能持续服务所有客户。

基于这一考量,我们尝试了一种扩展有效缓存容量的方案:在 Pingora 内部对符合条件的资源使用 Zstandard 进行编码,以轻微增加 CPU 消耗为代价,换取显著的存储和跨数据中心带宽节省。

我们一直在原型开发一个名为 Cache Transcoding 的系统,这是我作为 1.1.1.1 Intern Program 实习生时在 Cloudflare 构建的项目。当符合条件的响应进入缓存时,我们会先用 Zstandard(简称 zstd)对其编码,然后再写入磁盘。资源在缓存中存续期间以及通过 Tiered Cache 在各数据中心之间传输时,都保持压缩状态,直到向客户端提供服务前才解码。

初步测试显示,这种编码平均将符合条件的资源压缩至原始磁盘大小的三分之一。预估的额外 CPU 开销很小,但这就是取舍——轻微的 CPU 增长为 Cloudflare 带来了 PB 级别的有效缓存容量,并减少了数据中心之间的数据传输量。编码成本仅在资源首次进入缓存时支付一次,而存储和带宽的节省却在每次资源被重复使用时持续产生。

Zstandard 是什么?

Zstandard(简称 zstd)是一种无损压缩算法,由 Yann Collet 在 Facebook 开发并于 2016 年开源。无损意味着压缩数据解码后,每一个字节都与原始数据完全一致。我们可以在不改变资源本身的前提下,改变其在磁盘上的表示方式。

Zstd 在设计时兼顾压缩率与速度。在我们此前进行的浏览器压缩测试中,它的压缩速度比 Brotli 快 42%,而文件大小几乎相同;在相近速度下,生成的文件比 gzip 小 11.3%。这种平衡至关重要,因为 Cache Transcoding 会处理大量流量,编码和解码都必须保持高速。

原型采用 zstd level 3,在获得大部分压缩收益的同时,不会让缓存填充成为 CPU 瓶颈。

Cloudflare 传统上按照源站提供的 content encoding 存储资源。如果源站发送未压缩的响应,我们就将未压缩的字节存储在磁盘上,并以相同形式在数据中心之间传输。Cache Transcoding 则直接在缓存内部添加压缩。

并非所有内容都值得压缩

转码并不意味着压缩一切。图片、视频和字体通常已经过压缩。在我们的流量样本中,这类媒体内容占请求量的 21.4%,却占字节量的 63.3%。再次压缩它们只会白白消耗 CPU。

可压缩的文本则不同。HTML、JSON、CSS 和 JavaScript 占请求量的 67.3%,字节量的 22.3%。在这部分文本中,约 71% 的资源以未压缩形式到达且 Content-Encoding 未设置,压缩效果很好。

在受控测试语料库中,符合条件的资源压缩率约为 2.8 倍。

指标

数值

压缩比

2.834x

编码成本

每字节 4.31 ns,约 232 MB/s,每次缓存填充支付一次

解码成本

每字节 1.56 ns,约 641 MB/s,每次服务支付

虽然编码每字节成本更高,但资源的被服务次数远多于填充次数。

通过改变资源的表示方式,现有硬件能够存储更多客户内容。

磁盘上的字节更少,意味着每台服务器可以保留更多对象。这提高了缓存密度,并降低了有用内容因未压缩表示占用过多空间而被驱逐的可能性。

资产在分层缓存系统中流转时,更小的体积同样能带来收益——它减少了 Cloudflare 数据中心之间的数据传输量,提升了骨干网的利用效率。

只付一次压缩代价

压缩从来不是免费的。编码和解码都需要消耗 CPU,关键问题是节省的字节数能否抵消计算开销。

在 zstd level 3(速度与安全解压体积的常用默认档位)下,基于我们测试的流量与复用假设,模型测算出的额外 CPU 开销仅占几个百分点。

我们曾考虑只对热门内容进行转码,因为热资产被复用的频率更高,但效果并不理想。每次服务资产都需要解码,因此将功能限制在最热的内容反而会减少存储收益,却没能同等程度地节省 CPU。

更简单的策略反而效果更好。对所有不低于 4 KiB 的可压缩文本一律转码,几乎捕获了全部可测量的存储收益,同时 CPU 预算仍在可控范围内。

缓存转码的工作原理

缓存未命中时,基于 Pingora 的代理会在写入磁盘前使用 zstd 对响应体进行编码。缓存元数据会记录该存储表示已被压缩,并保留原始内容长度。在响应离开代理之前,响应体会被解码还原为原始表示

缓存命中时,直接从磁盘读取 zstd 对象并解码。在分层缓存中,压缩表示以压缩形式从上层传至下层,解码仅在面向客户端的那一跳发生。

全量缓存未命中时,上层从源站拉取原始字节。这些字节被编码一次后以 zstd 形式存储,再以压缩形式传至下层。下层同样保存 zstd 表示,随后在请求路径上对其进行解码。

unnamed (77).png

当下层未命中但上层已存在该对象时,不涉及源站。压缩对象直接在缓存层之间流转,在网络传输和磁盘存储时始终保持压缩状态,仅在下层读取时解码一次。

当下层已缓存该对象时,无需任何网络传输或编码操作。下层直接从磁盘读取 zstd 字节,解码后返回原始内容。

存储编码标记可防止对象被重复编码。缓存层从上接收对象时,可通过该标记识别其已采用 zstd 存储,并维持这一格式。

为何只对部分文本进行转码

最快的压缩操作就是根本不需要执行的操作。因此缓存转码通过一系列筛选条件,跳过不太可能受益的内容。

原型仅在满足以下条件时才对 200 OK 响应进行转码:Content-Encoding 未设置、Content-Type 为可压缩文本,且 Content-Length 已知且至少为 4 KiB。切片子请求、启用了上游压缩的响应、范围请求、预压缩响应、长度未知的正文以及二进制内容均保持不变。

4 KiB 的阈值过滤掉了大量微小请求,同时仅排除了约 1% 的其他符合条件字节。降低阈值只会带来按对象计算的额外开销,却省不下多少存储。

该阈值和 zstd 压缩级别均为可调参数,而非永久限制。我们从 zstd level 3 和 4 KiB 起步,以此作为保守方式来评估这套架构。在初步摸清 CPU 预算后,我们可以测试更高的压缩级别是否足以带来可观的压缩率提升,从而证明其额外开销是合理的。

在一百万次请求上测试缓存

我们在受控测试区域对原型进行了验证,并将每条请求在请求日志、Prometheus 指标和 Jaeger 链路追踪中交叉关联。

正确性测试涵盖了缓存未命中、缓存命中、单跳填充、分层缓存填充等场景。我们通过调整缓存键,让每个请求走特定的路径,并用追踪工具确认编码和解码发生的位置。 其中一项性能测试在 10 台缓存服务器上发送了超过一百万个请求。一半测试禁用分层缓存,另一半启用分层缓存,这样我们就能够分别测量本地缓存行为和缓存层间的传输开销。 这两个资产分别约为 195 KiB 和 272 KiB,均被压缩了约 2.8 倍。这显然是刻意选择的高可压缩测试集——它为我们验证架构提供了清晰的信号,但并不能代表互联网上的所有文本对象。要将测得的压缩比当作全集群常量来使用,还需要更广泛的语料库。

一次压缩,多次受益

这次实验告诉我们,仍有显著的效率提升可以部署在我们的缓存服务中,惠及所有客户。Cache Transcoding 的方案证明,在测试条件下这种权衡是划算的:架构既完整保留了内容,CPU 开销也控制在预算内。 下一步,我们计划评估更高的 zstd 压缩级别,测试更广泛的 content types 和 object sizes,以及调优 eligibility criteria 等参数。未来工作还将研究 range requests、pre-compressed origin responses,以及如何将 compressed object 直接传递给已支持它的 downstream components,无需解码。 实习期间,我有幸与 Cloudflare 的工程团队并肩工作,参与真正在全球网络上存储和分发内容的核心基础设施。如果你想从帮助构建更好的 Internet 开启职业生涯,欢迎了解我们的 internship opportunitiesjob openings
原始来源: Hacker News

评论 (0)