DNS 区域按需重建:把全量构建改成定向构建的工程拆解
Cloudflare 把 DNS 区域构建从全量重建改为定向构建:先让事件携带变更语义,再用五种构建类型选出可证明安全的最小范围,以“与全量构建字节一致”作为正确性判据,并对超大 zone 保留“永不升级全量”的例外。
- 全量构建的真正成本在读取、编码与比较整区记录,最终写入本来就只含变更键。
- 定向构建的安全边界是一条硬断言:写入的每个键必须与全量构建字节一致,证明不了就退回全量。
- 超过固定记录数阈值的 zone 不再升级全量构建,改为重建 apex、metadata 和事件涉及的每个 name。
Cloudflare 在 2026 年 11 月 2 日的博客里给出了一套答案:不再让每种变更各写一个特例,而是先补齐事件语义,再让 planner(构建规划器)只在“能证明最小构建与全量构建输出完全一致”时才缩小范围,证明不了就退回全量。整套设计的重心不是少写几个键,而是把“安全”变成一条可检验的断言。以下机制与数字均来自该文,统计口径未完整披露的部分标注待核实。
全量重建的成本在读取与比较,不在写入#
先看变更怎么到达边缘。按原文的简化视图,链路是:
- 客户通过 DNS Records API、控制台或其他 Cloudflare 服务修改记录或区域设置;
- 变更写入数据库,由数据库变更或其他系统产生 Kafka 事件;
- DNS Kafka Aggregator 合并多个 topic 的事件,投递给 Zone Builder;
- Zone Builder 读取所需区域数据,转换成权威 DNS 服务器认识的键;
- 与分布式键值存储 Quicksilver 比对,只写变化的部分,再由 Quicksilver 传播到全网。
旧的 Zone Builder 有一个好处:不需要知道“为什么变”。它读区域和设置、读全部记录、编码成键、与现有键比对、删除陈旧键、写入变化键、更新区域元数据。最后一步写入的确实只有变化键,但前面几步仍然要把整区记录读一遍、编码一遍、比对一遍,并在此过程中把整个区域放进内存。小区域无所谓,原文提到已有区域超过 2000 万条记录,并预期向数亿条增长——这种规模根本放不进内存。
负载背景也值得记下:2022 年 Zone Builder 每秒接收约 250 条 DNS 记录变更,文章发布时约 700 条,另有约 100 条其他事件(例如区域设置变更)。原文的判断是,事件数量从来不是真正的问题,问题在于其中一部分事件所需的工作量会随区域规模增长。这也是“定向构建”要解决的对象。
区域设置为什么比记录变更难做定向#
记录事件能指出改了哪条记录;区域设置事件可能来自多个产品,不说明变更原因,还可能改动同名多个区域共享的数据。原文以 Universal SSL 为例:启用或禁用它会更新元数据,并可能改变区域 apex(裸域,如 example.com)上的 CAA 记录(Certification Authority Authorization,告诉证书颁发机构是否可以为该域签发证书)。
于是定向构建必须先回答三个问题:哪些 DNS 名称可能变化?共享元数据或已发布区域列表是否变化?Quicksilver 中哪些旧状态必须删除?原文把后续工作拆成五个问题,依次是:事件要说清变了什么;每个变更要映射到仍然正确的最小构建;构建要顾及同名区域共享的数据;事件与构建之间数据库可能已经变化;合并工作不能丢 Kafka 消息。
第一步:让事件携带变更语义#
做法是改事件生产者,让事件带上发生变更的区域字段与设置,并在可得时附带旧值和新值;然后沿着“设置 → 生成 DNS 输出”的代码路径逐个追踪并分类影响范围。
并非所有事件都这么具体。有些数据库函数只更新一个时间戳,含义相当于“这个区域有东西变了”。这有时是无害的记账,有时意味着别处的数据变了、必须重读。在事件里这两种情况长得一样,所以在能识别调用方和目的之前,只有时间戳的更新仍然触发全量构建。缺失细节、未知事件、以及可能影响区域内任意记录的变化,同样走全量。原文的措辞很直接:planner 绝不把不确定性转化为更少的工作。
第二步:五种构建类型与一条硬断言#
Quicksilver 的输出在这里分成两部分:record keys 承载某个区域、某个名称的 DNS 答案;较小的共享 metadata key 告诉权威服务器如何使用该区域,包括去哪里找 nameserver、wildcard 这类特殊记录。有的变更只动 record keys,有的必须两者都动。
| 构建类型 | 重建范围 | 原文给出的典型触发 |
|---|---|---|
| Resources only | 指定 DNS 名称(resources)的记录,不动区域元数据 | 普通 A、AAAA、TXT、CNAME 变更 |
| Resources and metadata | 指定名称加共享元数据 | nameserver、wildcard 变更 |
| Apex and metadata | 区域 apex 加元数据 | Universal SSL 一类设置 |
| Purge | 移除不应再发布的区域 | 区域下线 |
| Full zone | 全部记录 | 无法证明更小构建安全时 |
正确性判据只有一句:定向构建写入的每个键,必须与全量构建会写出的内容逐字节一致(byte-for-byte)。证明不了,就做全量。
删除旧状态的思路沿用 2022 年的按记录构建:记录事件包含被删除的记录和更新记录的旧版本;Zone Builder 扫描 Quicksilver 中每个待重建名称下已有的键,删掉新构建没有写入的那些。需要全量的情形仍然存在——变更可能影响区域内任意记录,或者区域可能需要首次发布。
第三步:同名多区域共享的 metadata#
Quicksilver 把每个区域的 record keys 分开存,但同一个域名下所有 Cloudflare 区域共用一个 metadata key,它像目录,告诉权威服务器查询时可以使用哪些内部 zone ID。为什么一个域名会有多个 zone ID?原文解释:在客户接入、下线及其他生命周期变化中,同一公开域名可能短暂存在多个 Cloudflare 区域对象,系统用优先级策略选出一个小的、有界的集合来发布。
原文示例:example.com 有 Zone 101、202、303、404 四个内部区域,metadata 目前列出 101、202、303,404 存在但未被选中;这些编号是彼此独立的区域对象,客户看到的仍是一个域名。此时对 Zone 202 中 www.example.com 的 TXT 记录做变更,只影响 Zone 202 的 record keys,metadata 不变;nameserver 与 wildcard 变更不同,因为 metadata 记录了它们的信息,必须同时更新记录和 metadata key。
执行时还有两个顺序约束:Zone Builder 重新读取当前列表;如果要写 metadata key,就从这份新鲜视图整键重建,而不是在旧副本上打补丁;并且先写触发区域的记录,再写引用这些记录的 metadata。
第四步:事件是过去时,执行前再核对一次#
事件描述的是已经发生的事,而构建可能排队等待,期间新事件到达、数据库也已改变,所以规划发生两次:事件先产生一份保守的工作清单,执行前再读取一份新的发布快照。
可见材料在这里被截断。快照具体如何收敛工作清单、新调度器如何在不丢 Kafka 消息的前提下合并同一区域的工作,本次材料未提供细节,待核实;不应据此推断其实现方式。
大区域的有意例外:宁可多建几个名称,也不做全量#
全量构建会把数据库中的每条记录和该区域在 Quicksilver 中的每个已有键同时放进内存,最大的区域放不下。因此原文设了一个固定记录数阈值:超过阈值的区域,Zone Builder 永不升级为全量构建,改为重建 apex、metadata,以及队列中事件涉及的每个名称。
这是取舍而非等价替换。理由有两条:这些区域的全量构建根本无法完成;而在重试期间,它会阻塞同一 Kafka 分区上的所有其他变更。原文还提到,许多本来需要全量的场景涉及多个区域共用一个名称,zone holds 通过阻止同一名称被加入另一个账户来降低这种风险;剩余罕见情况(例如在另一个网络上启用区域)可以手动重建区域内的单个名称。
结果与可迁移的部分#
原文给出的结果是:全量构建减少 40%,峰值内存下降 85%,从 DNS 变更到数据写入 Quicksilver 的时间减半。需要注意因果链:内存余量让管道得以安全缩短“一直为韧性保留的等待时间”,延迟减半有一部分来自等待时间变短,而不全是构建本身变快。这三个数字的统计窗口、比较基期与测量方法在可见材料中未完整说明,引用时宜标注待核实。
对自建 DNS 或类似“派生数据”管道的开发者,这套拆解可迁移的部分是顺序而非具体类型:
- 建议先补事件语义,再谈调度优化。没有“变了什么”的事件,只能全量,任何合并策略都救不了。
- 建议把正确性写成可执行的断言(这里是与全量输出逐字节一致),并保留一条“证明不了就退回全量”的路径。
- 建议显式处理共享状态与写入顺序:按名称分片的数据和按域名共享的目录不是一回事,先写被引用者再写引用者。
- 建议为超大对象单设策略,并写清代价。原文的阈值策略牺牲了“单次构建覆盖全量”的性质,换来的是不再阻塞分区。
局限也要说清:这套设计依赖可枚举的“设置 → DNS 输出”影响关系、可靠的事件语义,以及一套比对校验。若你的系统三者都缺,优先级应是先做事件语义与输出比对,而不是先做工作合并。
来源:How we halved DNS build delay and cut memory use by 85%(Cloudflare Blog,2026-11-02)。本文依据该文可见部分整理,抓取内容有截断,涉及调度器细节与统计口径的结论待原文完整版核实。
常见问题(FAQ)
定向构建会不会漏删旧记录?
为什么同名域名的 metadata 要单独处理?
参考来源
以上为本文写作时的参考资料,引用观点以原文为准。