首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 汽车 游戏 社会热点 开源推荐 医疗健康 归档 标签 关于

通过优化 1.1.1.1 的 DNS 缓存节省 100 TB 内存

2026-08-28 1 阅读 约6分钟阅读 TangerineDream
分享:
字号:
Big Pineapple 是 1.1.1.1 、 Gateway DNS 、 DNS Firewall 、 AS112 和其他几个 Cloudflare DNS 服务背后的平台,在任何给定时间都存储超过 2500 亿个 DNS 缓存条目。在这种规模下,每个条目浪费一个字节就会导致我们的设备组占用超过 250 GB 的内存。对缓存条目在内存中存储方式的连续五次更改将每个条目的占用空间减少了 50% 以上。在我们的机群中,这些更改释放了大约 100 TB 的内存,相当于我们 130 台第 13 代服务器中的 RAM 量。缓存也变得更快。插入吞吐量上升了 43%,查找延迟下降了 19%,因为更少的分配和更好的内存局部性意味着我们不必为了空间而牺牲速度。我们缓存的内容冷启动时,Big Pineapple 会以空缓存开始。当 DNS 查询到达时,缓存会被填满,直到达到最大条目数,此时我们会驱逐较旧或不太受欢迎的项目以腾出空间。确切的缓存大小因数据中心而异。当使用 EDNS 客户端子网 (ECS) 时,权威服务器根据客户端的网络返回不同的答案,因此我们缓存同一查询的多个版本。这会增加条目数量和每个条目消耗的内存,使得本文中的优化对于 ECS 密集的位置特别有影响。缓存中的每一项都是一个键值对。键标识所查询的内容: pub struct CacheKey { qname : Name , qtype : Rtype ,authentiated : bool , tag : Vec < u8 >, } 该值存储 DNS 响应本身:答案、权限和其他记录部分,以及元数据,例如创建时间、命中计数器和生存时间 (TTL)。 pub struct CacheEntry { timestamp : UnixTimeStamp , pub inception : Instant , pub ttl : Ttl , pub attempts : u32 , pub Answers : Vec < Record >, pubauthority : Vec < Record >, pub extra : Vec < Record >, pub error : Vec < ExtendedError >, ... } 这两个结构体都有改进的空间。有几个字段使用的类型会带来存储条目后我们不需要的开销。内存使用基准测试 为了衡量每个更改的影响,我们通过使用随机生成的条目填充缓存来进行基准测试,这些条目大致匹配我们在生产中看到的流量分布:56% A 记录、25% AAAA 和 19% TXT 。每个条目包含一到四条记录。 TXT 记录充当基准中所有非 A/AAAA 记录类型的替代。它们的大小在 64 到 224 字节之间随机化,接近我们看到的可变长度记录类型的平均响应大小。我们使用自定义分配器来跟踪内存使用情况,该分配器包装 Rust 的系统分配器并记录每个缓存条目的分配数量和大小。除了内存之外,我们还测量整个缓存流中的插入吞吐量和查找延迟,以确保节省内存不会以牺牲性能为代价。这些投入近似于生产,而不是精确地再现它。进程内存还取决于流量组合、缓存占用、分配器状态以及缓存外使用的内存。因此,我们在推出期间测量了生产实例中的驻留内存。容量成本 Vec 存储三个字段:指向堆分配数据的指针、当前长度和总容量。当你推送一个项目时,Vec 会检查长度是否超过容量,并在需要时重新分配。如果有空间,它只会附加该项目并增加长度。然而,一旦我们将 DNS 响应存储在缓存中,我们就不会再修改它。容量字段没有任何作用,但每个 Vec 仍然需要 8 个字节。过度分配的堆空间也被浪费了,因为一个 Vec 可以容纳 8 个项目,但只存储了 5 个项目,在堆上留下了 3 个未使用的插槽。使用 Box<[T]> 可以解决这两个问题。它在创建后无法增长,因此不需要容量字段或为未来的元素保留空间。这同样适用于 String ,它也带有容量字段。 Box 将其丢弃。每个缓存条目存储 8 个 Vec 和 String 字段。将它们替换为 Box<[T]> 和 Box 可节省每个字段 8 个字节、每个条目 64 个字节。它还消除了 Vec 为未来增长保留的多余堆内存。总计节省的成本超过 15 TB,缓存条目超过 2500 亿个。更少的列表,更少的指针 我们可以存储一个带有到每个部分开头的偏移量的列表,而不是将答案、权威和其他部分存储在单独的列表中。由于每个部分的 DNS 记录计数适合 u16 ,因此与每个单独的 Box<[T]> 所需的 8 字节指针和 8 字节长度相比,我们可以为每个偏移量使用 u16 (2 字节)。这会删除两个列表,每个列表都有一个 8 字节指针和 8 字节长度,并用两个 2 字节偏移量替换它们,每个条目节省 28 字节。这些节省并不总是直接映射到从各个字段中删除的字节数。 Rust 插入填充以满足对齐要求,并将结构体的大小舍入到其对齐方式的倍数。因此,删除一个小字段可以消除
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱