摘要

  • SOA 序列号不是时间戳。它把区域副本放进一个 32 位环形空间,让每台从服务器独立比较相对位置。
  • 普通整数比较会在回绕处出错。RFC 1982 规定,小的 0 可以跟在最大值之后;相距恰好半个空间的两个值则没有定义上的先后。
  • 这套规则依赖运营约束:仍在服务的副本必须在 EXPIRE 时限内保持在半圈比较窗口中。序列号只证明允许序列中的位置,不证明内容正确、编辑者获授权或所有副本已经收敛。

最大值之后为什么不是终点

设想一台从服务器保存的 SOA 序列号是 4,294,967,295,主服务器刚发布 0。按普通无符号整数排序,前者更大;按 DNS 序列号运算,0 可以是下一代。

固定宽度的计数器迟早会用完。若最大值只能表示终点,长期运行的区域最终不是冻结,就是被迫更换版本制度。允许回绕解决了寿命问题,却带来更难的问题:同一个数字空间被重复使用后,服务器如何知道前进方向?

DNS 没有要求所有服务器相信同一座钟,也没有安排一个机构宣布“哪个版本最新”。它把共同要求压缩成一段每个参与者都能本地执行的确定性规则。服务器只需比较自己看到的两个值,而不必请求许可。

1987 年留下的一句未完成说明

RFC 1034规定,区域修改由主服务器协调,从服务器定期查询 SOA。每次改动都要推进 SERIAL;从服务器比较两份副本,在主服务器版本更新时取得区域。REFRESH、RETRY 与 EXPIRE 分别约束正常检查、失败重试和无法确认新鲜度时停止权威服务的期限。

RFC 1035把 SERIAL 定义为原始区域副本的 32 位无符号版本号。区域传送保留这个值;计数会回绕;比较要使用“序列空间运算”。

问题是,最初两份规范并没有给出 DNS 所需的完整比较定义。它们借用了传输协议序列号的直觉,却没有说明所有边界对该如何排序。不同实现可能都知道计数器会归零,却在归零附近得出相反结论。

一个看似很小的定义空白,决定的却是哪份权威数据可以替换哪份数据。把“更新”留给习惯理解,等于把互操作性押在实现者彼此猜对上。

环上唯一没有赢家的直径

1996 年 8 月发布的 RFC 1982补上了定义,并更新 RFC 1034 与 RFC 1035。DNS 的序列空间从 0 到 4,294,967,295。加法按 2^32 取模,但一次有定义的最大增量只能是 2^31−1。

比较时,不看十进制数谁更大,而看环上的短方向。两个不同值若相距不到半圈,沿前进短路径到达的那个值更新。因此 3 可以比 4,294,967,294 更新。

恰好相距 2^31 时,两个方向一样长。若硬选一边,同样给两数加一后,先后关系可能反转。规范因此不制造答案:两值不相等,但谁大谁小都没有定义。

这说明 SOA 序列号并不是覆盖所有历史值的全序。它只在活跃版本受控的窗口内提供偏序。规范公开承认信息不足的地方,要求运营者不要让正常系统落到那里。

EXPIRE 是运算规则的一部分

环形比较成立的前提,是旧副本在计数器推进太远之前退出。RFC 1982 警告,在一个 SOA EXPIRE 周期内,无论一次还是多次累计,都不应把序列号推进超过 2,147,483,647。

如果一台从服务器长期离线,主服务器在它回来前走过半圈以上,旧副本可能被算法判断为“更新”。这不是比较器突然失灵,而是运营系统破坏了比较器的适用条件。

DNS 以一个有边界的活性承诺替代全局时钟。参与者不必同意现在几点,但必须让版本推进速度、失联期限和权威撤回彼此匹配。无法在期限内确认主服务器的从服务器,应停止把旧副本当作权威。

共同层因而很薄,却并非没有纪律。确定性规则只能在参与者维护其前提时给出确定答案。

一个写得太大的数字为何不能直接改小

现实事故通常不是自然走完四十多亿次修改,而是有人误写了一个过大的序列号,发布后又想把它改回“正确”数字。

RFC 2182明确警告,不要简单递减。从服务器已经见过大值,会把较小的修正视为旧版本,并可能继续忽略后续正常递增。恢复必须沿有定义的前进方向分阶段推进,每一步都等所有从服务器跟上,再进行下一步。

这种修复显得反直觉,因为数据已经成为分布式事实。修改主文件只能改变主服务器的意图,不能删除其他机器已经观察并用于决策的证据。

这也是可逆性的真实含义。回滚不是把本地文件恢复成昨日内容,而是建立一条从每个现存状态都能验证的新路径。

长得像日期,仍然不是时钟

RFC 1912记录了十进制写法转换产生意外结果的问题,并建议使用 YYYYMMDDnn。例如 2026082201 很容易被人读成 2026 年 8 月 22 日的第一次修改。

但线上只有一个 32 位整数。从服务器不会核验日期、时区、操作员身份或真实提交时间。错误时钟可以制造“未来日期”,两个自动化流程可以重用同一个值,简单计数器也完全可以合法工作。

日期格式是运营界面,环形比较才是互操作规则。可读性有价值,但不能被抬高成协议从未承载的时间证明。

NOTIFY 与 IXFR 消费的只是位置证据

序列号会触发其他机制,却不会替它们完成工作。RFC 1996规定的 NOTIFY 可以让从服务器提前醒来检查 SOA。通知本身不决定新旧,更不直接安装区域。

RFC 1995让客户端在 IXFR 请求中带上当前副本的序列号。服务器若保留了相应历史,可以返回从该版本到当前版本的删除与新增序列;没有共同起点时,则退回完整传送。

序列号表达的仅是“我的副本位于 X”。它不包含差异,不保证传送完整,不证明内容正确,也不替接收方作出上线决定。把这些声明拆开,才避免一个小字段获得超出观察能力的权力。

没有共同裁判的共同顺序

RFC 1982 的历史价值,在于把日常版本判断变成了可重复的本地计算。任何实现都能运行同一规则,无需了解区域属于谁、由哪个国家运营、谁签了合同或编辑发生在几点。

这种最小化也限制了解释。Y 在允许序列中跟在 X 后面,不代表 Y 真实、合法、安全。一个错误地址完全可以拥有完美递增的序列号。

互联网的共同规则不必回答所有问题。它应只回答互操作必须共同回答、且能被参与者自行验证的问题。连半圈边界都公开说“不知道”,反而是对权力边界最诚实的设计。

来源与证据边界

RFC 1034 与 RFC 1035 建立原始机制;RFC 1912 记录运营错误与日期格式;RFC 1982 定义序列号运算;RFC 2182 说明从服务器运维和错误恢复;RFC 1995、RFC 1996 展示 IXFR 与 NOTIFY 如何使用序列号。

这些文档不提供统一部署日期,也不能证明所有实现都正确处理过零值。把它理解为“以本地确定性顺序替代中央时钟”是对机制的架构推论。序列号不证明所有权、编辑者身份、内容真实性或全球收敛。