查询失败、回答不同、修改尚未被看到,是三类问题。每次记录查询名、类型、所问服务器和时间,再比较权威、递归与系统解析路径。示例域名不用于真实变更。
排障时先别用 +short 隐藏状态
先读完整响应
dig example.com A
记录 status、flags、ANSWER、AUTHORITY、SERVER 和 Query time。下面是示例片段,TTL 300 的单位是秒,SERVER 才说明问了谁。
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 12345
www.example.com. 300 IN A 192.0.2.10
;; SERVER: 192.0.2.53#53(192.0.2.53) (UDP)
dig @1.1.1.1 example.com A +noall +comments +answer +authority +stats
@ 指定服务器;这组显示选项保留状态、回答、权威区和服务器统计,用来与上次结果比较,不改变解析记录。
按业务需要选择记录类型
dig example.com AAAA
IPv4 正常但 IPv6 失败时,检查 AAAA 与实际 IPv6 路径;无 AAAA 不等于域名不存在。
dig example.com MX
数字越小优先级通常越高;随后单独查询交换器主机的地址。TXT、CAA、CNAME 等同样按实际问题替换类型,不用 ANY 获取所谓全部记录。
dig -x 192.0.2.10
IP 替换为实际目标;PTR 与正向 A/AAAA 分别维护,不能用其中一个证明另一方向已配置。
确认应用是否走同一解析路径
getent ahosts example.com
结果可能来自 hosts、DNS 或其他 NSS 模块;与 dig 不同应先查来源。应用也可能自带解析器或使用 DoH。
resolvectl query example.com
不使用 resolved 的系统无需执行;配合 resolvectl status 看每链路 DNS 和路由域。
| 响应 | 含义与下一步 |
| NOERROR + answer | 查询得到记录,仍核对 CNAME 链、类型、TTL 和 SERVER |
| NOERROR + empty answer | 可能是该类型无数据(NODATA)、转介等;读 authority,不等同域名不存在 |
| NXDOMAIN | 所问解析视图认为该名字不存在,留意负缓存 |
| SERVFAIL | 服务器未能完成查询;可能 DNSSEC、权威不可达等,非直接证明域名不存在 |
| timeout | 没有及时收到回复;查路径、UDP/TCP 53、服务及超时设置 |
dig 直接问 DNS;getent 经过系统 NSS,可能包含 hosts 等来源;resolvectl 适用于 systemd-resolved。它们不是完全相同的路径。+short 适合已确认状态后的提取。ANY 不保证返回全部记录;按 A/AAAA/CNAME/MX/TXT/NS/SOA/CAA 等实际问题指定类型。
TTL 属于各缓存条目,不是全球倒计时
正在呈现知识画面
改权威记录后,两个缓存会同时更新吗TTL=120 秒的旧记录分别在 t=0 和 t=30 缓存;权威在 t=60 改成新 IP。两个缓存的旧副本分别在 t=120 和 t=150 到期。
{"id":"ops-dns-cache","title":"改权威记录后,两个缓存会同时更新吗","summary":"TTL=120 秒的旧记录分别在 t=0 和 t=30 缓存;权威在 t=60 改成新 IP。两个缓存的旧副本分别在 t=120 和 t=150 到期。","height":600,"html":"<main><p class=\"quiet\" data-i18n=\"scope\"></p><label class=\"range\" for=\"time\"><span data-i18n=\"time\"></span><output id=\"timeValue\"></output><input type=\"range\" id=\"time\" min=\"30\" max=\"180\" value=\"90\"></label><div class=\"row code\">t=0 A ← old · TTL 120\nt=30 B ← old · TTL 120\nt=60 authority ← new</div><div class=\"cards\"><section class=\"card \"><h3 data-i18n=\"auth\"></h3><strong id=\"auth\"></strong></section><section class=\"card \"><h3 data-i18n=\"r1\"></h3><strong id=\"r1\"></strong></section><section class=\"card \"><h3 data-i18n=\"r2\"></h3><strong id=\"r2\"></strong></section></div><div class=\"explain\"><div class=\"stable \"><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"hint\"></p><p id=\"message\" aria-live=\"polite\"></p></div></div></main>\n","css":"main {\n display: grid;\n gap: 18px;\n min-width: 0;\n}\np {\n overflow-wrap: anywhere;\n}\n.controls {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n align-items: center;\n}\n.presets {\n display: flex;\n flex-wrap: wrap;\n}\n.presets button {\n flex: 1 1 85px;\n font-size: 15px;\n}\n.cards {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: 10px;\n}\n.card {\n border: 1px solid var(--rule);\n border-radius: 10px;\n padding: 14px;\n min-width: 0;\n background: var(--surface);\n}\n.card h3 {\n font-size: 15px;\n color: var(--muted);\n margin-bottom: 8px;\n}\n.card strong {\n font-size: 23px;\n font-weight: 500;\n overflow-wrap: anywhere;\n}\n.code {\n font-family: var(--viz-font-mono);\n font-size: 15px;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n.stable {\n display: grid;\n min-width: 0;\n}\n.stable > * {\n grid-area: 1/1;\n margin: 0;\n}\n.stable > .reserve {\n visibility: hidden;\n pointer-events: none;\n}\n.explain {\n border-left: 3px solid var(--accent);\n padding: 12px 14px;\n background: var(--accent-soft);\n}\n.arrow {\n text-align: center;\n color: var(--accent);\n font-size: 25px;\n}\n.row {\n padding: 12px;\n border: 1px solid var(--rule);\n border-radius: 8px;\n min-width: 0;\n}\n.row.on {\n border-color: var(--accent);\n background: var(--accent-soft);\n}\n.row.off {\n color: var(--muted);\n}\n.bar {\n height: 18px;\n background: var(--surface);\n border: 1px solid var(--rule);\n border-radius: 5px;\n overflow: hidden;\n margin-top: 8px;\n}\n.bar > i {\n display: block;\n height: 100%;\n background: var(--accent);\n width: 0;\n transition: width 0.22s;\n}\n.checks {\n display: grid;\n gap: 8px;\n}\n.checks label {\n display: flex;\n gap: 10px;\n align-items: center;\n padding: 9px;\n border: 1px solid var(--rule);\n border-radius: 7px;\n font-size: 16px;\n}\n.checks input {\n width: 18px;\n height: 18px;\n accent-color: var(--accent);\n flex-shrink: 0;\n}\nlabel.range {\n display: block;\n font-size: 16px;\n}\nlabel.range output {\n float: right;\n font-variant-numeric: tabular-nums;\n}\n.pair {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 10px;\n}\n.flow {\n display: grid;\n gap: 6px;\n}\n.quiet {\n font-size: 14px;\n color: var(--muted);\n}\n.badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 5px;\n background: var(--accent-soft);\n color: var(--accent);\n}\nbutton {\n overflow-wrap: anywhere;\n}\n.large {\n font-size: 32px;\n color: var(--accent);\n font-weight: 500;\n}\n.layers {\n display: grid;\n gap: 12px;\n}\n.layer {\n border-left: 3px solid var(--rule);\n padding-left: 12px;\n}\n.layer.on {\n border-color: var(--accent);\n}\n@media (max-width: 520px) {\n .cards,\n .pair {\n grid-template-columns: 1fr;\n }\n .card {\n padding: 12px;\n }\n .card strong {\n font-size: 22px;\n }\n}\n.card strong {\n font-size: 19px;\n min-height: 3.2em;\n display: block;\n}\n","js":"const $ = (s) => document.querySelector(s);\nconst $$ = (s) => [...document.querySelectorAll(s)];\nconst t = (k) => viz.t(k);\nconst put = (id, v) => ($(\"#\" + id).textContent = v);\nconst say = (k) => put(\"message\", t(k));\nconst state = (v) => ($(\"main\").dataset.state = JSON.stringify(v));\nconst press = (key, v) =>\n $$(\"[data-\" + key + \"]\").forEach((b) =>\n b.setAttribute(\"aria-pressed\", String(b.dataset[key] === v)),\n );\nfunction draw() {\n let time = +$(\"#time\").value;\n put(\"timeValue\", time + \" s\");\n const auth = time < 60 ? \"old\" : \"new\",\n a = time < 120 ? \"old\" : auth,\n b = time < 150 ? \"old\" : auth;\n put(\"auth\", t(auth));\n put(\"r1\", t(a) + \" · TTL \" + (time < 120 ? 120 - time : 120));\n put(\"r2\", t(b) + \" · TTL \" + (time < 150 ? 150 - time : 120));\n state({ time, auth, a, b });\n}\n$(\"#time\").oninput = draw;\nsay(\"hint\");\ndraw();\n","audio":false,"strings":{"auth":"权威记录","hint":"权威改动不会推送覆盖已有缓存。A 先到期、B 后到期,因此中间可能一新一旧;到期后的这次查询才取新值。","new":"新 IP","old":"旧 IP","r1":"缓存 A:t=0 取得","r2":"缓存 B:t=30 取得","scope":"无预取、serve-stale 或额外缓存。拖动时间,比较“此刻发起一次查询”的答案;不是连续请求回放。","time":"查询时刻 t(秒)"}}
先确认权威,再比较缓存
dig example.com NS
对子域还需确认是否另有委派;不能因为父域 NS 存在就默认它负责所有子域。
dig @ns1.example.com www.example.com A +norecurse
替换服务器名并检查 aa 标志、SOA/委派信息;无 aa 先确认问对区域。多台权威需要分别核对,排除部署不同步。
dig @1.1.1.1 www.example.com A
记录查询时间;权威已更新而递归仍旧,可能等待旧 TTL 到期。负缓存也会延迟新建记录可见性。
dig +trace www.example.com A
需要本机能直接访问各级 DNS;它不复现企业转发器、分域配置或目标递归器的缓存。
只在证据指向本机缓存时刷新
sudo resolvectl flush-caches
这是可选修复,不是每次查询的前置步骤。执行后重做 resolvectl query 和实际应用请求;其他递归器、浏览器、容器的缓存不会因此消失。
ns1.example.com 替换为实际权威服务器;核对 AA、委派与目标区域。+trace 从根迭代查询,不复现生产递归解析器的缓存/转发/分视图路径。各地答案不同可来自 GeoDNS、分视图、缓存、轮转或故障,不能仅凭不同就判定污染。降低 TTL 必须等旧 TTL 的缓存退出后才对已有缓存有效;负回答也可被缓存。
改记录前保存身份,改完分层验证
云平台操作先读取 zone/record ID、RR、Type、Value、TTL 与线路,保存旧值。按记录 ID 更新,避免无意新增同名记录;Alibaba Cloud 的 UpdateDomainRecord 同时需要 RR、Type、Value,不能只给 Value。TXT 尤其可能有多条业务验证值,不覆盖整组。API 返回成功后,依次查权威、目标递归解析器和实际应用。
只在确认本机缓存是问题时使用 resolvectl flush-caches;它不会清空 ISP、浏览器、容器或其他递归器的缓存。ACME DNS-01 还需核对委派、TXT 名和值,见 证书。
加密传输与 DNSSEC 验证分开看
请求 DNSSEC 记录与自行验证是两件事
dig @1.1.1.1 example.com A +dnssec
读 RRSIG 等记录和 ad 标志;dig 本身不会因此验证信任链。
delv example.com A
检查 validated / unsigned / validation failure 的区别;先确保时钟、信任锚和网络正常。未签名区域与签名损坏不是同一结果。
比较传输方式时保持问题一致
dig @1.1.1.1 example.com A +tcp
UDP 超时而 TCP 成功时查防火墙、分片与路径;TCP DNS 本身不是加密 DNS。
kdig @1.1.1.1 example.com A +tls +tls-ca +tls-hostname=cloudflare-dns.com
+tls 使用 TLS,+tls-ca 检查信任链,+tls-hostname 指定应匹配的身份。握手失败先查端口、时间与证书,不通过关闭校验掩盖问题;协议能力按提供商文档确认。
kdig 需安装 Knot 工具并支持 TLS;目标服务能力以提供商文档为准。+dnssec 设置 DO 请求 DNSSEC 相关记录,不让 dig 自己验证链。AD 表示该解析器声称已验证,信任它还依赖可信传输与解析器;delv 自行验证并依赖正确的信任锚和时钟。DoT/DoH/DoQ 保护传输,不自动证明所查询区域启用 DNSSEC;不要把任意 DNS IP 当作支持所有加密协议。
BIND dig/delv · DNS negative caching RFC 2308 · Alibaba UpdateDomainRecord · Cloudflare DoT