DNS 负载均衡有哪些常见算法?
DNS 负载均衡是在 DNS 解析阶段将用户请求分发到不同服务器的技术。它的核心思路是:同一个域名配置多条记录,DNS 服务器按照特定算法决定返回哪条记录对应的地址。面试中常考的是算法原理、各自的局限性,以及 DNS 负载均衡与应用层负载均衡的本质区别。
真正在 DNS 层工作的算法
先明确一点:DNS 是无状态协议,每次查询相互独立,DNS 服务器无法感知后端服务器的实时负载或连接数。因此,像"最少连接""最快响应"这类依赖实时状态的算法,在 DNS 层根本无法实现——它们属于应用层负载均衡(Nginx、HAProxy)的范畴。DNS 层能用的算法,本质上都只能基于静态配置或客户端特征做决策。
轮询(Round Robin)
最基础的算法。DNS 服务器维护一个 IP 列表,每次查询按顺序返回下一个 IP。
dns; BIND 配置示例 www.example.com. IN A 192.0.2.1 www.example.com. IN A 192.0.2.2 www.example.com. IN A 192.0.2.3
BIND 默认对同一域名的多条 A 记录做轮询。第一次查询返回 192.0.2.1,第二次返回 192.0.2.2,依此循环。
局限:不区分服务器性能差异,不感知服务器是否宕机。如果某台服务器挂了,DNS 仍会把流量分过去,直到手动剔除该记录。
加权轮询(Weighted Round Robin)
给每条记录分配权重,权重高的 IP 被返回的概率更大。DNS 层的加权通常通过 SRV 记录实现:
dns; SRV 记录格式:_service._proto.name TTL IN SRV priority weight port target _http._tcp.example.com. IN SRV 10 60 80 server1.example.com. _http._tcp.example.com. IN SRV 10 30 80 server2.example.com. _http._tcp.example.com. IN SRV 10 10 80 server3.example.com.
三条记录优先级相同(10),权重分别为 60、30、10,流量大致按 6:3:1 分配。
局限:SRV 记录需要客户端主动支持。浏览器访问网页用的是 A/AAAA 记录,不查 SRV 记录,所以这个方案在 Web 场景基本无效。SRV 主要用在 SIP、LDAP、Active Directory 等服务发现场景。
对于纯 A 记录的加权,部分商业 DNS 服务(如 AWS Route 53、Cloudflare)通过自有系统实现了加权策略,但这不属于 DNS 协议本身的能力。
地理位置路由(GeoDNS)
根据客户端 DNS 查询的来源 IP 判断其地理位置,返回距离最近的服务器 IP。
dns; BIND view 配置示例 view "asia" { match-clients { asia-ips; }; zone "example.com" { type master; file "example.com.asia"; ; 返回亚洲服务器 IP }; }; view "europe" { match-clients { europe-ips; }; zone "example.com" { type master; file "example.com.europe"; ; 返回欧洲服务器 IP }; };
局限:判断位置用的是 DNS 递归服务器的 IP,不是用户真实 IP。如果用户用了 8.8.8.8 做解析,GeoDNS 看到的是 Google 的 DNS 节点 IP,位置判断可能偏差。另外,GeoIP 数据库本身也有精度问题。
运营商路由(ISP Routing)
根据客户端 IP 所属运营商,返回对应运营商线路的服务器 IP,避免跨网访问。
dnsview "telecom" { match-clients { telecom-ips; }; zone "example.com" { type master; file "example.com.telecom"; }; }; view "unicom" { match-clients { unicom-ips; }; zone "example.com" { type master; file "example.com.unicom"; }; };
局限:运营商 IP 段会调整,需要持续维护 IP 归属表。国内运营商之间的互联互通问题在改善,但仍然存在。
Anycast
将同一个 IP 地址分配给多台地理上分散的服务器,通过 BGP 路由协议让客户端的请求自动到达拓扑上最近的节点。根 DNS 服务器和大型公共 DNS(如 8.8.8.8、1.1.1.1)都使用 Anycast。
Anycast 与其他算法的区别:它不是在 DNS 响应里选择 IP,而是在网络层通过路由决定流量走向。客户端拿到的 IP 是一样的,但网络自动把包送到最近的节点。
局限:需要 BGP 自治域和网络运维能力,部署成本高。流量分布取决于路由拓扑,不完全可控。
DNS 负载均衡的核心限制
不管用哪种算法,DNS 负载均衡都有几个绕不过去的问题:
TTL 缓存问题
DNS 响应会被各级缓存(浏览器、操作系统、本地 DNS 服务器),缓存时间由 TTL 控制。TTL 设长了,服务器宕机后客户端还在用缓存的旧 IP;TTL 设短了,DNS 查询量增大,解析延迟上升。实际中 TTL 通常设 30~300 秒的折中值,但即便如此,故障切换仍需要等待缓存过期。
无法做健康检查
DNS 服务器不知道后端服务器是否存活(除非使用 Route 53 等商业服务附带的健康检查功能)。标准 DNS 协议没有定义健康检查机制。
无法做会话保持
同一客户端的两次 DNS 查询可能返回不同 IP,导致会话中断。解决方案是用源 IP 哈希(Source IP Hash),但标准 DNS 协议不支持,只有少数商业 DNS 服务提供。
DNS 层与应用层负载均衡对比
| 维度 | DNS 负载均衡 | 应用层负载均衡(Nginx/HAProxy) |
|---|---|---|
| 工作阶段 | DNS 解析,连接建立前 | 请求到达后,连接建立后 |
| 可用算法 | 轮询、加权、GeoDNS、ISP 路由、Anycast | 最少连接、最快响应、源 IP 哈希、一致性哈希等 |
| 状态感知 | 无状态 | 有状态,可跟踪连接数和响应时间 |
| 健康检查 | 无(商业服务除外) | 主动/被动健康检查 |
| 会话保持 | 困难 | Cookie/IP 哈希/Session 绑定 |
| 故障切换速度 | 慢,受 TTL 缓存影响 | 快,毫秒级 |
| 部署成本 | 低 | 中高 |
实际架构中两者通常配合使用:DNS 层把流量分发到不同机房,每个机房内部用应用层负载均衡分发到具体服务器。
shell客户端 DNS 查询 ↓ DNS 负载均衡(GeoDNS → 选择机房) ↓ ┌────┴────┐ ↓ ↓ 机房 A 机房 B ↓ ↓ Nginx Nginx (应用层负载均衡) ↓ ↓ 服务器集群 服务器集群
面试高频问题
DNS 负载均衡为什么只能用简单算法?
DNS 是无状态协议,每次查询独立,服务器无法追踪客户端连接状态。轮询和加权轮询只需要维护一个计数器或权重表,不依赖运行时状态。最少连接、最快响应需要知道每台服务器的实时连接数和响应时间,DNS 层拿不到这些数据。
追问:那商业 DNS 服务(Route 53)是怎么做健康检查的?——它们在 DNS 协议之外跑独立的健康检查服务,定期探测后端服务器,把不健康的 IP 从响应中剔除。这是服务层面的增强,不是 DNS 协议本身的能力。
DNS 负载均衡的 TTL 该设多少?
这取决于对故障切换速度和解析性能的取舍。短 TTL(3060 秒)意味着故障切换快,但 DNS 查询量大;长 TTL(3003600 秒)减少查询量,但故障切换慢。生产环境通常设 60~300 秒。
追问:TTL 设成 0 行不行?——技术上可以,但每次访问都要重新解析,严重影响性能。而且部分本地 DNS 服务器会忽略极低的 TTL,强制缓存更长时间。
GeoDNS 判断位置为什么不准?
GeoDNS 用的是 DNS 递归服务器的 IP 来判断位置,不是用户的真实 IP。如果用户配置了 8.8.8.8 作为 DNS,GeoDNS 看到的是 Google DNS 节点的 IP。Google 在全球有节点,但并非每个城市都有,判断就可能偏差。EDNS Client Subnet(ECS)协议可以传递客户端子网信息来缓解这个问题,但不是所有递归服务器都支持。
追问:ECS 有什么副作用?——ECS 把客户端 IP 前缀传给权威 DNS,增加了隐私泄露风险,也可能导致缓存膨胀(权威 DNS 需要为不同子网缓存不同响应)。
Anycast 和 GeoDNS 有什么区别?
两者都实现"就近访问",但机制不同。GeoDNS 在 DNS 响应阶段根据客户端位置返回不同 IP;Anycast 在网络路由阶段,多个节点共享同一个 IP,BGP 把流量送到拓扑最近的节点。Anycast 的"就近"由路由表决定,更精确但也更难控制;GeoDNS 的"就近"由 GeoIP 数据库决定,可控但精度受限于数据库质量。