开始前,会这些就够了
- 知道域名是便于记忆的名字,连接网站还需要地址
遇到不熟悉的词,先看这 3 个解释
- TTL
- 在缓存语境里,指一条数据允许保留或被当作有效副本的时间长度。到期后怎样失效、清理或重新读取,要看具体系统的过期规则和使用方式。
- DNS(域名系统)
- 按域名查询地址等记录的系统。知道网站的名字后,可以先借它查找连接所需的信息。
- 递归解析器
- 替使用者查找 DNS 答案的服务,也常把查到的答案暂存起来供后续复用。
假设你把学习网站搬到新机器,已经把域名对应的地址改好。自己访问正常,同学却还被送到旧机器。先别急着认为某个人操作错了:替你们查地址的服务,可能各自保存着一份还没到期的旧答案。
DNS 可以帮助程序按名字查找地址等记录。本篇只看同一个名字的一条地址记录,用十秒钟的虚拟时间演示;这不是实际网站的等待时长,也没有向外部服务器发送查询。
改了地址,两张缓存便条会一起变吗?
A 在第 0 秒、B 在第 4 秒分别直接获取了旧地址,TTL 都是 10 秒。现在从第 4 秒开始;可以先推进 2 秒,再修改权威值,并分别查询 A 和 B。
当前虚拟时间:第 4 秒。按钮只推进这个数字,没有真实等待。
解析器 A
- 保存的地址
- 192.0.2.10
- 装入 / 到期
- 第 0 秒 / 第 10 秒
- 现在剩余 TTL
- 6 秒
仍可复用:下次查询会命中这份缓存。
最近一次查询在第 0 秒:重新获取,回答 192.0.2.10,当时剩余 TTL 为 10 秒。
解析器 B
- 保存的地址
- 192.0.2.10
- 装入 / 到期
- 第 4 秒 / 第 14 秒
- 现在剩余 TTL
- 10 秒
仍可复用:下次查询会命中这份缓存。
最近一次查询在第 4 秒:重新获取,回答 192.0.2.10,当时剩余 TTL 为 10 秒。
起点是第 4 秒:A 在第 0 秒装入,B 刚在第 4 秒装入,同样收到 10 秒 TTL。
这是内存中的虚构解析器,不联网、不查询或清理真实 DNS。到期后只有点击查询才获取新值,缓存命中不会延长到期时间。模型不含负缓存、serve-stale、预取、多层缓存和网络失败;刷新网页也不等于清空真实 DNS 缓存。
一边管原始记录,一边暂存答案
权威服务器负责给出某个范围内的正式 DNS 记录,可以先想成保管地址簿的人。递归解析器负责替使用者查找答案,也常把查到的结果暂存起来;这种副本就是缓存,目的是减少重复查找。
这里假设权威处的修改已经成功,而且每次重新查它都能拿到当前值。即便如此,解析器仍可能复用尚未到期的旧副本。更新权威值与更新这里每份缓存,是两件不同的事,不能只检查前者就断言大家的答案相同。
TTL 是副本的使用期限,不是全球倒计时
TTL 是记录可被缓存的时间长度,以秒计。我们的模型把收到答案的时刻加上 TTL,得到到期时刻;剩余时间则是到期时刻减去现在。缓存命中只会读出原答案,不会因为又有人问了一次,就重新赠送完整的十秒。
A 在第零秒直接查到旧地址,B 在第四秒才直接查到同一旧地址,都收到十秒 TTL。第六秒时,A 还剩四秒,B 还剩八秒。这里特意让两者各自从权威处获取;真实的多层缓存转交答案时,应关注收到的剩余 TTL,不能想成每经过一层都重新计满。
同一秒查询,可以得到两个地址
第六秒修改权威地址后,两个旧副本仍未到期。第十秒查询 A,它会重新获取新地址;同一秒查询 B,却还能命中旧地址。到第十四秒再问 B,它才在本例中重新获取。不同答案可以由不同装入时刻解释,不需要假设更新正沿一条固定路线传到全世界。
代码中的时间由我们直接给出,不会真的等待。HIT 表示用缓存,FETCH 表示在内存里读取当前权威值;两个 IP 都来自文档示例地址段,不代表真实服务。到期只是令旧副本不能再按本例规则复用,不会在无人查询时自动发起更新。
TTL = 10
authority = "192.0.2.10"
caches = {"A": None, "B": None}
def query(name, now):
entry = caches[name]
hit = entry is not None and now < entry[1]
if not hit:
entry = (authority, now + TTL)
caches[name] = entry
remaining = entry[1] - now
source = "HIT" if hit else "FETCH"
print(f"t={now:02d} {name}: {source} {entry[0]} remaining={remaining}")
query("A", 0)
query("B", 4)
authority = "192.0.2.20"
print("t=06 authority: 192.0.2.20")
query("A", 6)
query("B", 6)
query("A", 10)
query("B", 10)
query("B", 14)
怎样运行:保存为 dns_ttl.py,用 Python 3.10 或更新版本运行:python dns_ttl.py。只操作内存和虚拟秒数,不联网、不等待。
运行后对照结果
t=00 A: FETCH 192.0.2.10 remaining=10
t=04 B: FETCH 192.0.2.10 remaining=10
t=06 authority: 192.0.2.20
t=06 A: HIT 192.0.2.10 remaining=4
t=06 B: HIT 192.0.2.10 remaining=8
t=10 A: FETCH 192.0.2.20 remaining=10
t=10 B: HIT 192.0.2.10 remaining=4
t=14 B: FETCH 192.0.2.20 remaining=10还有两种情况,需要另外看规则
负缓存保存的是“这个名字不存在”或“没有所问类型的数据”等否定结果,它也有缓存期限。因此,刚补上一条原本不存在的记录,不一定能用本文只有成功答案的程序解释;RFC 2308 专门说明这类规则。
serve-stale 则是在无法成功刷新等规定条件下,允许解析器暂时使用过期数据,以改善故障时的可用性。RFC 8767 说明了这项例外。本例没有负缓存、预取或 serve-stale,也没有网络失败,所以不能据此保证真实世界到第十秒必定换成新答案。
刷新网页,不等于清掉所有 DNS 缓存
点击网页刷新,不等于把沿途解析器保存的答案全部清空。排查时应先分清看到的是旧地址,还是地址正确但页面内容仍旧;DNS 地址缓存与网页内容缓存,保存的是不同东西。把其中一个层次的变化,当成另一个层次也已更新,容易找错原因。
更有用的记录包括:问了哪台解析器、何时询问、返回哪个地址、剩余 TTL 是多少,以及权威处当前给出的记录。它们能帮助解释这一次观察。本文互动只让你改变虚拟时间与内存中的权威值,没有连接、修改或清理电脑的真实 DNS 配置。
记住这三点
- 权威记录已经更新,不代表每份尚未到期的缓存都同时改变。
- 看剩余 TTL 和装入时刻;缓存命中不会把使用期限重新计满。
- 本例在到期后的下一次查询才重取,真实系统还需考虑负缓存、serve-stale 等规则。
别把例子用过头
- 十秒只是教学数值,不是 DNS 更新的通用承诺;两台解析器都直接从同一权威值获取。
- 模型不含负缓存、serve-stale、预取、多层转发或权威服务器之间的同步过程。
- 网页刷新不等于清空 DNS 缓存;得到新地址也不能证明网页内容一定已更新。
自己试一小步
若 B 更晚才第一次装入缓存,两个使用者会在什么阶段看到不同答案?
- 先在纸上另定 B 的装入时刻,保持 TTL 与 A 的装入时刻不变。
- 在两台缓存都到期前修改权威值,分别计算每次查询的剩余时间。
- 只增加一次缓存命中,再检查它有没有改变原先的到期时刻。
做到这些,就算完成
- 是否按收到答案的时间分别计算,而不是从修改权威值时一起起算?
- 是否区分缓存到期与真的发起下一次查询?
- 是否明确纸面模型没有覆盖负缓存或刷新失败后的过期回答?
查原始资料
想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。
- RFC 1034:Domain Names — Concepts and Facilities (新标签页)↗
TTL 以秒计的缓存期限、resolver 利用已有结果以及 DNS 分布式副本的基本模型。
- RFC 2308:Negative Caching of DNS Queries (新标签页)↗
NXDOMAIN
/NODATA的负缓存有自己的 TTL 规则,缓存响应转交时剩余 TTL 会递减。 - RFC 8767:Serving Stale Data to Improve DNS Resiliency (新标签页)↗
无法刷新等规定条件下使用过期记录的 serve-stale 机制,说明普通 TTL 玩具模型不是所有行为。
- RFC 5737:IPv4 Address Blocks Reserved for Documentation (新标签页)↗
192.0.2.0
/24属于文档示例地址段,可用作本文两个虚构地址。
找一份资料继续练
先选一项,按下面的范围继续学;不必从头读完整门课程。
小林 coding:沿一张 DNS 图找出网页地址 (新标签页)↗
读 DNS 流程图与缓存段落,标出谁保存答案,再用本文的两份 TTL 时间线补充观察。
查看学习准备与访问说明 →阿里云中文:DNS 缓存为何还拿着旧地址 (新标签页)↗
对照 TTL 生效机制,再看运营商缓存策略提醒;用两张时间线解释为何不同人仍可能得到不同地址。
查看学习准备与访问说明 →