Cloudflare优化1.1.1.1 DNS缓存节省100TB内存的工程实践

DNS缓存背后的内存挑战
Cloudflare的公共DNS解析服务1.1.1.1是全球规模最大、速度最快的DNS服务之一,每天处理着数以万亿计的查询请求。该服务于2018年4月1日正式上线,是一个面向公众的递归DNS解析服务。递归DNS解析器的角色类似于互联网的"电话簿中介"——当用户在浏览器中输入一个域名时,递归解析器负责从根域名服务器开始逐级查询,最终找到目标域名对应的IP地址。
具体来说,一次完整的递归DNS查询通常经历三级跳转:首先,递归解析器向全球13组根域名服务器(由ICANN协调管理,实际通过Anycast部署了数百个实例)发起查询,根服务器不直接返回目标域名的IP地址,而是告知负责该顶级域(如.com、.org)的TLD(Top-Level Domain)服务器地址;接着解析器向TLD服务器查询,TLD服务器返回目标域名的权威DNS服务器地址;最后解析器向权威服务器发起查询,获取域名的最终记录。这个过程被称为"迭代查询"——虽然用户发起的是一次递归请求(即"帮我查到最终结果"),但解析器在后端是逐级迭代的。正因为每次全链路查询涉及多次网络往返,缓存机制才成为DNS性能的关键——理想情况下,热门域名的解析结果可以直接从本地缓存返回,将查询延迟从数十毫秒降至亚毫秒级别。
与Google的8.8.8.8、Quad9的9.9.9.9等同类服务相比,1.1.1.1以隐私保护和查询速度见长,Cloudflare承诺不将DNS查询数据用于广告目的,并定期发布第三方审计报告。
在如此庞大的流量规模下,即使是微小的效率问题也会被无限放大。近期,Cloudflare工程团队分享了一项引人注目的成果:通过优化1.1.1.1的DNS缓存机制,他们成功节省了高达**100TB(100太字节)**的内存资源。
这一数字背后,反映的是超大规模系统工程中"细节即成本"的核心逻辑。当你的服务运行在遍布全球数百个数据中心、成千上万台服务器上时,每台机器上节省几十兆字节的内存,累积起来就是惊人的规模效应。
DNS缓存为何如此消耗内存
缓存是DNS解析性能的核心
DNS解析服务的核心价值在于速度。为了让用户尽可能快地获得域名解析结果,DNS解析器会将查询到的记录缓存在内存中。这样当同一个域名再次被查询时,就无需重新向权威服务器发起递归查询,可以直接从内存返回结果。
然而,缓存的代价就是内存占用。互联网上存在着海量的域名——根据Verisign的统计,仅.com和.net域名就超过1.7亿个,加上其他顶级域,全球注册域名总数超过3.5亿。每个域名可能对应多种记录类型。DNS记录类型是DNS协议中定义的不同资源记录格式,每种类型承载不同的信息:A记录将域名映射到IPv4地址(如93.184.216.34),AAAA记录则映射到IPv6地址(如2606:2800:220:1:248:1893:25c8:1946)。MX记录指定处理该域名电子邮件的邮件服务器及其优先级。TXT记录存储任意文本信息,常用于SPF(发件人策略框架)、DKIM(域名密钥识别邮件)等邮件验证机制。CNAME记录是一种别名机制,将一个域名指向另一个域名。此外还有NS记录、SOA记录、SRV记录等。IANA(互联网号码分配局)目前定义了超过250种DNS记录类型,虽然实际常用的只有十几种,但一个繁忙的域名可能同时拥有十余种不同类型的记录,每种都需要在缓存中独立存储。
值得注意的是,DNS协议本身在报文层面就包含了一种节省空间的设计——RFC 1035中定义的DNS消息压缩(Name Compression)机制。在一条DNS响应报文中,如果同一个域名或域名后缀出现多次,后续出现时可以用一个2字节的指针引用之前出现的位置,而非重复存储完整的域名字符串。例如,当响应中同时包含 www.example.com 的A记录和MX记录时,example.com 部分只需在报文中出现一次。然而,这种压缩是针对传输报文的,缓存中的存储方式通常需要将记录解析为结构化的内存对象,压缩指针在此场景下并不直接适用,缓存层面的优化需要另辟蹊径。
加上TTL(生存时间)等元数据,这些信息全部驻留在内存中。TTL是DNS记录中的一个关键字段,由权威DNS服务器设定,表示该记录在递归解析器缓存中允许存活的秒数。当TTL倒计时归零后,缓存条目失效,解析器必须重新向权威服务器查询以获取最新记录。TTL的设定体现了一种经典的工程权衡:较长的TTL(如86400秒即一天)可以减少递归查询次数、提升响应速度,但DNS变更的传播延迟增大;较短的TTL(如60秒)允许快速切换IP地址,有利于故障转移和负载均衡,但会增加查询流量和缓存系统的维护开销。对于缓存系统而言,TTL还意味着每个条目都需要额外存储过期时间戳,并持续进行过期检查和清理操作。对于1.1.1.1这样处理全球流量的服务而言,缓存条目数量极其庞大。
数据结构的隐性内存开销
在实际的缓存实现中,除了域名和记录本身占用的空间之外,还存在大量的"隐性开销":
- 数据结构元数据:如指针、哈希表槽位、对齐填充(padding)等。哈希表是DNS缓存系统中最常用的核心数据结构,它通过哈希函数将域名和记录类型的组合映射到一个固定大小的数组索引,实现O(1)平均时间复杂度的查找。然而哈希表本身带有显著的内存开销:为了控制冲突率,通常维持50%-75%的负载因子,意味着至少有25%-50%的槽位是空的但仍占用内存。冲突处理机制(开放寻址法或链地址法)也需要额外的指针和节点分配。在64位系统中,每个指针就占用8字节。此外,哈希表扩容时通常需要将容量翻倍并重新哈希所有条目,期间会短暂占用双倍内存。
- 重复存储的信息:同一域名的不同记录之间可能存在冗余数据。例如,
www.example.com的A记录和AAAA记录在缓存中可能各自独立存储完整的域名字符串,而实际上域名部分完全相同。如果采用域名字符串共享或intern池化技术,这些冗余可以被有效消除。 - 内存对齐带来的浪费:为了访问效率,编译器会对数据结构进行内存对齐,导致字节级浪费。内存对齐是计算机体系结构中的一个基础概念——现代CPU访问内存时并非逐字节读取,而是以固定大小的"字"(通常为4字节或8字节)为单位进行读取。当数据的起始地址恰好是其大小的整数倍时,CPU可以通过一次内存访问完成读取;反之可能需要两次访问再拼接,显著降低性能。为保证对齐,编译器会在结构体字段之间插入填充字节。例如,在64位系统上,一个包含1字节char和8字节long的结构体,编译器可能在char之后插入7个填充字节,使结构体占用16字节而非9字节,浪费率高达44%。
- 内存分配器的碎片化开销:除了数据结构本身的内存浪费,内存分配器(如glibc的ptmalloc、FreeBSD的jemalloc、Google的tcmalloc)也会引入额外开销。现代内存分配器通常采用分级分配策略——将内存划分为不同大小等级的"桶"(size class),一次分配请求会被向上取整到最近的桶大小。例如,jemalloc的小对象桶大小通常是8、16、32、48、64字节等,如果实际需要33字节,则会分配48字节的桶,产生15字节的内部碎片。此外,分配器还需要在每个分配块前后维护簿记信息(如块大小、空闲链表指针等),通常每次分配额外消耗8-16字节。对于DNS缓存这种拥有海量小对象的场景,分配器碎片和簿记开销的累积效应尤为显著。长期运行的服务还会面临外部碎片问题——已释放的内存块因大小不匹配而无法被新的分配请求复用,导致进程的RSS(常驻内存集)持续增长,即使有效数据量并未增加。
正是这些看似不起眼的开销,在超大规模下汇聚成了巨大的内存消耗。
Cloudflare DNS缓存优化的核心思路
从数据结构层面精简内存布局
Cloudflare团队的优化重点在于精简缓存条目的内存布局。在超大规模系统中,最有效的内存优化往往不是删除数据,而是让数据的存储方式更加紧凑高效。
典型的优化手段包括:
- 重新设计缓存条目的数据结构:通过调整字段顺序、消除内存对齐造成的填充空隙,让每个缓存条目占用更少的字节。具体来说,通过将较大的字段放在前面、较小的字段集中排列,可以显著减少编译器插入的填充字节。这种技术看似简单,但在拥有数十亿缓存条目的系统中,每条目节省几个填充字节的效果极为可观。
- 消除冗余存储:识别并合并重复存储的信息,避免同一份数据被多次保存。
- 更紧凑的编码方式:对域名、记录等信息采用更节省空间的编码或压缩方案。例如,域名可以使用变长编码(variable-length encoding)替代固定长度缓冲区存储,或者采用前缀树(Trie)结构共享公共域名后缀。对于数值字段,可以使用变长整数编码(varint),让小数值只占1-2字节而非固定的4或8字节。
在缓存替换策略方面,DNS缓存与通用缓存系统有一个重要区别:DNS记录自带TTL,过期条目必须被清除。但对于缓存容量不足时的淘汰决策,仍需要额外的替换策略。常见的LRU(最近最少使用)策略需要为每个条目维护一个双向链表节点(两个指针,在64位系统中占16字节)以及最近访问时间戳(8字节),这意味着仅缓存替换策略的元数据就为每个条目增加24字节的开销。更先进的策略如LFU(最近最不常用)还需要维护访问频率计数器。一些现代缓存系统采用的近似LRU算法(如时钟算法或采样式LRU)可以用更少的元数据实现接近最优的替换效果——例如只需每条目1个比特的访问标记位,将元数据开销从24字节降至接近零。优化缓存替换策略的元数据开销,也是整体内存优化的重要组成部分。
规模效应的乘数放大
这类优化的魅力在于其乘数效应。假设通过优化让每个缓存条目平均节省了几十个字节,看起来微不足道。但当系统中存在数十亿个缓存条目,并且这些条目在全球数百个数据中心中被独立缓存时,单个条目的微小节省就会被放大成TB级别的整体收益。
值得特别说明的是Cloudflare的Anycast网络架构对这一规模效应的放大作用。Anycast是一种网络寻址和路由方法,允许多个地理分散的节点共享同一个IP地址(如1.1.1.1)。当用户发起DNS查询时,BGP(边界网关协议)路由协议会将请求导向网络拓扑距离最近的节点。BGP的路由决策基于多个因素:AS(自治系统)路径长度是最基本的度量,即数据包到达目标需要穿越的自治系统数量;此外还考虑本地偏好(Local Preference)、多出口鉴别器(MED)、路由来源类型等属性。在Anycast场景下,由于所有节点都宣告相同的IP前缀,BGP会自然地将流量导向路径最短的节点,实现了无需客户端感知的地理级负载均衡。这种架构还天然具备DDoS防护优势——攻击流量会被分散到全球所有节点而非集中在单一目标,每个节点只需承受总攻击流量的一个子集。
然而,Anycast架构也带来了缓存方面的独特挑战——每个数据中心的DNS缓存是独立维护的。同一个热门域名的记录会在全球330多个城市的数据中心中被分别缓存,形成大量冗余。这与集中式缓存架构有本质区别:在集中式架构中一条缓存记录只需存储一次,而在Anycast架构下,热门域名的记录可能被缓存数百份。这种设计上的取舍是为了保证查询延迟的确定性——跨数据中心的缓存查询会引入额外的网络延迟(即使在Cloudflare的骨干网络中,跨洲际的往返延迟也在100-200毫秒),违背了DNS解析"快速响应"的核心诉求。部分DNS服务商尝试过使用一致性哈希或分层缓存(将多个PoP节点的缓存查询回源到区域级缓存节点)来缓解冗余问题,但这种方案增加了系统复杂性,并在缓存未命中时引入额外的延迟跳数。Cloudflare选择了每节点独立缓存的简洁架构,接受冗余的代价,转而通过极致的单条目内存优化来控制总体成本。
这也解释了为何最终能达到100TB的节省规模——它不是某个单点的巨大突破,而是精细优化在超大规模基础设施上的累积体现。
100TB内存优化带来的深层启示
超大规模工程中细节决定成本
对于普通应用开发者而言,几十字节的内存优化几乎没有意义。但对于运行全球基础设施的公司来说,这种优化直接转化为实实在在的成本节约。100TB的内存节省在超大规模基础设施的成本语境下具有重大经济意义。以当前服务器级DDR5 ECC内存的市场价格估算,每GB约5-8美元,100TB(约102400GB)对应的硬件采购成本约为50万至80万美元。但实际节省远不止硬件本身——内存是服务器中功耗密度最高的组件之一,每GB DDR5内存的典型功耗约0.3-0.5瓦,100TB内存的持续功耗约为30-50千瓦。按照数据中心的综合电力成本(包含PUE系数约1.2-1.4的冷却和配电损耗),这相当于每年约26万-44万千瓦时的电力消耗。
PUE(Power Usage Effectiveness,电力使用效率)是衡量数据中心能效的核心指标,定义为数据中心总电力消耗与IT设备电力消耗的比值。PUE为1.0意味着所有电力都用于计算,这在物理上不可能实现,因为冷却系统、配电损耗、照明等辅助设施都需要消耗电力。行业平均PUE约为1.58,而Google、Microsoft等超大规模运营商的顶级数据中心可以做到1.10-1.12。Cloudflare作为全球化分布的网络服务商,其数据中心遍布各种气候条件的地区,平均PUE可能在1.2-1.4之间。这意味着IT设备每消耗1千瓦电力,整个数据中心实际消耗1.2-1.4千瓦。在此背景下,100TB内存节省带来的30-50千瓦IT功耗降低,实际转化为36-70千瓦的总功耗降低。
此外,减少内存占用意味着相同的服务器可以承载更多的缓存条目或其他工作负载,提升了单机利用率,推迟了硬件扩容的时间节点。在供应链紧张时期,这种"软扩容"的价值尤为突出。
这提醒我们,系统工程的价值评估必须放在其运行规模的语境下。同样一行代码的优化,在小规模系统中可以忽略不计,在超大规模系统中却可能价值百万。
DNS性能与资源消耗的平衡艺术
DNS缓存优化本质上是在响应速度与资源消耗之间寻找最优平衡。Cloudflare的实践表明,通过精巧的工程设计,完全可以在不牺牲缓存命中率和响应速度的前提下,大幅降低内存占用。这种"既要又要"的平衡能力,正是顶级基础设施团队的核心竞争力。
从更广泛的系统设计视角来看,这种平衡还涉及缓存命中率与内存容量之间的非线性关系。根据Zipf定律,互联网上域名的访问频率遵循幂律分布——少量热门域名(如google.com、facebook.com)占据了绝大部分查询流量,而长尾域名虽然数量庞大,单个域名的查询频率极低。这意味着缓存命中率与缓存容量之间存在一条典型的边际递减曲线:最初增加缓存容量会带来命中率的显著提升,但超过某个拐点后,继续增加容量的边际收益急剧下降,因为新增的容量主要用于缓存那些极少被再次查询的长尾域名。Cloudflare的优化通过降低每条目的内存占用,在相同的物理内存约束下可以容纳更多的缓存条目,间接提升了对长尾域名的缓存覆盖率,实现了内存效率和缓存效果的双赢。
对分布式系统开发团队的借鉴意义
随着云服务、边缘计算和大规模分布式系统的普及,越来越多的技术团队将面临类似的规模化挑战。Cloudflare的这次实践提供了一个宝贵的范例:
- 重视底层数据结构的内存布局,这是很多高级语言开发者容易忽视的领域。在Java、Go等带有垃圾回收机制的语言中,对象头、指针压缩、内存分配器的碎片化等问题同样会造成显著的内存开销,值得系统级开发者深入理解。以Java为例,HotSpot JVM中每个对象都携带12-16字节的对象头(包含Mark Word和类指针),一个仅包含单个int字段的Integer对象实际占用16字节内存,其中有效数据仅4字节,开销率达75%。Go语言虽然没有对象头的问题,但其切片(slice)头部固定占用24字节(指针+长度+容量),对于存储大量小型切片的场景同样存在显著的元数据开销。
- 建立完善的度量体系,只有精确测量了内存消耗的构成,才能有针对性地优化。这包括使用内存分析工具(如Valgrind的Massif、jemalloc的统计功能、Linux的
/proc/[pid]/smaps)进行精细化的内存剖析,区分有效数据、元数据、碎片和填充各自的占比。Massif工具特别适合此类分析,它能够生成详细的堆内存分配时间线和调用栈快照,帮助工程师精确识别哪些数据结构是内存消耗的主要来源。对于Rust或C++编写的系统,还可以使用自定义分配器(custom allocator)在运行时统计每种对象类型的分配次数和总内存占用。 - 善用规模效应思维,将局部的微小改进放在全局规模下评估其真实价值。一个节省8字节的结构体改动,在10亿条目×300个数据中心的规模下,就是2.4TB的内存节省。
结语
Cloudflare通过优化1.1.1.1的DNS缓存节省100TB内存的案例,是一次典型的超大规模系统工程实践。它没有依赖什么颠覆性的技术,而是通过对数据结构和内存布局的精细打磨,在庞大的运行规模下实现了惊人的资源节约。
这个故事最有价值的启示或许是:在超大规模系统中,工程师对细节的极致追求,最终会转化为可观的商业价值和更优秀的用户体验。对于所有从事基础设施建设的技术团队来说,这都是一堂值得深思的课。
核心要点
相关推荐

同款模型三大AI Agent横评:DeepSeek Harness、ZCode与Hermes谁更强
同一个GLM Flash模型、相同提示词,分别放进DeepSeek Harness、ZCode和Hermes三大AI Agent中横评对比。实测显示Agent框架差异显著,速度、代码量与最终效果各有高低,DeepSeek Harness综合表现最佳。

DeepSeek扒谱时喊"困了"?聊聊LLM思维链里的拟人化现象
B站UP主发现DeepSeek在扒谱、BPM识别任务的思维链中出现"困了""想睡觉"等拟人化表达。本文解析LLM为何会模仿疲劳、思维链如何放大拟人化现象,以及如何应对模型输出跑偏。

DeepSeek Harness实战改造:打造低成本AI编程神器
国外技术UP主分享如何改造DeepSeek官方harness,用Claude Code驾驭开源框架,配合Bright Data数据抓取与视觉模型,打造成本仅半分钱的AI编程工作流,实测比Claude Code更便宜。