8.4 DNS 解析链路与缓存
一句话
一次域名解析不是 “一个查询”,而是一条链路:浏览器缓存 → 操作系统 stub → 递归解析器 → 根 → 顶级域 → 权威;每一层都缓存、每层都有 TTL,所以 “改了域名为什么没生效” 的答案几乎总是 “某一层还没到期”。
链路:六跳,每跳都可能缓存
| 环节 | 谁 | 缓存什么 | 典型 TTL |
|---|---|---|---|
| 浏览器 | Chrome/Firefox 内部缓存 | 域名 → IP | 60 秒(Chrome 固定约 60s) |
| 操作系统 | stub resolver / nscd / systemd-resolved | 同上 | 受记录 TTL 限制 |
| 递归解析器 | ISP、公司内网、8.8.8.8、223.5.5.5 | 全链路结果 | 按记录 TTL |
| 根服务器 | 13 组 anycast | 顶级域委派(NS) | 天级 |
| 顶级域(TLD) | .com / .cn 的服务器 | 你的权威 NS 列表 | 小时–天级 |
| 权威服务器 | 你的 DNS 服务商 | 记录本身(A/CNAME/TXT…) | 由你设定 |
dig +trace example.com # 从根开始走一遍委派链,看每一跳给了什么
dig example.com A +noall +answer # 看 TTL 还剩多少(第二列)
dig @8.8.8.8 example.com # 换一个递归解析器对比,判断是不是本地缓存
记录类型:只用四种就够看懂 90% 的场景
| 类型 | 指向 | 用途 | 坑 |
|---|---|---|---|
| A / AAAA | IPv4 / IPv6 | 最终地址 | 只配 A 不配 AAAA,纯 IPv6 客户端直接失败 |
| CNAME | 另一个域名 | 别名(CDN、托管服务) | 不能与其它记录共存;CNAME 套 CNAME 会增加解析跳数与延迟 |
| NS | 权威服务器 | 委派 | 改 NS 要等父区 TTL + 子区生效 |
| TXT | 任意文本 | 域名验证、SPF/DKIM、CAA 校验 | 内容过长会被拆成多段 |
| MX / SRV | 邮件 / 服务 | 邮件路由、服务发现 | SRV 的优先级与权重语义容易搞反 |
“改完多久生效” 的算法
生效时间 ≈ max(各级缓存剩余 TTL) + 递归解析器的负缓存/预取行为
| 你改了什么 | 要等多久 | 怎么缩短 |
|---|---|---|
| A 记录换 IP | 原记录 TTL(可能几小时) | 变更前 24–48 小时把 TTL 降到 60–300 秒 |
| 换权威 NS | 父区委派的 TTL(天级) | 只能提前规划,无法加速 |
| 新增记录 | 通常几十秒–1 个 TTL | 无缓存时几乎立即生效 |
| 删除记录(变成 NXDOMAIN) | 受 SOA 的 negative TTL 影响 | 少用 “删除”,改用 “指向维护页” |
负缓存常被忽略:查询一个不存在的名字,解析器会把 “不存在” 也缓存起来(时长取 SOA 的 minimum 字段)。所以 “刚加了解析怎么还说不存在”——前面那次失败被缓存了。
递归解析器与调度:CDN 为什么依赖 DNS
- 递归 vs 权威:递归替客户端跑完全链路并缓存;权威只回答自己区内的记录。
- ECS(EDNS Client Subnet):递归把客户端网段带给权威,权威才能按地理位置返回最近的节点。递归不支持 ECS 时,CDN 会把节点调度到解析器所在地(常常是省会的机房),而不是用户所在地——这是 “调度不准” 的头号原因。
- GeoDNS:权威按来源返回不同 A 记录,本质是把 “就近” 放在 DNS 层;它的精度受递归位置与本地 DNS 影响,所以大型 CDN 还要在 HTTP 层做二次调度(302 或 anycast),见 9.1 边缘缓存原理。
常见故障对照表
| 现象 | 最可能原因 | 验证 |
|---|---|---|
SERVFAIL | DNSSEC 校验失败、权威不可达 | dig +dnssec、换公共 DNS 对比 |
NXDOMAIN | 记录不存在 / 拼错 / 未生效 | dig +trace 查委派链 |
| 部分地区解析失败 | 权威多线不全(只配了电信) | 各地拨测(第三方 DNS 监测) |
| 解析到旧 IP | 某层 TTL 未到期 | 逐层 dig @<resolver> 看 TTL |
| 解析很慢(>200ms) | 走公网递归、CNAME 链过长、ECS 缺失 | dig +stats 看 query time |
| 内网域名解析不到 | 搜索域/ndots 与集群 DNS 配置(见 7.8 网络模型 (CNI / NetworkPolicy));私有域名还依赖 VPC 的解析器与子网边界(见 8.7 VPC、子网、安全组) | 看 /etc/resolv.conf、VPC 的私有 DNS 配置 |
反模式
| 反模式 | 后果 |
|---|---|
| TTL 设 86400 还指望分钟级切流 | 切换窗口以小时计,故障时想回滚也回不去 |
| 用 DNS 做负载均衡(多条 A 轮询) | 无健康检查,挂掉的 IP 继续接流量 |
| CNAME 套三层以上 | 解析跳数增加、首包延迟上升、排障困难 |
| 只配一个权威服务商 | 权威挂 = 全站域名不可解析(比机房挂更致命) |
| 忽略 AAAA 记录 | 纯 IPv6 网络下直接不可达 |
| 变更 DNS 不留回滚窗口 | 切错后要等 TTL 才能切回 |
三条铁律
🔑 TTL 是切换速度的上限;要大改,先把 TTL 降下来,等一个旧 TTL 周期再改。
🔑 DNS 只解决 “找到哪个入口”,不做健康检查与负载均衡(那是 LB/网关的事)。
🔑 权威要有两家或双活;单权威的可用性通常低于你的机房。
自检
Q1 把 A 记录从旧 IP 切到新 IP,TTL 是 3600,切完 5 分钟就有人报 “还访问旧站”,为什么?
▸ 答案:因为部分递归解析器与客户端仍持有旧记录(最长 1 小时)。DNS 变更天然是 “渐进生效”,无法保证所有客户端同时切换。正确做法是:变更前先把 TTL 降到 60–300 秒并等一个旧周期,让缓存 “变短”;切换后旧 IP 至少保留一个原 TTL 时长继续可用(双写过渡),而不是立刻下线。
Q2 为什么说 “用 DNS 做负载均衡” 不可靠?
▸ 答案:因为 DNS 没有健康检查与连接感知:返回的 IP 挂了,客户端仍会拿到它;记录被缓存后,权重调整也不生效。此外不同递归的缓存时间不同,流量分布不可控。真正的负载均衡要放在四层/七层(8.9 四层与七层负载均衡),DNS 只负责 “把用户带到入口”。
Q3 CDN 调度不准、用户被分配到远端节点,先从哪查?
▸ 答案:先看递归是否支持 ECS。不支持时权威只能看到解析器的 IP,于是按 “解析器所在地” 调度,可能离用户几百公里。验证:dig +short TXT o-o.myaddr.l.google.com @<公共DNS> 看递归出口,或在权威侧看收到的 ECS 字段。缓解:启用支持 ECS 的权威/CDN,或在 HTTP 层做二次调度(302/anycast)。
Q4 dig 显示解析正常,但应用就是连不上,可能是什么问题?
▸ 答案:解析正常 ≠ 可达。区分三类:① 解析到的是内网 IP,客户端不在同一网络;② 证书/SAN 与域名不匹配(见 8.6 TLS 握手、证书与 mTLS);③ 应用使用了缓存的连接池/长连接,仍连着旧 IP(DNS 变更对长连接无效)。用 curl -v 看它实际连的 IP,比 dig 更能反映问题。
Q5 为什么要给关键域名配两个权威服务商?
▸ 答案:因为权威故障会让所有下游一起失败:递归拿不到记录,缓存过期后全站不可解析,而且恢复要等 TTL 与递归重试。双权威(不同厂商、不同 anycast 网络)能把 “单厂商故障” 降级为 “部分递归抖动”。这比多机房更便宜,也常常更有效。
关联
- 前置:8.6 TLS 握手、证书与 mTLS(证书校验依赖域名)
- 并列:8.7 VPC、子网、安全组
- 下游:8.9 四层与七层负载均衡、7.8 网络模型 (CNI / NetworkPolicy)