你打开这篇文章之前,手机在后台已经问过好几次"这个网址对应哪台机器"。这一问一答就是DNS,网页加载的第一步,平时你完全看不见它。全球有相当一部分这类问询,落在一个叫1.1.1.1的地址上——Cloudflare在2018年4月1日上线的公共DNS服务,现在平均每天扛1.9万亿次查询。

8月27日,Cloudflare发了篇工程博客,说他们把1.1.1.1的缓存内存砍掉了一半多,全网腾出大约100TB。这篇当天上了Hacker News头版,400多分、110多条评论。

标题挂着100TB,猜法无非那么几种。最顺手的是压缩,觉得他们上了个更狠的压缩算法。也有人往运维方向想,加机器,或者把缓存淘汰得更勤一点。还有一种属于常识层面——省内存总得拿速度去换。这几种这次全落空了,最后那种落得最彻底。

没有压缩,也没有新机器

5处改动,从头到尾没有压缩这回事。机器数量没动,缓存能装多少条也没动,还是2500亿条。改的只有一件事:同一份数据在内存里怎么摆。

2500亿条是这整件事的杠杆。原文开篇给了个换算——每条多浪费1个字节,全网就多烧掉250GB内存。这个我自己按2500亿乘1字节验了一遍,正好是2500亿字节,250GB,对得上。所以在这个体量上,字节不是计量单位,是汇率。

按最大那件行李定箱子

5处改动里最有意思的是第4处,跟枚举有关。枚举是程序里一种数据格式,意思是"这块地方可能装A,也可能装B,具体装哪个另有个标记说明"。DNS的记录数据用它来存很自然:A记录装IPv4地址,AAAA记录装IPv6地址,别的类型装别的。

问题出在尺寸上。这种格式的大小,永远按里面最能装的那个变体算——像给全班每个人都发一个行李箱,箱子尺寸按班上行李最大的那位定,哪怕大多数人只带一支牙刷。

在1.1.1.1这边,行李最大的那位是NAPTR,一种很少见的记录类型,136字节。加上标记和对齐填充,整个枚举撑到144字节。而一个A记录真正需要的只有4字节,AAAA是16字节,这两种加起来占了八成以上的流量。也就是说,绝大多数记录白白背着120多字节的空箱子。

这条我想自己验一下。先查本机有没有Rust工具链,which rustc和which cargo两个都是空的,没装,所以原文那套代码我跑不了。退一步,我用系统自带的clang写了个C版本——C的联合体和Rust枚举在内存布局上是一回事。照原文的描述把最大变体建模成136字节(里面有指针,按8字节对齐),小的两个分别是4字节和16字节。编译跑出来:不装箱144字节,装箱后24字节,每条记录差120字节。跟原文给的三个数一个不差。

这里得把边界说清楚:我建模的是布局规则,不是Cloudflare真实的NAPTR字段表。136这个数是我照原文那句描述(三个变长文本、一个域名、两个整数)配出来的,能对上不代表字段一样。

所谓装箱,就是把大块数据挪到内存别处另存,原地只留一个8字节的地址,用的时候顺着地址去取。小的常用类型继续留在原地,大的挪走。空箱子问题就这么解决了。

但这一步自己也带着账。数据散在各处,读的时候CPU得多跑一趟;内存分配器又按尺寸分档,一条40字节的记录会被凑到48字节,白扔8字节。有意思的地方在这儿:第5处改动干脆把记录数据整个按原始字节连着存成一整块,等于把第4处刚建立起来的装箱又收了回去。5处改动不是叠着往上加的,后面那处推翻了前面那处的一半。

我自己把账算了一遍

官方给了个总数,大约100TB。我想知道用文中逐项的节省数字能不能凑出来。先说口径,下面的TB都按1TB等于1000GB算。

第1处改动,把会自动扩容的数组换成定长的,每条缓存条目省64字节,乘2500亿是16TB。原文写的是"超过15TB",对得上。第2处把三个记录列表并成一个、用偏移量标位置,每条省28字节,是7TB。这两处加起来23TB,离100TB还远。

大头在装箱那处。原文只给了每条记录省120字节,没给每条缓存条目平均能省多少。基准测试的口径写着每条条目装1到4条记录,我取中间值两条半,A和AAAA按测试分布占81%,算下来每条条目省243字节,全网六十来TB。

这个数是我估的,误差几乎全在"平均几条记录"上。要是实际均值是2,就掉到不到49TB。所以它只能当量级看。

三项凑起来八十几TB,剩下的靠省掉记录归属域名那处、以及最后改存原始字节那处补上。方向是对的。

换个算法再验一次更干脆。原文最后给了每条缓存条目的净占用,从953字节降到420字节,省533字节。533乘2500亿,是133TB。

这就比官方公布的100TB多出33TB了。差在哪,原文自己交代过两处:基准测试里每条省下的字节,不等于生产环境常驻内存降下来的量,因为一个进程里还有缓存以外的一堆东西;各个机房的缓存装满程度也不一样,有的还没填满。换句话说,100TB这个数是往保守里报的,纸面上的算术账比它更好看。

速度这笔账反着走

最该被驳掉的是"省内存必然牺牲速度"这条。原文直接给了反证:插入吞吐从每秒62万多条涨到89万多条,快了四成多;查询延迟从828纳秒降到670纳秒,降了近两成。

原因不神秘。省内存用的手段,本身就是减少内存里的零碎分配、让相关的数据挨着放。CPU读内存不是一个字节一个字节读的,是整块搬进片上缓存。数据挨着,一次就搬到位;散着,就得来回跑。省和快在这件事上是同一个动作的两面,不是取舍。单实例的常驻内存也跟着降了,p99从9GB出头掉到5GB出头。

评论区吵的不是技术

我把Hacker News那条讨论的评论全拉下来翻了一遍,最热的一支根本不在技术细节上,而在追问当初为什么没做对:一份存进去就再也不改的缓存,为什么要用一个专门为"以后还要往里加东西"准备的数据结构?当年设计评审的时候没人提吗?

Cloudflare的CTO约翰·格雷厄姆-卡明本人下场回了这条。他的意思是,过早优化真正的代价是工期:早期目标是把东西做对、尽快上线,在不缺内存的时候抠内存就是浪费时间,而且那会儿你也判断不出日后真正该优化的是哪一块。底下有人替他补了笔账,说130台服务器的钱对当年的Cloudflare算不上什么,为这个耽误1.1.1.1上线不划算。

真正有技术含量的挑刺来自一个写C的人:还有一处优化没做——把记录数据直接接在缓存条目后面存,连那次单独的内存分配都省掉,C里这是老手艺。回帖解释了为什么没做,Rust的哈希表要求存进去的值大小固定,长度不定的东西塞不进去。还有人说这类抠字节的活儿另一门语言更趁手,Cloudflare最近也确实在用。这条我没去核实,放这儿只当一句风向。

100TB值多少钱

100TB内存,按1TB等于1024GB折,大概是10万GB出头,够装满六千多台16GB内存的笔记本。

折成钱要费点劲。我找到最硬的出处只到2025年9月:Counterpoint的数据被媒体引用过,三星把32GB的DDR5服务器内存模组从149美元提到了239美元,折每GB约7.5美元,按这个价,100TB的料钱大概77万美元。同一份报告说到2026年底,64GB的服务器内存会比2025年初翻一倍,按翻倍口径推,就是150万美元上下。这两年内存涨得太凶,这个区间只能当量级看,别当报价。

顺手对了一下:评论区那位按130台整机估的是260万美元,跟我这个只算内存条的区间在同一个数量级,两边能互相印证。

乘数决定值不值得抠

这件事能带走的不是省钱的技巧,是那个乘数。

2500亿这个体量下,1个字节等于250GB。同一个决定,在你手上跑几万次,那点浪费永远浮不出水面;跑几千亿次,单次的浪费就不再是浪费,是乘法。所以判断该不该抠,看的从来不是这段代码丑不丑,是它后面挂着几个零。

这也正是格雷厄姆-卡明那句话的另一面。当年不抠是对的,因为乘数还没长出来;今天抠也是对的,因为它长出来了。中间隔的不是技术水平,是7年的量级变化。

回到那100TB

这100TB腾出来,Cloudflare说了不打算让它闲着,要拿去多装缓存条目。缓存装得多,命中率就高,往上游服务器转发的查询就少。所以它最后大概率不会变成账单上省下的一笔钱。

它会变成你下次打开一个网页时,少等的那一下。摊到单次查询上,可能只有几十纳秒,你根本感觉不到。但乘以每天1.9万亿次,就是开头那个数的另一种写法。

资料来源:Cloudflare工程博客《How we saved 100 terabytes of memory by optimizing 1.1.1.1's DNS cache》(2026-08-27,作者Sebastiaan Neuteboom)、Cloudflare官方公告《Announcing 1.1.1.1》(2018-04-01)、Cloudflare Radar博客公布的日均查询量、Hacker News相关讨论、Network World援引Counterpoint Research的服务器内存价格报道。

文中标注为本人测算的部分为独立估算,口径已在正文说明。