DNS解析优化实战:从缓存、TTL到HTTPDNS的性能调优指南

发布时间:2026/10/21 5:25:35
DNS解析优化实战:从缓存、TTL到HTTPDNS的性能调优指南 都说DNS解析就是“输入域名、返回IP”一句话的事。但如果你做过线上服务的性能压测或者排查过偶发的超时问题就会知道这句话远没有想象中简单。很多时候一个看起来不起眼的DNS解析在链路里能吃掉几百毫秒高峰期稍微抖动一下用户端的失败率就跟着往上跳。我这些年做系统架构踩过的DNS相关的坑比想象中多得多缓存失效风暴、LocalDNS劫持、TTL配错导致故障转移延迟、集群内部解析超时……每一类问题都实打实地影响过线上稳定性。这篇文章不打算讲理论课。我想借着一个完整的视角把“DNS解析优化”这件事拆开先看一次解析到底慢在哪里再看客户端、服务端、架构层面各自能做什么最后用几个生产环境真实遇到过的问题复盘排查思路。要优化DNS先得知道时间都去哪儿了。1. 一次DNS查询的完整链路优化前先看清时间都去哪儿了1.1 从浏览器输入域名到拿到IP中间经历了什么很多人以为输入域名之后电脑会“直接”去问某台DNS服务器。真实情况是一次正常的域名解析是一个多级缓存逐层询问的过程浏览器缓存Chrome、Firefox这类浏览器内置DNS缓存缓存时间一般只有几十秒到几分钟。操作系统缓存Windows的DNS Client服务、Linux的systemd-resolved或nscd都会缓存解析结果。hosts文件操作系统在发起网络请求前会检查本地hosts文件命中了就直接用。LocalDNS本地递归服务器运营商或企业网络分配下来的DNS服务器代替客户端去完成整棵DNS树的递归查询。根域名服务器、顶级域名服务器、权威域名服务器LocalDNS一层层向上询问直到找到负责该域名的权威服务器拿到最终的A记录或CNAME记录。这条链路每一级缓存都可能命中也可能全部落空。全部落空时耗时完全取决于LocalDNS到权威服务器之间的网络情况几十毫秒到几百毫秒都出现过。我见过一次内网环境的解析耗时高达2秒最后查出来是LocalDNS配置了上游DNS而上游DNS响应超时且没有做合理的重试与降级。所以想优化DNS第一个要养成的习惯是先分清楚你的请求到底命中了哪一层缓存耗时是在哪一段产生的。不知道这些后面所有优化都是拍脑袋。1.2 递归查询与迭代查询的性能差异与优化含义DNS查询有两种方式这是很多人容易混淆的地方。客户端向LocalDNS发起的是递归查询LocalDNS必须给出最终答案或者报错。而LocalDNS向根域名服务器、顶级域名服务器、权威域名服务器发起的则是迭代查询每一级服务器只需要告诉它“下一步去哪里问”。这个机制决定了两个优化方向客户端侧的优化重点是降低递归查询的频率。因为每次递归查询都要把压力交给LocalDNS而LocalDNS在高峰期的表现并不稳定尤其是公共Wi-Fi、校园网、部分运营商网络下的LocalDNS解析慢是常态。LocalDNS侧的优化重点是提高缓存命中率与上游选择的智能性。LocalDNS如果能妥善利用TTL并合理配置上游DNS大部分请求都不需要真正触达权威服务器。另外还有一个容易被忽略的细节DNS报文基于UDP天然存在丢包重传问题。如果LocalDNS和上游之间的链路质量不好一次查询可能要经历超时重传耗时就成倍放大。这也是为什么有些长期解析慢的网络环境换用TCP方式的DoHDNS over HTTPS之后明显改善——TCP有可靠传输和拥塞控制包丢了不需要干等那么久。1.3 TTL的真正作用缓存时长不是越大越好TTLTime To Live是DNS记录里用来告诉各级缓存“这条记录可以缓存多久”的参数。它的选择本质上是个权衡题TTL太长缓存命中率高解析快但域名记录的变更要等很久才能全球生效。TTL太短变更很快生效但各级缓存频繁回源权威服务器和LocalDNS的压力会变大。我给过一个内部业务的例子某个核心域名一开始TTL设了24小时做机房迁移时发现改完A记录将近一天之内还有大量用户访问旧IP体验非常糟糕。后来学乖了所有核心域名统一采用“长TTL做CNAME入口短TTL做A记录出口”的方式域名结构里的CNAME作者保持稳定入口用比较长的TTL如600秒以上保证入口解析命中率高真正的业务IP走A记录TTL控制在30到60秒这样后端IP切换时最多一分钟就能全局生效。注意TTL不是拍脑袋定的需要同时考虑你的流量峰值、权威服务器承载能力、以及故障切换允许的延迟。如果你没有压测过权威服务器的查询上限不要把TTL设得太激进。1.4 域名解析失败到底是怎么发生的还有一件很反直觉的事DNS解析失败不一定发生在网络上很多时候是配置层面的问题。比如域名的NS记录指向错误、权威服务器上A记录不存在、DNSSEC签名过期导致解析被拒绝这些问题在客户端看来都表现为“解析失败”或“域名不存在”。这意味着排查时第一步不是怀疑LocalDNS而是先用外部工具独立验证域名状态。我常用的做法是直接在不同网络环境下执行解析测试如果所有网络环境都失败那问题大概率在域名本身或权威侧如果只有特定网络失败问题往往出在LocalDNS或者中间链路。2. 从客户端到服务端几个立竿见影的调优手段2.1 系统层本地DNS缓存的正确打开方式操作系统自带的DNS缓存机制各有差异但对大多数服务端应用来说最合适的方式是自建一个本地DNS缓存服务比如Linux下的dnsmasq或unbound。把应用服务器的resolv.conf指向127.0.0.1由本地缓存统一做解析与缓存管理。以dnsmasq为例一个常见配置是这样的# /etc/dnsmasq.conf # 不读取/etc/resolv.conf的上游配置手动指定更可靠的上游 no-resolv server223.5.5.5 server8.8.8.8 # 开启缓存默认缓存数约150条这里增大 cache-size10000 # 不对无效域名做负面缓存可选避免坏域名反复查询拖慢本地 no-negcache # 日志便于排查 log-queries log-facility/var/log/dnsmasq.log配置完之后记得把/etc/resolv.conf改成nameserver 127.0.0.1这样应用层的所有DNS请求都会先经过本地缓存。如果缓存命中解析耗时会从毫秒级直接降到微秒级更重要的是它帮你挡掉了大量对于上游DNS的重复请求相当于给系统加了一层“缓冲垫”上游节点抖动时影响会被削弱很多。2.2 应用层语言运行时自带的DNS缓存坑系统层做了缓存不代表应用层就没事了。很多语言运行时自己还维护了一层DNS缓存这是最坑的地方——你改了系统配置应用却还拿着旧IP。以Java为例JVM默认对DNS解析结果做过缓存安全策略不同的情况下负缓存解析失败的结果甚至可能缓存10秒到几十秒正缓存有时被配置为永久有效。线上如果依赖域名做服务发现JVM缓存策略和实际服务IP变更节奏一旦不匹配就会出现“服务状态异常但客户端还在请求旧IP”的问题。比较稳妥的做法是通过JVM参数显式声明-Dnetworkaddress.cache.ttl30 -Dnetworkaddress.cache.negative.ttl5Go语言里net.Resolver的行为不太一样但如果你用了一些第三方库做服务发现同样要留意它们是否做了独立缓存。Python的requests库本身不做缓存但urllib3的重试机制在某些版本下会和底层DNS解析产生奇怪的交互。经验凡是涉及“改了DNS却迟迟不生效”的问题先怀疑应用框架有没有自带缓存再回头查系统缓存。直接从应用层入手往往能省半天时间。2.3 连接池与连接复用比调缓存更划算的优化还有一个容易被忽略的方向不是所有请求都需要做DNS解析。如果连接能复用DNS解析次数就会大幅下降。HTTP/1.1的Keep-Alive、HTTP/2的多路复用、RPC框架的长连接池本质都是在让“一条TCP连接承载多个请求”。连接不释放IP就不需要重新解析。我曾经压测过某个网关服务刚开始每秒有上千次DNS查询打往LocalDNS后来启用连接池并调大空闲连接存活时间之后每秒DNS查询降到了几十次整体P99延迟下降了约40%。这背后的逻辑很简单TCP建连时必然要先完成DNS解析如果每次HTTP请求都新建连接DNS解析频率就会跟QPS成正比。连接复用优化的是“请求与连接的比例”而非“DNS本身的速度”但效果往往更加直接。很多团队一谈DNS优化就急着上HTTPDNS其实先检查一下自己的连接池配置说不定才是性价比最高的第一步。3. 架构视角的DNS设计从本地解析到全局调度3.1 HTTPDNS绕过LocalDNS这道不可控的墙传统DNS最大的痛点在于客户端把解析请求完全交给了LocalDNS。而LocalDNS是你的应用无法控制的它可能缓存了错误结果可能被劫持可能根据业务逻辑返回错误IP也可能因为上游链路抖动而解析超时。HTTPDNS也叫DNS over HTTPS的另一种形态的思路很直接客户端应用不通过系统DNS去解析而是直接向HTTPDNS服务商的API接口发起HTTP请求拿到“IP列表”。这个IP列表往往还包含调度策略信息可以自动选路。接入方式大致是三步在客户端配置HTTPDNS服务商域名和账号信息。替换系统默认解析逻辑业务域名统一走HTTPDNS接口获取IP。拿到IP后建立连接并在IP不可用时执行降级策略——回退到系统DNS。// 伪代码示例 async function resolveDomain(domain) { try { const resp await fetch(https://httpdns-service.example.com/resolve?host${domain}); const data await resp.json(); if (data.ips data.ips.length 0) { return data.ips; } } catch (e) { // 请求失败回退系统DNS } return null; }HTTPDNS相比传统DNS有两层优势一是避开了LocalDNS的缓存污染与劫持问题拿到的一定是从权威链路获取的真实结果二是可以实现更精细的流量调度比如根据客户端的地理位置、运营商、网络类型返回不同的IP。对于移动端App、边缘计算场景这个东西几乎是标配。以下是传统DNS与HTTPDNS的核心差异解析入口传统DNS走系统LocalDNS不可控HTTPDNS走应用内HTTP接口可控缓存污染传统DNS容易被LocalDNS缓存错误结果HTTPDNS由服务端统一控制调度精度传统DNS最多按LocalDNS位置粗粒度调度HTTPDNS可按客户端IP、运营商、地域精细调度劫持风险传统DNS有被劫持可能HTTPDNS基于HTTPS篡改难度大缺点HTTPDNS需要单独接入SDK依赖第三方服务质量3.2 多地域流量调度与故障转移智能DNS只是起点DNS不只是“把域名解析成IP”它还能承担一部分流量调度职责。大型系统通常会在多个地域部署机房DNS可以通过GeoDNS或EDNS Client Subnet机制让不同地域的用户解析到不同的机房入口IP。有个很重要的点是智能DNS调度必须和健康检查联动。如果没有健康检查一旦某个机房故障DNS依旧返回该机房的IP用户访问就持续超时。常见的做法是让智能DNS定时探测各机房的健康状态发现异常后把该机房的解析权重降为零自动切换流量到其他机房。故障转移的生效速度取决于你设置的TTL。TTL太长了即便健康检查秒级发现故障用户侧还是会在TTL超时之前一直拿旧IP访问故障机房。所以对于承担流量调度的域名建议TTL控制在30到60秒之间配合客户端侧的连接失败重试逻辑才能实现真正的高可用。3.3 集群内部DNSKubernetes环境下CoreDNS的性能陷阱互联网应用讲完了再说说集群内部的DNS问题。容器化普及之后DNS优化多了一个非常关键的战场Kubernetes集群内部的服务发现。Kubernetes的Service默认通过CoreDNS做域名解析。集群小的时候没什么感觉Pod数量上千之后CoreDNS就开始成为瓶颈了。原因很典型新Pod创建时都要解析Service域名如果同时有大量Pod扩容或频繁重启瞬间会有海量DNS查询打向CoreDNS超过其处理能力就会导致解析超时、间歇性失败。实际遇到过的场景是某个服务做扩容时新Pod反复CrashLoopBackOff排查日志发现大量“connection timed out; no servers could be reached”不是应用本身的问题而是CoreDNS在扩容瞬间负载过高。解决思路有几层部署NodeLocal DNSCache在每个节点上跑一个DNS缓存代理大部分Pod解析请求都命中节点本地缓存不再打到集群中心的CoreDNS。合理配置CoreDNS副本数和反亲和性保证多个副本分散在不同节点避免单点过热。适当开启CoreDNS的autopath等优化选项减少无效的搜索域遍历查询。注意集群内DNS调优永远先看监控曲线再动手。我见过太多人上来就加副本、调参数结果问题根本不在CoreDNS性能上而是某个Pod疯狂重建制造了DNS暴雨。先看QPS和P99耗时再决定要不要扩容。4. 生产环境DNS高延迟事故四条线索的定位思路4.1 事故链路是怎么断的一个典型的DNS缓存风暴复盘有一次做活动大促前业务方为了让后端能快速切流量把某个核心域名的TTL从600秒改成了10秒。活动当天流量一上来监控显示权威服务器请求量暴涨到平时的十几倍部分用户反映打开页面要等好几秒。复盘后发现问题就出在“所有人几乎同时刷新页面而TTL只有10秒”。原先的LocalDNS在600秒TTL下每个用户端可能只要回源一次改成10秒后同一个用户在较长浏览过程中可能需要多次回源缓存命中的逻辑被完全打乱了权威服务器的QPS直接被打满。这个案例说明一个很简单的道理TTL不是越小越好小TTL带来的回源压力需要权威服务器能够承受。活动前提TTL时必须同时评估权威服务器的容量并且预留几倍的冗余。4.2 用dig和tcpdump逐步定位DNS问题针对DNS高延迟问题我一般按下面这套流程排查分享出来供参考。第一步先用dig看完整链路dig trace example.comtrace会模拟递归服务器逐级查询的过程能看到根域名服务器、顶级域名服务器、权威域名服务器每一级的响应时间和返回结果。如果某一级迟迟不响应问题就锁定在那一层。第二步分别向不同DNS服务器发查询做对照测试# 用本地配置的DNS dig 127.0.0.1 example.com # 用公共DNS dig 223.5.5.5 example.com如果本地DNS明显比公共DNS慢问题就在LocalDNS或本地缓存。如果公共DNS也慢问题可能在域名本身或者权威侧。第三步如果怀疑是网络问题用tcpdump抓DNS流量观察报文交互sudo tcpdump -i eth0 -n port 53 -w dns.pcap从tcpdump能看到查询发出时间、响应返回时间和重传次数。如果UDP报文大量重传基本可以判断是链路丢包导致的解析延迟。第四步也是最容易被忽视的统计解析耗时的分位数。用脚本循环执行dig记录每次耗时然后计算P50、P95、P99。很多时候平均值看着正常P99已经爆了这种情况下劣质链路的问题会被平均耗时的表象掩盖掉。4.3 域名迁移时TTL不够用的教训最后分享一个域名迁移的经典翻车案例。某个系统要从旧机房迁到新机房运维直接在迁移当天改了A记录旧IP指向新IP。结果业务方反馈迁移后几个小时仍有大量流量打到旧机房旧机房设备已经开始下线不少请求直接超时。后来发现这个域名的TTL被设成了4小时。也就是说即便权威服务器上的记录已经改了大量用户本地的DNS缓存还是会继续使用旧IP四小时。这也是很多系统在做机房迁移时提前降TTL的原因——先在一到两天前把TTL降到300秒甚至更低等缓存基本刷新一轮后再做真正的记录变更。再加上旧IP保留一段时间的“双跑”策略客户端的DNS缓存才能平滑过渡到新IP。最后说点实在的做了这么多DNS优化的事我个人的体会是DNS优化的前提是先把观测做起来。如果你连自己系统的DNS解析耗时P50、P99是多少都不知道那就谈不上优化。先在监控系统里加一个“DNS解析耗时”的指标再逐步按本地的缓存、应用层的缓存、连接复用、HTTPDNS、集群内DNS优化这几个方向去排查你会发现收益比想象中更直接。还有一个小技巧每次准备改DNS配置之前先用dig把当前配置和解析结果完整记录下来改完之后拿新结果和旧结果对比。等哪天线上出问题的时候这份记录能帮你省下大把回溯配置的时间。这套习惯我一直保持到现在效果非常稳定。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询