Cloudflare 将 1.1.1.1 DNS 缓存的内存占用削减了 100TB
Cloudflare 重新设计了 1.1.1.1 递归解析器所使用的 DNS 缓存内存表示,将每个条目的基准空间占用减少了 56%,在整个服务器集群中释放了约 100 TB 的工作集内存。这项工作针对 DNS 平台 Big Pineapple 的 Rust 缓存表示形式连续进行了五次迭代优化,最终使缓存插入吞吐量提高 43%,查询延迟降低 19%。
在任何时刻,Big Pineapple 都存储着超过 2500 亿个 DNS 缓存条目。Cloudflare 系统工程师 Sebastiaan Neuteboom 在 LinkedIn 上这样描述这一成果:
能节省 100 TB 的内存,这可不是每天都能遇到的。
五轮迭代优化的技术路径
优化的核心思路是逐层消除 Rust 数据结构中的冗余分配与表示开销:
-
堆替换栈上动态数据(Vec/String → Box<[T]>/Box<str>):将插入后固定不变的数据类型从
Vec和String替换为Box<[T]>和Box<str>。这一步的关键洞察在于缓存条目一旦写入即不可变——Vec和String的容量字段(cap)对于只读场景是纯浪费。替换后每个条目节省 64 字节,集群累计释放超过 15 TB。 -
记录合并与紧凑偏移量列表:将应答(answer)、权限(authority)及其他记录合并为单一带紧凑偏移量的列表,消除多记录独立分配的元数据开销。
-
布尔值位打包:将布尔字段打包成位标志(bit flags),将 8 字节的 bool 对齐浪费压缩到单个 bit。
-
省略冗余所有者名称:省略与查询域名匹配的所有者名称(owner name),转而通过缓存键在查询时重建。DNS 缓存条目中所有者名称往往与查询名重复,这一步省去了大量字符串存储。
-
枚举装箱的权衡与最终放弃:这是最大的挑战。Cloudflare 最初尝试对较大的 Rust 枚举变体进行装箱(boxing),但独立的内存分配不仅增加了分配器开销,还降低了缓存局部性(cache locality)——指针跳转破坏了 CPU 缓存行预取。一位 Reddit 评论者对此总结道:
许多这种类型的内存优化技巧只有在达到 Cloudflare 的请求量级别时才能发挥作用;在规模比较小的情况下,将变体进行装箱所造成的额外间接访问,对缓存局部性的损害可能大于其实际益处。
最终设计:DNS 有线格式作为存储格式
Cloudflare 的最终方案放弃了自定义 Rust 结构体,转而直接采用 DNS 有线格式(wire format)——将记录数据存储在连续的字节缓冲区中。这一设计的工程价值在于:
- 消除了枚举变体的内存分配开销和按记录分配的元数据开销;
- 连续字节布局天然提升缓存局部性,CPU 预取器友好;
- 常用记录类型可直接
memcpy到响应缓冲区,零解析开销; - 包含域名的记录仍需解析以实现 DNS 名称压缩(name compression),但这是局部而非全局代价。
图1:Cloudflare 的 DNS 缓存使用 DNS 有线格式将记录数据存储为连续的字节缓冲区(图片来源:Cloudflare 博客文章)
与同类递归解析器的架构对比
这一方案与其他递归 DNS 解析器的缓存架构有显著差异:
- Unbound:维护独立的消息缓存、RRset 缓存、键缓存和负缓存,缓存大小和分块设置均可配置。模块化程度高,但单条目表示开销较大。
- PowerDNS Recursor:使用多种缓存类型,包括数据包缓存(packet cache)和记录缓存(record cache),分层设计。
- Cloudflare Big Pineapple:工作重点不在于多级缓存分离,而在于将单条目表示和分配开销压到极致——本质上是在追求"一个缓存条目占用的字节数"这一指标的全局最优。
这种差异本质上反映了不同的工程哲学:Unbound 和 PowerDNS 追求灵活性与可配置性,而 Cloudflare 在超大规模下选择牺牲结构灵活性换取内存效率。
生产环境部署与实测结果
该优化于 2026 年 5 月 18 日至 7 月 6 日期间在生产环境中全面部署。关键指标变化:
- 单条目内存占用:953 字节 → 420 字节(−56%)
- 单条目内存分配:1.1 KB → 461 字节
- 驻留内存(p99):每实例 9.3 GB → 5.3 GB
- 驻留内存(p90):每实例 6.5 GB → 3.8 GB
- 插入吞吐量:+43%
- 查询延迟:−19%
- 集群级释放内存:约 100 TB
图2:Cloudflare 生产环境内存使用图(图片来源:Cloudflare 博客文章)
Cloudflare 计划利用释放出的 100 TB 内存来增加缓存容量,而非增加整体内存消耗——即在硬件成本不变的前提下提升缓存命中率,形成性能正循环。
工程启示
从架构视角看,这个案例有几个值得注意的点:
-
数据不可变性是优化的前提:只有当数据生命周期明确(写入后只读),才能安全地用紧凑布局替换动态容器。这对前端状态管理中的不可变数据模式有类比意义。
-
存储格式选择应匹配访问模式:选择有线格式而非自定义结构体,是因为热点路径(常用记录直接复制到响应)受益于零拷贝,而冷路径(域名解析)的代价可接受。格式设计与访问模式对齐是高性能系统的通用原则。
-
装箱/指针跳转的局部性代价在规模放大后才显著:小规模下指针跳转的延迟可忽略,但在每秒百万级查询的缓存路径上,缓存行未命中(cache miss)会成为瓶颈。这提醒我们在做性能优化时要区分"逻辑正确"和"物理高效"。
-
100 TB 不是一次优化完成的:五轮迭代每轮贡献不同量级,从 64 字节/条目到整体架构重构。大规模系统的优化往往是持续递进而非一蹴而就。
原文链接:https://www.infoq.com/news/2026/09/cloudflare-dns-cache/