系统设计 / DNS · 层级、委任、缓存,以及不存在的「浸透」 / 名字解析:从根走到答案 待审核 4 / 11
反复检索 · priming · 缓存

名字解析:从根走到答案

前两页讲的是静态的一半:数据长什么样、放在哪里。这一页讲动态的一半——一个名字怎么变成答案。

1 · 解析链上的角色与检索方式

「帮我把名字变成地址」这句话在不同环节上含义不同,而中文语境里两个关键术语常被混称:

定义 1.1(递归检索要求) 带 RD=1 的查询,含义是一次委托:请对方把整件事办完,只给我最终答案。只有全服务解析器接受这种请求。

定义 1.2(反复检索) 带 RD=0 的查询序列,是被委托方实际干的活:自根出发逐台询问,每台只回答自己那一层知道的事,不替提问方继续。权威服务器只接受这一种。

「递归」指的是委托那一步,不是自根向下那一串。

图 1-1 · 解析链上的四个角色与两种检索。可切换阶段观察哪些角色参与其中;转发器那一层可有可无,却是排查时最容易被忽略的。

2 · 一次完整的反复检索

冷缓存下解析 mail.vega.dev 要三次往返:问根得到 dev 的委任,问 dev 得到 vega.dev 的委任,问 vega.dev 得到权威应答。每一步的应答里,AA 位与三个段的内容共同表达了「这是最终答案」还是「该去问别人」。

图 2-1 · 反复检索的轨迹与缓存状态。可连续解析不同名字观察往返次数从 3 降到 1 再降到 0,用「+1 小时」推进时钟看缓存逐层过期,并可开关 QNAME minimisation 对照。

沿途每一步的结果都进缓存,且各有各的 TTL。这一点是理解第 9 讲那笔账的关键:缓存不是「一个域名的答案」这样一个整体,而是一串独立到期的条目——根的委任、TLD 的委任、目标 zone 的委任、最终那条记录,四者的 TTL 通常差着两三个数量级。

3 · 五种应答

从递归服务器的视角看,一次查询的结果只有五种可能。这个分类是 RFC 1034 §4.3.2 解析算法的骨架:

权威应答(AA=1,ANSWER 非空)——拿到了,缓存并返回。

委任(AA=0,AUTHORITY 放子 zone 的 NS)——该名字已交给别人,换服务器继续。

CNAME——这个名字的数据在别处,换名字重新解析。第 2 讲已说明它不得与其他类型共存。

NODATA(NOERROR,ANSWER 空,AUTHORITY 放 SOA)——名字存在,但没有所问的类型。

NXDOMAIN——名字整个不存在。

后两种合称否定应答,区别很实在:NXDOMAIN 意味着该名字之下的一切都不必再问,NODATA 只说明这一个类型没有。两者在报文里的差别仅在 rcode——ANSWER 段都是空的,只看有没有记录区分不出来。

注 · rcode 与这五种应答不是一一对应的。REFUSED(rcode 5)表示对方不愿意作答,通常是问错了对象——拿权威服务器当递归服务器用,或者拿递归服务器去问它管辖外的名字。SERVFAIL(rcode 2)在 DNSSEC 之后含义变重了:验证失败也归入这一档,于是「签名过期」的表现是 zone 从解析器视角整体消失,而不是降级为不可信(见第 7 讲)。

4 · priming:怎么找到根

反复检索从根开始,可根服务器的地址本身也是 DNS 数据。这是个自举问题,解法是给递归服务器预置一份根提示文件(root hints,named.root)——一份纯文本的根服务器名与地址清单。

启动时它做一次 priming query(RFC 9609):向提示文件里随机一台服务器查询 . NS,用得到的应答替换掉手里那份。所以提示文件不需要精确,只需要至少一条还能用;之后的正确性由 priming 保证。

警示 · 根提示不是信任锚。它只解决「先问谁」,不解决「答案是否可信」——后者要 DNSSEC 的根信任锚,那是另一份数据,也是唯一必须由带外途径(操作系统包、IANA 网页)获得并人工核对的东西。把两者混为一谈会得出「更新了根提示文件就更新了信任锚」这种错误结论。

5 · 根的冗余与 anycast

根有 13 个字母标识(a 到 m),由 12 家独立组织运营——不是 13 家,a 与 j 同属一家。13 这个数字是历史约束:早期要求根的 NS 应答能装进一个 512 字节的 UDP 报文,而 13 组名字加 IPv4 glue 刚好装满。如今再加上 AAAA 就装不下了——实测 dig +tcp . NS @198.41.0.4 得到 811 字节(13 条 ANSWER、27 条 ADDITIONAL,核对于 2026-08)。EDNS(0)(第 5 讲)之后这条限制早已不成立,但字母数没有再改。

真正提供冗余的不是这 13 个标识,而是 anycast:同一个 IP 地址被宣告在全球许多个物理位置,路由把提问方送到网络上最近的那一个。核对于 2026-08,root-servers.org 列出 2003 个运行中的实例。

这个数字与「13」之间的落差,说明了两件容易混淆的事:13 是协议层的标识数,2003 是部署层的实例数。宣称「全世界只有 13 台根服务器,打掉就完了」的说法混淆了这两层。

6 · 一处实测

写本页那个模拟器时,测试断言过「委任 NS 的 TTL 一到就得退回根」,实测不成立——退回的是上一级,不是根。原因见第 3 讲末节:各级委任的 TTL 相差很大,本系列 fixture 里 dev 的委任是 172800 秒而 vega.dev 的是 10800 秒,后者过期时前者还剩得多。

同一处还纠正了一个更基本的错误。模拟器最初只把「严格意义的 glue」(in-domain,即 NS 落在被委任子树内的那些)放进委任应答的 ADDITIONAL 段,于是模拟出的根不给 TLD 服务器的地址,解析在第一步就卡住——而真机 dig . NS 与查任何 TLD 的输出里,ADDITIONAL 段都是满的。改成「手上有地址就附上」之后才与真机吻合。RFC 9499 §7 把这两类分别叫 in-domain glue 与 sibling domain glue(in-bailiwick 一词该 RFC 已标为历史说法)。RFC 9471(2023)之后这不再只是惯例:委任应答对 in-domain glue 是 MUST 带全(装不下则置 TC=1),对含 sibling 在内的其余可用 glue 是 SHOULD 带全。

7 · 参考文献

  1. Mockapetris, P. (1987). Domain Names — Concepts and Facilities (RFC 1034). IETF. §4.3.2 是解析算法,五种应答的分类源自该节。
  2. Koch, P., Larson, M., & Hoffman, P. (2017). Initializing a DNS Resolver with Priming Queries (RFC 9609). IETF.
  3. Andrews, M. (1998). Negative Caching of DNS Queries (DNS NCACHE) (RFC 2308). IETF. §2 区分 NXDOMAIN 与 NODATA。
  4. Hoffman, P., & Fujiwara, K. (2024). DNS Terminology (RFC 9499, BCP 219). IETF. §7 是 glue 与 sibling glue 的定义。
  5. Root Server Technical Operations Association. (2026). Root server instances. https://root-servers.org/(实例数核对于 2026-08。)