Cloudflare 近日发布了一篇引人注目的工程博客:通过优化 1.1.1.1 的 DNS 缓存数据结构,在全舰队范围内释放了约100TB内存——相当于130台 Gen 13 服务器的内存总量。而且这不是用空间换速度:缓存插入吞吐量提升43%,查找延迟下降19%。
每字节乘以2500亿等于巨额成本
支撑 1.1.1.1、Gateway DNS、DNS Firewall、AS112 等服务的 Big Pineapple 平台,任何时刻都缓存着超过2500亿条 DNS 记录。在这个规模下,每条记录浪费1个字节,全舰队就是250GB内存。五轮对缓存存储结构的修改,把每条记录的内存占用削减了50%以上。
五步优化:Rust 数据结构抠内存实录
① Vec 换 Box<[T]>:Vec 携带长度和容量两个字段,但 DNS 响应一旦入缓存就再也不会修改——capacity 字段纯属浪费,每个 Vec 多占8字节。每条缓存有8个 Vec/String 字段,换成 Box 后单条省64字节,仅此一项全舰队省下超过15TB。
② 合并列表、压缩指针:把 answer/authority/additional 三个独立列表合并成一个,用 u16 偏移量代替8字节指针加8字节长度的组合;布尔字段打包成位标志,顺带消除了 Rust 结构体的对齐填充。每条再省28字节。
③ 扔掉 owner 字段:绝大多数记录的 owner 就是查询的域名本身(如 example.com 的 A 记录)。对这类记录直接不存 owner,读取时从缓存 key 推断;只有 CNAME 链中 owner 不同的记录才额外存储。
④ 枚举变体装箱:Rust 枚举按最大变体占空间——RecordData 枚举因 NAPTR 变体(136字节)整体膨胀到144字节,而占流量80%以上的 A/AAAA 记录只需要4/16字节。把大变体装箱上堆、小变体内联后,每条 A/AAAA 记录省120字节。
⑤ 直接存 wire format 字节:不再存解析后的记录枚举列表,而是把记录编码成2字节长度前缀加原始字节,塞进单个 Box<[u8]>。构建响应时 A/AAAA/TXT/DNSSEC 记录可直接拷贝,无需逐字段序列化;数据连续存放还改善了 CPU 缓存局部性,这一项单独贡献了13%的插入吞吐提升。
结果:953 字节到 420 字节
五轮优化叠加,每条缓存记录的内存占用从953字节降到420字节(-56%),分配开销从1.1KB降到461字节(-58%)。生产环境里 p99 单实例内存从9.3GB降到5.3GB,p90 从6.5GB降到3.8GB,整个舰队的工作集内存低了约100TB。性能同样受益:插入吞吐从62.5万条/秒提升到89.3万条/秒(+43%),查找延迟从828ns降到670ns(-19%)。优化从5月18日到7月6日分步上线。
社区反响
HN讨论(451分,126条评论)里,工程师们的关注点很一致:有人质疑 capacity 字段既然没用,当初设计评审怎么没发现;有人感慨这就是为什么系统编程依然重要;也有人提出更激进的思路——缓存都这么大,为什么不直接优化查询路径、减少对缓存的依赖。Cloudflare 表示,省下的内存将用于扩大缓存容量、提升命中率,降低上游查询量。
原文:Cloudflare Blog | HN讨论:news.ycombinator.com/item?id=49468083