解析过程
这一课你将能做到
- 用自己的话区分「递归查询」和「迭代查询」,并指出客户端、递归解析器、权威服务器三者各用哪种方式
- 用
dig +trace完整跟踪一次www.coderoadmap.cn从根到权威的解析链路,逐跳解释 - 说清楚一次解析结果会经过哪几级缓存、分别受什么 TTL 控制,并动手清缓存验证
为什么要学
第 1 课我们建立了「DNS 是电话簿」的直觉,但真正回答「一次查询在网络上到底经历了什么」时,很多人只会背「先查根、再查顶级域、最后查权威」。背流程解决不了实际问题:为什么解析有时只要 2 毫秒、有时要 2 秒?为什么递归器问根时根不直接给答案?为什么 dig +trace 的输出和普通 dig 完全不是一回事?
理解递归与迭代的分工、缓存的层级和 TTL 的作用机制之后,你才能看懂第 10 课的故障现象——「解析超时」「首次访问慢、二次访问快」「改记录后全国生效时间不一样」全部源于这条链路。
先建立图像
① 客户端 ──递归查询──► 递归解析器(运营商/公共DNS,如 223.5.5.5)
"我不管过程,给我最终答案"
│
② │ 缓存有?─有─► 直接返回(最快,常见 1~20ms)
│ 没有 ↓
▼
③ 迭代查询(每一级只回答"我知道谁可能知道")
递归器 ──► 根服务器(.)
根回答:".cn 的事去问 a.dns.cn(附胶水IP)"
递归器 ──► .cn TLD 服务器
TLD回答:"coderoadmap.cn 的权威是 ns1.cloudflare.com"
递归器 ──► ns1.cloudflare.com(权威)
权威回答:"www.coderoadmap.cn A 203.0.113.10,TTL 300"
│
④ ▼
递归器缓存答案(300秒)──► 返回客户端 ──► 客户端也缓存
| 角色 | 它的职责 | 查询方式 |
|---|---|---|
| 客户端(浏览器/系统) | 发起一次查询,等最终结果 | 向递归器发递归查询 |
| 递归解析器 | 替你跑完全程,缓存结果 | 对各级权威发迭代查询 |
| 根 / TLD / 权威 | 只回答自己负责的那一层 | 被迭代询问 |
关键直觉:「递归」是对查询方的承诺(我帮你查到底),「迭代」是干活方式(我一层层自己去问)。一次完整解析里两者同时存在,只是发生在不同角色之间。
跟着做
步骤 1:普通查询,观察「递归器替你跑完」
dig www.coderoadmap.cn A
# Windows
nslookup www.coderoadmap.cn
看输出结尾的统计行:
;; Query time: 18 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
SERVER 是你实际使用的递归解析器(这里是家用路由器,它再转发给运营商)。你只发了一个包、收了一个答案——中间的跑腿过程全被递归器藏起来了。
步骤 2:用 +trace 把迭代过程显形
dig +trace www.coderoadmap.cn A
输出分四段,每段对应一跳(示例经过精简):
; <<>> DiG 9.18 <<>> +trace www.coderoadmap.cn A
. 518113 IN NS a.root-servers.net. # 第1段:本机预取的根 hint
;; Received 525 bytes from 192.168.1.1#53(UDP) in 8 ms
cn. 172800 IN NS a.dns.cn. # 第2段:问根,根指到 .cn
;; Received 722 bytes from 198.41.0.4#53(a.root-servers.net) in 32 ms
coderoadmap.cn. 172800 IN NS ns1.cloudflare.com. # 第3段:问.cn,指到权威
coderoadmap.cn. 172800 IN NS ns2.cloudflare.com.
;; Received 814 bytes from 203.119.25.1#53(a.dns.cn) in 24 ms
www.coderoadmap.cn. 300 IN A 203.0.113.10 # 第4段:问权威,拿到答案
;; Received 64 bytes from 173.245.59.104#53(ns1.cloudflare.com) in 19 ms
注意:+trace 模式下是你的电脑直接做迭代,绕过递归器(除非加 +recurse 等参数),所以它展示的才是教科书全链路,也因此在部分 NAT/防火墙环境下可能拿不到结果。
步骤 3:手工迭代一遍(理解胶水记录)
# 先问根:不给递归(+norecurse),看根如何引荐
dig @a.root-servers.net www.coderoadmap.cn A +norecurse +nodnssec
# 再拿根返回的 .cn 地址继续问
dig @a.dns.cn www.coderoadmap.cn A +norecurse
# 最后问权威
dig @ns1.cloudflare.com www.coderoadmap.cn A +norecurse
第 1 跳的 ADDITIONAL 段里会附带 .cn 服务器的 IP,这就是胶水记录(Glue Record)——防止「要找 .cn 得先知道 .cn 的 NS 名字、解析那个名字又要问 .cn」的死循环。
步骤 4:观察缓存如何让查询变快
连续执行两次,对比 Query time 和 TTL:
dig @223.5.5.5 www.coderoadmap.cn A | grep -E "Query time|IN\s+A"
sleep 1
dig @223.5.5.5 www.coderoadmap.cn A | grep -E "Query time|IN\s+A"
典型现象:第一次几十毫秒(回源权威),第二次个位数毫秒;TTL 从 300 开始倒计时。等约 5 分钟后再查,TTL 会跳回 300、耗时也可能略增——说明递归器缓存过期、重新走了一遍迭代。
步骤 5:清本机缓存后复现
# Windows(管理员 PowerShell)
ipconfig /flushdns
# macOS(较新版本)
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
# Linux(systemd-resolved)
sudo resolvectl flush-caches
清完立刻查,TTL 显示的是递归器的剩余时间(本机缓存不影响它);但如果递归器缓存也过期了,你看到的 TTL 会「满血复活」。
步骤 6:量化一次「冷解析」的真实成本
# 第一次查询强制走新名字(可用刚建的测试记录),并打印耗时
dig @8.8.8.8 test-new.coderoadmap.cn A
# curl 视角看 DNS 占比
curl -o /dev/null -s -w "DNS解析: %{time_namelookup}s\n总耗时: %{time_total}s\n" https://www.coderoadmap.cn/
冷解析通常 20~150ms(视递归器与权威的网络距离);命中本机缓存时 time_namelookup 可低于 1ms。跨网(如国内递归器问海外权威)冷解析可能到 300ms 以上。
讲清楚要点
- 递归是一种「服务承诺」:客户端对递归器说「给我答案,别让我跑腿」;递归器负责跑完全程并承担缓存。家庭宽带默认用运营商递归器,企业可自建(dnsmasq、Unbound)。
- 迭代是实际跑腿方式:递归器问根,根不替它继续查,只给下一级的名字;逐级引荐直到权威。整条链路通常 3 跳:根 → TLD → 权威(有子域委派时更多)。
- 缓存分布在三级:应用/浏览器缓存(几分钟到几十分钟,各家策略不同)、操作系统缓存(受 TTL 约束)、递归解析器缓存(严格按 TTL,最多再叠加 SOA negative TTL)。
- 递归器可以主动缩短 TTL 上限:部分公共解析器对超长 TTL 会截断(如最大缓存 1 小时),但不会放大;客户端信任递归器返回的 TTL。
- 否定结果也缓存:NXDOMAIN 和 NODATA(名字存在但无该类型)的缓存时间取 SOA 的 minimum 字段(SOA 最后一列)与实际 SOA TTL 的较小值(RFC 2308)。
- UDP 为主、TCP 兜底:普通查询走 UDP 53;响应超过 512 字节(EDNS0 下通常 4096)、或区域传送(AXFR)时走 TCP。DNSSEC、大量 TXT 场景常见 TCP 回退。
- 根服务器不直接回答业务记录:全球只有 13 组根(a~m.root-servers.net,通过任播在全球有上千实例),它只负责引荐 TLD。
你可能会懵的地方
-
递归和迭代到底差在哪?一句话说清? 递归查询要求「对方给最终答案」;迭代查询中对方只给「下一步该问谁」。客户端只做递归(一次),递归器对权威做迭代(一串)。
-
根服务器只有 13 台,扛得住全球流量吗? 「13」是 13 个逻辑标识(a 到 m),每个标识通过任播(Anycast)在全球部署数百台物理实例,你实际访问的是网络最近的那一台。而且绝大多数查询被各级缓存挡掉,真正打到根的比例很低。
-
为什么
dig +trace在公司网络经常失败? 它需要本机直接对外发起 UDP 53 迭代查询,很多企业防火墙只允许流量经过指定递归器,直接外联 53 端口会被拦截。改用普通dig或在服务器/家用网络上做实验。 -
TTL=300 的记录,为什么 5 分钟后我浏览器里还是旧 IP? TTL 管的是 DNS 缓存,但浏览器还可能复用已建立的 TCP 连接、保持旧的 Happy Eyeballs 选择。关掉标签页重开、或重启浏览器才能彻底对齐。
-
CNAME 链上的 TTL 怎么算? 每一跳(CNAME 本身、最终 A)有各自的 TTL,整体缓存时间受限于最先过期的那一条。长 CNAME 链(3 跳以上)既慢又脆弱,应尽量避免。
错法对照
| 错法 | 后果 | 正确做法 |
|---|---|---|
| 认为客户端自己会一层层问根 | 解释不了缓存与解析器行为 | 客户端只问递归器;迭代是递归器的事 |
用 dig +short 判断「解析慢」 | +short 看不到耗时 | 用完整 dig 看 Query time,或 curl 的 time_namelookup |
| 只在本机清缓存就等公网生效 | 递归器缓存仍在 | 变更前降 TTL;用多公共解析器验证 |
| 排查时忽略 SERVER 字段 | 不知道自己问的是哪台递归器 | 先确认递归器,再对比公共解析器 |
| 把 CNAME 链设到 4 跳以上 | 每跳一次 RTT,解析慢且易断链 | 别名最多 1~2 跳,能直接 A 就不绕 |
卡点急救
- 首次访问慢、刷新后正常:典型冷解析开销。
dig看 Query time,若稳定很高,换个递归器(@223.5.5.5)对比;若只有特定递归器慢,问题在该递归器到权威的链路,可考虑换权威托管。 - 解析结果在权威改了,但递归器坚持旧值:确认权威侧 TTL;递归器按它取记录时拿到的 TTL 缓存,改 TTL 不会影响已经缓存出去的答案。等当前缓存过期,或联系递归服务商刷缓存(大型厂商多有刷新接口)。
dig +trace第一段就卡住:本机拿不到根 hint 或 UDP 外联被拦。加@指定递归器用普通查询代替,或在网络放行的机器上跟踪。
面试常问
-
详细描述一次 DNS 递归解析的完整过程。 客户端先查浏览器与系统缓存;未命中则向递归解析器发起递归查询。递归器查自身缓存,未命中则开始迭代:问根服务器,根返回对应 TLD 的 NS 和胶水 IP;问 TLD,TLD 返回该域权威服务器的 NS;问权威拿到记录与 TTL。递归器把结果缓存后返回客户端,客户端也缓存。之后进入 TCP/TLS/HTTP。
-
什么是胶水记录?为什么需要它? 胶水记录是父级区域(如 .cn)在委派 NS 时,在 ADDITIONAL 段额外附带的 NS 主机 IP。当某个域的权威服务器主机名就挂在该域自身下面(如 coderoadmap.cn 的 NS 是 ns1.coderoadmap.cn),会出现「解析 NS 名字又要问该域权威」的循环依赖;胶水让解析器不用先解析名字就能直接连到权威。
-
递归解析器为什么要缓存?TTL 在其中起什么作用? 缓存把重复查询就地回答,既把解析延迟从几十上百毫秒降到亚毫秒,又极大降低了根/TLD/权威的负载。TTL 是权威为每条记录设定的最大缓存秒数,递归器过期后必须重新解析;它平衡了性能与数据新鲜度,也是运维控制变更传播速度的主要旋钮。
自测三问
- 客户端到递归器、递归器到权威,分别用的是递归还是迭代?为什么这样分工?
- 一条 TTL 300 的 A 记录刚被修改,最坏情况下哪些地方的用户还要等多久?为什么各地等待时间不同?
dig +trace与普通dig的输出差异,本质上反映了两者在链路中扮演什么不同角色?
小结一句话
客户端一次递归把活儿外包给解析器,解析器用三级迭代(根→TLD→权威)跑腿取答案,再靠各级缓存和 TTL 在速度与新鲜度之间做平衡。
延伸
- RFC 1034 第 2、5 章:解析器与名字服务器模型
- RFC 2308:查询的否定缓存
- RFC 6891:EDNS0(扩展报文机制)
- 根服务器官方介绍(Root Server Technical Operations)
- 下一课预告:域名层次——根、顶级域、权威的层级结构,SOA 与委派到底怎么组织。