用户登录

技术文章

外部链接将在新标签页打开。

DNS 区域从整区重建改为定向构建:影响面、验证顺序与回滚边界怎么定

一句话回答

整区重建的问题不在事件数量,而在于单次工作的成本随区域规模增长。Cloudflare 的做法是用定向构建替代整区重建:先由事件声明改了什么,再由 planner 选出可证明安全的最小构建,执行前重读最新状态,先写记录键再写共享元数据键,最后清理旧键。影响面由“变更可能触及的键集合”定义,验证的核心不变式是定向写入的每个键必须与全量构建逐字节一致,证明不了就退回全量。材料只给出了退化到全量与人工重建单个名称这两种兜底手段,未描述自动回滚流程。

核心要点 TL;DR
  • 影响面不等于“哪个字段变了”,而是“哪些 Quicksilver 键可能变”:记录键按域名分片,元数据键由所有同名区域共
  • 验证的第一顺位是不变式而非性能:定向构建写入的每个键必须与全量构建逐字节一致,无法证明就走全量。
  • 执行顺序有硬约束:重读最新发布视图、先写记录键、再写引用这些记录的元数据键、最后扫描并删除旧键。
  • 回滚边界在材料中只有两种:退化到全量构建,以及超阈值区域的人工单名重建;自动回滚未描述,待核实。

如果要在自己的系统里把“整区重建”换成“定向构建”,真正需要先定下来的不是性能目标,而是三件事:这次变更可能触及哪些键(影响面)、按什么顺序证明它安全(验证顺序)、证明不了时退回到哪里(回滚边界)。Cloudflare 在 2026 年 11 月 2 日的博客中描述了他们在 DNS 管道里做的这次改造,本文以该文为唯一事实来源,梳理可迁移的判断顺序,并标注哪些是原文事实、哪些是推断与建议。原文:How we halved DNS build delay and cut memory use by 85%

为什么整区重建会先撞上内存,而不是吞吐#

据该文描述,Zone Builder 在 2022 年每秒接收约 250 条 DNS 记录变更,现在约 700 条,另有约 100 条其他事件(如区域设置变更)。但作者明确写道,事件数量从来不是真正的问题,问题在于某些事件所需的工作类型,以及这类工作如何随区域规模增长:部分区域已超过 2000 万条记录,并预期向数亿条增长。

一次“构建”(build)指:读取当前数据库状态,转换成边缘使用的格式,再把变化写入 Quicksilver(Cloudflare 的分布式键值存储)。全量构建的步骤是读区域与设置、读全部记录、转成键、与 Quicksilver 中已有键比较、删除陈旧键、写入变更键、更新区域元数据。最终写入的虽然只有变化的键,但读取、编码、比较覆盖了每一条记录,并且整个过程要把整个区域放进内存。对最大的一批区域来说,区域根本放不进内存——这是容量问题,不是延迟问题。

影响面:从“哪个字段变了”到“哪些键可能变”#

原文指出,全量构建有一个好处:不需要知道区域为什么变。定向构建则必须回答三个问题:哪些 DNS 名称可能变化?共享元数据或已发布区域列表是否变化?Quicksilver 中哪些旧状态必须删除?

要回答这三问,先要分清 Quicksilver 输出的两部分:记录键按“区域 + 名称”存放 DNS 答案;共享元数据键较小,告诉权威服务器如何使用该区域,包括去哪里找名称服务器、通配符这类特殊记录。这个区分直接决定影响面:

  • 普通的 A、AAAA、TXT、CNAME 变更通常只动记录键(原文的 Resources only 类型)。
  • 名称服务器与通配符变更动记录键加元数据键(Resources and metadata)。
  • Universal SSL 这类设置可能改变 apex(裸域,如 example.com)上的 CAA 记录——CAA 用来告诉证书颁发机构是否可为该域签发证书——因此走 apex 加元数据(Apex and metadata)。
  • 还有两种:区域不再发布时的清除(Purge),以及无法证明安全时的全量(Full zone)。

原文举了一个容易漏掉的例子:同名域可能短暂存在多个内部区域对象(客户开通、关停及其他生命周期变更期间),元数据键用优先级策略选出一个小的、有界的集合发布。所以 www.example.com 上的一条 TXT 变更只影响对应区域的记录键,元数据键不变;但名称服务器变更会改变元数据键的内容,因为它记录的就是这类信息。

验证顺序:不变式在前,时间点在中间,写入顺序在后#

原文给出的核心规则只有一句,但它是整篇文章的支点:定向构建写入的每个键,必须与全量构建会写出的内容逐字节一致;无法证明,就做全量构建。围绕这条不变式,验证顺序可以拆成四步。

  1. 先证明等价,再谈省多少。事件必须说明改了什么:原文说他们改造了事件生产者,让它带上变更的字段与设置,并在可用时附带旧值与新值,然后逐个设置追踪生成 DNS 输出的代码路径并分类。没有这层信息,定向构建连“等价”都无法主张。
  2. 在执行前重读,而不是相信事件里的快照。事件描述的是过去;构建可能排队等待,期间数据库已经变化。原文写的是规划发生两次:事件先产生一份保守的工作清单,执行前再读最新的发布快照(原文此处被截断,调度细节未完整给出)。
  3. 元数据键整体重建,不打补丁。执行时重新读取当前区域列表;若需要写元数据键,就从这份最新视图把整个键重建出来,而不是在旧副本上修补。
  4. 先写记录,再写指向记录的元数据;最后清理。原文明确:先写触发区域的记录,再写引用这些记录的元数据。旧状态清理沿用 2022 年按记录构建的做法——记录事件包含被删除的记录和更新前的版本,Zone Builder 扫描 Quicksilver 中每个被重建名称下的现有键,删掉新构建没有写入的那些。

下面是一段未执行的示意伪代码,只用于说明判断次序,字段名与阈值均为占位,不代表原文实现:

# 未执行的示例:示意判断次序,非原文实现
def plan(event, zone):
    # 1. 事件说不清、或可能影响区内任意记录 → 全量
    if event.detail_unknown or event.may_touch_any_record(zone):
        return FullZone()
    # 2. 超过记录数阈值的区域永不升级为全量(原文的刻意例外)
    if zone.record_count > FULL_BUILD_LIMIT:   # 具体阈值:待核实
        return ApexMetadataAnd(event.names)
    # 3. 其余按“只动记录键 / 动记录键与元数据键 / 动 apex 与元数据键”选最小构建
    return smallest_provable_build(event)

回滚边界:材料只给了两种兜底#

这里需要明确边界:提供的材料没有描述自动回滚机制,既不包含回滚触发条件,也不包含回滚后的验证方式。文中能确认的兜底手段只有两类。

第一类是保守退化:缺少细节、未知事件、仅更新时间戳、以及可能影响区域内任意记录的变化,都仍然走全量构建。原文对仅更新时间戳的情况写得很直白:有些数据库函数只是更新一个时间戳,意思是“这个区域有东西变了”,它有时是无害的记录,有时意味着别处数据已改必须重读,两者在事件里长得一样,所以在能识别调用方与目的之前,一律全量。作者的表述是:planner 绝不把不确定性变成更少的工作。

第二类是刻意的例外:全量构建要同时把数据库里的每条记录与 Quicksilver 中该区域的每个现有键放进内存,最大的一批区域放不下。因此超过一个固定记录数阈值后,Zone Builder 永不升级为全量,改为重建 apex、元数据,以及队列中事件标识出的每个名称。原文给出的理由是全量构建对这些区域根本无法完成,而在重试期间它会阻塞同一 Kafka 分区上的所有其他变更。原文同时说明,许多本需全量的情形涉及多个区域共享一个名称,而 zone holds(阻止同一名称被加入另一个账户)降低了这种风险;对剩下的罕见情形,例如在另一个网络启用某个区域,可以人工重建区域内的单个名称。

换句话说,回滚边界在这套设计里不是“出错后撤回”,而是事前把无法证明的变更挡在定向路径之外。如果你的系统需要事后回滚,那么回滚验证必须自己补,且要满足与正向构建相同的不变式。

落地清单:把这三件事写成可检查的条目#

以下是从原文结构推出的建议,不是原文结论:

  • 把“键空间”先画清楚:哪些键按名称分片、哪些键被同名对象共享。共享键是影响面判断的主要难点。
  • 要求事件携带变更字段、旧值与新值;做不到时,把这类事件显式登记为“必然全量”,而不是让 planner 猜。
  • 把等价性写成可执行断言:定向构建的输出与全量构建的输出做逐键字节比较,作为上线门槛。
  • 确认写入顺序:先写被引用的数据,再写引用它的元数据;共享键整体重建而非增量修补。
  • 为超大对象定义“永不升级为全量”的阈值与替代路径,并把人工重建作为明确记录在案的操作手段。

效果口径与尚待核实之处#

据该文自述,改造后区域设置变更不再重建拥有数千万条记录的区域,峰值内存下降 85%,全量构建减少 40%,并且因多出的余量得以缩短管道一直需要的等待时间,使 DNS 变更到数据写入 Quicksilver 的时间减半。这些是 Cloudflare 对其自身生产管道的描述;提供的片段未给出测量时间窗、区域规模分布与统计基期,具体口径待核实。

此外,以下内容在提供的材料中被截断或未展开,不应据此推断:调度器如何合并同一区域的工作、如何保证合并过程不丢 Kafka 消息、固定记录数阈值的具体数值,以及是否存在自动回滚流程。这些均待核实。

引用说明:本文事实均来自 Cloudflare 博客《How we halved DNS build delay and cut memory use by 85%》(来源日期 2026 年 11 月 2 日)。写作时所获为抓取片段且存在截断,未阅读全文;凡材料未覆盖之处已标注待核实,未作补写。

常见问题(FAQ)

定向构建失败时能自动回滚吗?
提供的材料没有描述自动回滚机制。文中给出的兜底是:无法证明安全的变更直接走全量构建;对超过记录数阈值、全量构建无法完成的区域,改为重建 apex、元数据与事件涉及的所有名称,必要时人工重建单个名称。是否另有回滚流程,待核实。
为什么不干脆所有变更都走定向构建?
因为定向构建必须先证明“写入的键与全量构建逐字节一致”。仅更新时间戳、缺少细节、未知类型或可能影响区域内任意记录的变更,都无法完成这个证明。材料明确写明:planner 不会把不确定性转换成更少的工作。
同名多个区域对象是怎么处理的?
记录键按内部区域分别存储,但元数据键由所有同名区域共享,作用类似目录,告诉权威服务器可以用哪些内部区域 ID 应答。因此名称服务器和通配符类变更必须同时更新记录键与元数据键,且元数据键按执行时的最新视图整体重建,不打补丁。

参考来源

  1. How we halved DNS build delay and cut memory use by 85%

以上为本文写作时的参考资料,引用观点以原文为准。

作者:沐小悠

本文约 10 分钟读完 · 关键词:DNS 定向构建、Zone Builder、Quicksilver、变更影响面、回滚边界

更多文章 GET 示例接口

「」标签下的其他文章