5月28日 01:48

DNS 中的 CNAME 和 A 记录有什么区别?什么时候该用哪个?

A 记录把域名直接指向 IP 地址,CNAME 记录把域名指向另一个域名。这是两者最根本的区别,但它带来的连锁影响远不止于此——根域名能不能用 CNAME、CNAME 为什么不能和其他记录共存、CDN 接入该选哪种记录,都源于这个根本差异。

A 记录:域名到 IP 的直接映射

A 记录(Address Record)将域名直接解析到 IPv4 地址,是 DNS 最基础的记录类型。

dns
www.example.com. 3600 IN A 192.0.2.1

一条 A 记录就是一次直接映射:查询 www.example.com,DNS 服务器直接返回 IP 地址,不需要额外查询。

同一个域名可以配置多条 A 记录指向不同 IP,DNS 服务器会轮询返回,实现简单的负载均衡:

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

A 记录的关键特性:

  • 查询效率最高,一次 DNS 查询即可获得 IP
  • 根域名(如 example.com)可以使用 A 记录
  • 可以与 MX、TXT、SRV 等其他记录类型共存
  • IP 变更时需要手动修改每条 A 记录

CNAME 记录:域名的别名

CNAME 记录(Canonical Name Record)创建域名的别名,指向另一个域名而非 IP 地址:

dns
blog.example.com. 3600 IN CNAME example.github.io.

解析 CNAME 时,DNS 客户端需要再做一次查询才能拿到最终 IP:

shell
查询 blog.example.com → 返回 CNAME: example.github.io. → 再查询 example.github.io. → 返回 IP: 185.199.108.153

这就意味着 CNAME 的解析比 A 记录多一次查询,增加了 10-50ms 的延迟(具体取决于目标域名是否已缓存)。

CNAME 的关键特性:

  • 目标 IP 变化时自动跟随,无需手动修改
  • 多个子域名可以指向同一个目标,管理方便
  • 适合接入 CDN、GitHub Pages 等第三方服务
  • 不能用于根域名
  • 不能与同一域名下的其他记录类型共存

核心区别对比

维度A 记录CNAME 记录
指向目标IPv4 地址另一个域名
DNS 查询次数1 次至少 2 次
根域名是否可用可用不可用
能否与其他记录共存可以不可以
IP 变更时的维护需手动逐条修改自动跟随目标域名
典型场景自有服务器、根域名CDN、第三方托管

CNAME 的三个重要限制

1. 根域名不能使用 CNAME

dns
; 错误 @ 3600 IN CNAME example.herokuapp.com. ; 正确 @ 3600 IN A 192.0.2.1

原因在于 RFC 1034 的规定:根域名必须同时存在 NS 记录和 SOA 记录,而 CNAME 不允许与其他记录类型共存。如果根域名设了 CNAME,DNS 就无法正常返回 NS 和 SOA 记录,整个域名的解析会出问题。

2. CNAME 不能与其他记录共存

dns
; 错误:CNAME 与 MX 记录冲突 www.example.com. 3600 IN CNAME example.com. www.example.com. 3600 IN MX 10 mail.example.com.

RFC 规定,一个域名一旦设置了 CNAME 记录,就不能再设置任何其他记录(DNSSEC 的 RRSIG 除外)。这是因为 CNAME 的语义是"我就是另一个名字",所有对这个域名的查询都应该被重定向到目标域名去处理。如果允许共存,DNS 服务器在返回 CNAME 的同时还需要返回其他记录,会造成语义冲突。

这意味着:如果一个子域名需要配置 MX 记录(收邮件)或 TXT 记录(SPF 验证),就不能用 CNAME,只能用 A 记录。

3. CNAME 链不宜过长

dns
; 不推荐:多级 CNAME 链 a.example.com → b.example.com → c.example.com → d.example.com ; 推荐:直接指向最终目标 a.example.com → final-target.com

每一级 CNAME 都增加一次 DNS 查询和解析延迟。实际使用中建议 CNAME 链不超过 2-3 级。某些 DNS 解析器对超过 5-8 级的 CNAME 链会直接返回错误(SERVFAIL)。

实际场景怎么选

自有服务器 — 用 A 记录

dns
www.example.com. 3600 IN A 192.0.2.1 mail.example.com. 3600 IN A 192.0.2.2 @ 3600 IN A 192.0.2.1

IP 地址在你自己控制之下,变更频率低,A 记录性能最优。

CDN 加速 — 用 CNAME

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

CDN 的边缘节点 IP 会频繁调整,CNAME 让你不需要追踪这些 IP 变化。

第三方托管服务 — 用 CNAME

dns
blog.example.com. 3600 IN CNAME username.github.io. app.example.com. 3600 IN CNAME example-app.herokuapp.com. www.example.com. 3600 IN CNAME cname.vercel-dns.com.

GitHub Pages、Vercel、Heroku 这类服务的 IP 可能随时变化,CNAME 自动跟随。

根域名指向第三方服务 — 特殊处理

根域名不能用 CNAME,但很多场景又需要指向第三方服务。有两种解决方案:

方案一:CNAME Flattening(推荐)

Cloudflare 等服务商支持在根域名上配置 CNAME,但实际解析时由 DNS 服务器将 CNAME 展开为 A 记录返回:

dns
@ 3600 IN CNAME example.cdn-provider.com. ; DNS 服务器解析时自动展开为 A 记录返回给客户端

客户端拿到的是 A 记录,不存在记录冲突问题,同时 IP 变更由服务商自动处理。

方案二:A 记录 + 手动维护

dns
@ 3600 IN A 203.0.113.1 @ 3600 IN A 203.0.113.2

向服务商获取其 IP 地址,直接配置 A 记录。缺点是 IP 变更时需要手动更新。

需要邮件服务的子域名 — 用 A 记录

dns
mail.example.com. 3600 IN A 192.0.2.2 mail.example.com. 3600 IN MX 10 mail.example.com. mail.example.com. 3600 IN TXT "v=spf1 ip4:192.0.2.2 ~all"

因为 MX 和 TXT 记录与 CNAME 冲突,只能选 A 记录。

面试常见追问

为什么根域名不能用 CNAME?

RFC 1034 规定 CNAME 不能与其他记录类型共存,而根域名必须有 NS 和 SOA 记录。如果根域名设了 CNAME,NS 和 SOA 记录就无法存在,DNS 解析链会断裂。CNAME Flattening 通过在服务端将 CNAME 展开为 A 记录来绕过这个限制,但严格来说它已经不是标准 CNAME 行为了。

CNAME 和 A 记录能同时存在吗?

不能。RFC 规定同一域名下 CNAME 与其他记录互斥。如果同时配置,大部分 DNS 服务器会忽略 CNAME 记录,只返回 A 记录。

CNAME 的性能损失有多大?

CNAME 多一次 DNS 查询,增加 10-50ms 延迟。但现代 DNS 递归服务器会缓存中间结果,第二次查询通常命中缓存,实际影响可忽略。真正需要关注的是 CNAME 链过长导致的累积延迟和解析失败风险。

DNAME 和 CNAME 有什么区别?

CNAME 为单个域名创建别名,DNAME 为整个子域名树创建别名。比如 example.com DNAME example.org 会让 www.example.com 自动解析为 www.example.org。DNAME 在实际中使用较少,主要见于域名迁移场景。

速查表

场景推荐记录原因
根域名A / ALIAS / CNAME FlatteningCNAME 不允许
自有服务器A 记录性能最优
CDN 接入CNAMEIP 自动跟随
GitHub Pages / Vercel 等CNAME第三方 IP 变更时无需手动维护
需要邮件验证的域名A 记录CNAME 与 MX/TXT 冲突
多子域名统一指向CNAME改一处生效全部
标签:DNS