5月28日 02:45

DNS 性能优化实战:7 个策略提升解析速度与可靠性

DNS 解析是每一次网络请求的第一步——用户输入网址到页面开始渲染,中间首先要过 DNS 这一关。这一步慢了,后面所有优化都是白搭。一次 DNS 查询通常耗时 20-120ms,看着不多,但如果你的页面要解析 10 个域名,光 DNS 就吃掉 200ms-1.2s,这还没算上 TCP 连接和内容下载。

更要命的是,DNS 挂了,你的网站就彻底不可达——用户看到的就是"无法访问此网站"。所以 DNS 的性能和可靠性,是整个服务可用性的地基。

先搞清楚 DNS 慢在哪里

优化之前得知道瓶颈在哪。DNS 查询的延迟主要来自三个环节:

本地缓存未命中。浏览器有 DNS 缓存,操作系统也有,但如果 TTL 过期或者用户第一次访问,缓存里就没有,必须走完整查询链路。

递归查询链路长。一个域名可能经过 根域名服务器 → 顶级域名服务器(.com) → 权威域名服务器 三级跳,每一跳都有网络延迟。如果中间还有 CNAME 跳转(比如 CDN 域名),链路会更长。

权威服务器响应慢。你的权威 DNS 服务器如果部署在单一地区,海外用户查询就要跨洋,延迟直接飙到几百毫秒。

知道了瓶颈,接下来的优化就有的放矢了。

TTL 设置:最容易调但最常调错

TTL(Time To Live)决定了 DNS 记录在缓存中保留多久。设长了,变更生效慢;设短了,缓存命中率低,查询量暴增。

实战建议

  • CDN 域名、静态资源域名:TTL 设 3600-86400 秒。这些几乎不变,长 TTL 大幅减少查询
  • API 服务、动态服务:TTL 设 300-600 秒。需要快速切换 IP 时不会被旧缓存卡住
  • 准备做 DNS 变更前:提前 24 小时把 TTL 降到 300 秒,等变更完成后再改回来

一个常见错误是所有记录都用同一个 TTL。实际上同一域名的不同记录应该根据变更频率分别设置。

dns
; CDN 域名 - 变更极少,TTL 给长 cdn.example.com. 86400 IN CNAME cdn.provider.com. ; API 服务 - 可能随时切换,TTL 给短 api.example.com. 300 IN A 203.0.113.2

DNS 缓存:减少重复查询的核心手段

缓存分好几个层级,每一层都能拦截大量重复查询。

浏览器 DNS 缓存。Chrome 默认缓存 1000 条记录,TTL 大约 60-120 秒。这个你控制不了,但可以通过合理的 TTL 间接影响。

操作系统 DNS 缓存。Linux 上用 systemd-resolved 或 nscd 管理,可以调大缓存容量:

bash
# systemd-resolved 缓存配置 [Resolve] Cache=yes CacheFromInsecure=yes

递归 DNS 服务器缓存。这是你能控制的最重要的一层。BIND 的缓存配置:

bind
options { recursion yes; max-cache-size 1024m; # 根据服务器内存调整 cleaning-interval 60; # 每 60 分钟清理过期记录 };

关键是监控缓存命中率。如果命中率低于 80%,要么是 TTL 设太短,要么是查询域名太分散。用 rndc stats 看 BIND 的缓存统计:

bash
rndc stats grep "Cache statistics" /var/named/data/named_stats.txt

减少查询次数:前端也能帮上忙

页面加载时,浏览器要为页面中引用的每个新域名做一次 DNS 查询。引用了 8 个不同域名的资源?那就是 8 次 DNS 查询,串行执行时就是灾难。

dns-prefetch 是最简单的前端优化手段:

html
<link rel="dns-prefetch" href="//cdn.example.com"> <link rel="dns-prefetch" href="//api.example.com">

浏览器会在空闲时提前解析这些域名,等真正要用的时候缓存已经命中了。但注意别滥用——只有页面确实会用到的域名才做预解析,否则白白消耗用户网络。

更进一步的方案是减少域名数量本身。把静态资源集中在 1-2 个域名下,比做 10 个 dns-prefetch 更有效。

CDN + CNAME:让解析就近完成

把域名 CNAME 到 CDN 是最常见的 DNS 加速手段:

dns
www.example.com. 600 IN CNAME example.cdn-provider.com.

CDN 的权威 DNS 通常部署了 Anycast,全球有几十个节点,用户的 DNS 查询会被路由到最近的节点响应,延迟从几百毫秒降到几十毫秒。

选 CDN 的时候注意看它的 DNS 解析能力——有些 CDN 在亚太地区节点少,国内用户解析还是绕道海外,效果打折。

高可用:DNS 挂了怎么办

单点 DNS 是定时炸弹。一旦权威 DNS 不可达,所有依赖它的服务全部瘫痪,而且 TTL 没过期之前缓存还能撑一撑,TTL 一过期就彻底断联。

主从架构

至少部署两台权威 DNS 服务器,放在不同的物理位置(最好不同机房):

bind
; 主服务器 zone "example.com" { type master; file "/etc/bind/db.example.com"; allow-transfer { 192.0.2.10; }; also-notify { 192.0.2.10; }; }; ; 从服务器 zone "example.com" { type slave; file "/etc/bind/db.example.com.slave"; masters { 192.0.2.1; }; };

从服务器自动同步区域文件,主服务器挂了从服务器继续提供解析。关键是要确保 allow-transfer 只允许你的从服务器,防止区域传送泄露被利用。

DNS 轮询负载均衡

最简单的负载均衡——同一个域名配置多条 A 记录:

dns
www.example.com. 600 IN A 192.0.2.1 www.example.com. 600 IN A 192.0.2.2 www.example.com. 600 IN A 192.0.2.3

递归服务器每次查询会拿到不同顺序的 IP 列表,客户端通常取第一个,从而达到分发效果。

但 DNS 轮询有个硬伤:它不知道后端服务器健不健康。如果 192.0.2.2 挂了,DNS 轮询还是会把流量分给它。所以生产环境要用智能 DNS(如 Route 53、Cloudflare),配合健康检查自动摘除故障节点。

Anycast:一个 IP 多个节点

Anycast 让多个物理服务器共享同一个 IP,BGP 路由自动把请求导向最近的节点。这是大型 DNS 服务(8.8.8.8、1.1.1.1)的标准做法。

好处是:自动负载均衡、自动故障转移、就近响应降低延迟。缺点是配置复杂,需要 BGP 支持,小团队通常直接用云厂商的 Anycast DNS 服务。

故障切换脚本

对于小规模部署,写个简单脚本监控主 DNS 并自动切换:

bash
#!/bin/bash PRIMARY="192.0.2.1" BACKUP="192.0.2.2" DOMAIN="example.com" if ! dig @$PRIMARY $DOMAIN +short > /dev/null 2>&1; then echo "Primary DNS down, switching to backup" echo "nameserver $BACKUP" > /etc/resolv.conf # 发告警通知 curl -s "https://hooks.example.com/alert?msg=DNS+failover+triggered" fi

这只是应急手段。真正的生产环境应该用 keepalived 或云厂商的 DNS 故障切换功能,自动检测、自动切换、自动回切。

安全:DNS 是最容易被忽视的攻击面

DNS 劫持和 DNS 放大攻击是两种最常见的 DNS 安全威胁。

DNSSEC 防篡改

DNSSEC 给 DNS 响应加上数字签名,客户端可以验证响应是否被篡改。启用 DNSSEC 验证:

bind
options { dnssec-validation auto; };

部署 DNSSEC 的主要工作量在密钥管理——KSK(密钥签名密钥)和 ZSK(区域签名密钥)需要定期轮换,操作失误会导致域名解析全部失败。建议用自动化工具管理密钥轮换,不要手动操作。

DoH/DoT 加密查询

传统 DNS 查询是明文的,ISP 或中间人可以看到你查询了什么域名。DoT(DNS over TLS,端口 853)和 DoH(DNS over HTTPS,端口 443)加密了查询过程。

bash
# 配置 DoT(systemd-resolved) [Resolve] DNS=1.1.1.1#cloudflare-dns.com 8.8.8.8#dns.google DNSOverTLS=opportunistic

对于企业内部,推荐所有客户端统一使用 DoH/DoT 连接内部递归 DNS,防止内网 DNS 查询被窃听。

限制递归查询

开放递归的 DNS 服务器会被利用做 DNS 放大攻击——攻击者伪造源 IP 发送查询,你的服务器把大量响应发到受害者 IP。一定要限制递归查询只服务可信客户端:

bind
acl trusted { 192.0.2.0/24; 10.0.0.0/8; }; options { allow-recursion { trusted; }; recursion-clients 1000; };

监控:优化效果得靠数据说话

做了一堆优化,怎么验证效果?必须建立监控体系。

响应时间:用 dig 简单测量,或者用专业工具持续采集:

bash
# 简单测量单次查询延迟 dig @8.8.8.8 example.com | grep "Query time"

缓存命中率:BIND 用 rndc stats 查看,目标 80% 以上。

可用性:从多个地域持续探测 DNS 是否可达。Cloudflare 的 1.1.1.1 之所以快,不是因为它运算更快,而是因为全球 200+ 节点保证就近响应。

关键指标看板

  • P50/P95/P99 查询延迟
  • 缓存命中率
  • 查询失败率
  • 递归查询占比(越低越好,说明缓存有效)

优化 DNS 没有银弹,它是一个从客户端到服务端、从前端到基础设施的系统工程。先找到瓶颈在哪,再针对性优化——TTL 调优和缓存是最快见效的,Anycast 和智能 DNS 是长期投入但收益最大的,安全加固是容易被忽略但出事就致命的。

标签:DNS