{"id":"mb-20260827-ea4977","account":"mubei","brand":"","title":"在 2500 亿条全局缓存的尺度下，结构体里每多留一个无用字段，代价是 15000 GB 物理内存","summary":"在 2500 亿条全局缓存的尺度下，结构体里每多留一个无用字段，代价是 15000 GB 物理内存","body":"在 2500 亿条全局缓存的尺度下，结构体里每多留一个无用字段，代价是 15000 GB 物理内存。\n每条缓存只要浪费一个字节，乘以全网基数，就是 250 GB 内存凭空蒸发。\n现代编程语言总在强调灵活、动态、可扩展。\n但当海量数据一旦写入就永远不可变时，那些为了防御未知扩容而预留的抽象，到底在暗中吞噬多少硬件账单？\nCloudflare 负责承载 1.1.1.1 的 DNS 解析引擎 Big Pineapple，在全球同时维持着 2500 亿条缓存。\n工程师在底层 Rust 内存布局上连砍 5 刀，把单条缓存从 953 字节压到 420 字节。\n全网直接抠出 100 TB 内存，相当于 130 台顶配 Gen 13 服务器的物理内存总和。\n更反直觉的是：内存砍掉 56%，写入吞吐反而暴涨 43%，读取延迟下降 19%。\n这台让事件得以发生的机器，叫不可变数据的动态税。\n第一刀，砍掉容量字段。\n开发习惯随手使用的 Vec 和 String，在底层都由 3 个字段构成：指针、长度、容量，各占 8 字节。\n只要数据存入缓存，生命周期内就绝不再追加。\n那个用来防扩容溢出的容量字段完全是摆设，加上堆上过度预留的闲置空间，纯属白白占座。\n换成只含指针和长度的不可变切片 Box，单条缓存 8 个字段直接省下 64 字节。\n仅这一步，全网立刻抹掉 15 TB 内存。\n第二刀，消除多余指针。\n一条 DNS 响应包含应答、权威、附加三部分数据。\n传统设计会建 3 个独立列表，各背 16 字节的指针和长度。\n改造成一个连续大列表，每个分区的切分点改用 2 字节的 u16 偏移量定位。\n干掉 2 个独立列表，净省 28 字节，同时消除多余的内存对齐填充。\n第三刀，拆解枚举的对齐膨胀。\nRust 的枚举是标签联合体，整个结构体的大小，必须硬性对齐体积最大的那个变体。\n一个极其罕见的 NAPTR 记录因为字段繁多占了 136 字节，直接把整个记录枚举强行垫到 144 字节。\n而占全球 80% 以上流量的普通 A 记录只有 4 字节，AAAA 记录只有 16 字节。\n这意味着每一次最常规的解析，都要替那个几乎遇不到的冷门记录，平白背上 120 多字节的空白填充。\n把超大变体单独装箱丢到堆上，让核心枚举瞬间缩回正常体型。\n第四刀，剥离冗余的所有者域名。\n绝大多数解析记录的域名，和最初发起的查询键名完全一致。\n把原本每个记录都单独存一份的域名改成 Option，一致时设为 None，读取时直接复用现成键名，零堆分配。\n只有遇到 CNAME 这类跳转、域名发生变化时，才在堆上分配存储。\n第五刀，回归网络线格式。\n堆上分散的独立分配，会让内存分配器 jemalloc 产生尺寸对齐浪费，更会把数据打散在内存各处。\n最终把记录数据直接打包成紧凑的原生二进制线格式，通过全局复用的缓冲池写入。\n这不仅消除了内存碎片，更带来质的飞跃：\n由于主流记录已经是二进制格式，组装 DNS 响应时直接整块内存复制，跳过了逐字段序列化的 CPU 开销。\n计算机科学里有一条常识：时间换空间，空间换时间。\n但在现代硬件架构下，这条规律正在被改写。\nCPU 的计算速度极快，真正的瓶颈永远卡在数据在内存与核心之间搬运的延迟。\n当离散的指针被消除、多余的填充被剔除，数据紧凑地聚拢在连续内存中，CPU 缓存一行就能装下整条记录。\n减少内存占用的过程，恰恰消灭了 CPU 跨越堆空间去寻址的开销。\n最顶级的系统性能优化，往往不是去换更快的芯片。\n是在数据不可变的那一刻，果断剥掉现代编程语言强加给你的所有动态幻觉。","category":"科技","score":null,"translated_x_url":null,"translated_status_id":null,"published_at":"2026-08-27 21:12:42","created_at":"2026-08-27 21:12:42"}