DNS分地区解析叠加CDN调度,会不会把缓存搞乱

这个问题在实际项目里经常遇到,尤其是业务本身已经做了DNS分地区解析,比如华东用户解析到上海源站,华南用户解析到广州源站,海外用户解析到新加坡或美国节点;后面又接了一层CDN,希望让CDN继续按用户位置、运营商、链路质量去调度。看起来都是“就近访问”,但很多人担心两层调度叠在一起以后,缓存会不会互相串、会不会华东缓存命中华南源站内容、会不会动态接口拿到别的区域数据。

先把结论放在前面:DNS分地区解析和CDN调度本身不会天然产生缓存冲突,真正容易出问题的是Host、URL、缓存Key、回源地址、Header、Cookie、Vary、源站内容一致性这些配置没有统一好。也就是说,调度只是把请求送到哪里,缓存冲突通常发生在“CDN认为哪些请求是同一个资源”的判断上。

先分清两件事:DNS调度和CDN缓存不是同一个层面的逻辑

DNS分地区解析解决的是域名解析结果。用户访问www.example.com时,递归DNS根据用户来源、运营商、地理位置等条件返回不同IP。这个IP可能是源站IP,也可能是CDN CNAME背后的调度IP。

CDN调度解决的是用户请求进入CDN体系以后,分配到哪个边缘节点、哪个上层节点、回哪个源。CDN缓存判断的是HTTP层面的内容,比如URL、Host、Query String、Header、Cookie,以及配置里的缓存规则。

实际使用中发现,很多所谓“缓存冲突”,现场抓包一看,不是DNS和CDN打架,而是缓存Key太粗。比如不同地区返回的内容不一样,但URL完全一样,CDN又只按Host + URI做缓存Key,那第一个地区把内容写进缓存后,另一个地区访问同一个URL就可能命中这份缓存。

最典型的冲突场景:同一个URL,不同地区内容不同

举个很常见的例子:

https://www.example.com/api/config

华东用户访问时,源站返回上海机房配置;华南用户访问时,源站返回广州机房配置;海外用户访问时,源站返回新加坡配置。URL一样,Host一样,Query String也一样。如果CDN缓存了这个接口,并且缓存Key没有带地区维度,那么CDN会把它当成同一个资源。

这时候问题就来了。第一个访问的是华东用户,CDN边缘节点从华东源站回源,拿到配置A并缓存。后续华南用户如果被调度到同一个CDN缓存层,或者命中上层缓存,就可能拿到配置A。用户看到的现象是“明明DNS分到华南了,为什么配置还是上海的”。

这里不是DNS分地区解析和CDN调度冲突,而是业务把“地区差异”藏在了源站返回里,却没有让CDN知道这个差异。

这种场景要么不缓存,要么把差异写进缓存Key

动态配置、用户态接口、地区化价格、地区化库存、AB Test、登录态页面,这类内容默认不要让CDN直接缓存,除非缓存规则非常清楚。

如果确实要缓存,可以把地区维度显式化。比如URL变成:

/api/config?region=cn-east

/api/config?region=cn-south

或者让CDN缓存Key包含指定Header,例如:

X-Region: cn-east

X-Region: cn-south

但这里补充一点,不是所有CDN默认都会把自定义Header纳入缓存Key。很多CDN默认只看Host、Path、Query String,有些甚至默认忽略部分Query参数。配置前要确认CDN的缓存Key规则,而不是只在源站代码里加Header。

静态资源通常不会冲突,但也有例外

图片、CSS、JS、安装包、视频切片这类静态资源,如果同一个URL在全球各源站内容完全一致,DNS分地区解析加CDN调度一般没有问题。CDN命中哪个边缘节点都一样,回哪个区域源站也一样,缓存内容一致。

真正容易出事的是“URL一样但文件内容在不同源站不同”。比如:

/static/app.js

上海源站已经发布v2,广州源站还是v1。CDN某个节点从广州回源拿到v1并缓存,另一个用户从上海链路访问却命中了v1。业务侧看到的就是灰度发布错乱、页面白屏、接口字段不匹配。

这类问题在多机房发布时很常见。CDN不是发布系统,它不会自动理解哪个源站是新版本,哪个源站是旧版本。只要URL一样,缓存Key一样,它就会认为资源一样。

静态资源更建议做版本化URL

不要长期使用固定文件名覆盖发布,例如:

/static/app.js

更稳的方式是:

/static/app.20250118.js

/static/app.a8f3c9.js

文件内容变,URL也变。这样CDN缓存不会混,回滚也清晰。多机房源站哪怕有短暂同步延迟,影响也比覆盖式发布小很多。

多说一句,如果业务必须覆盖同名文件,那就要配合CDN刷新、预热、源站同步检查。刷新不等于全网立即一致,实际大CDN平台通常会有几十秒到几分钟的传播过程。对强一致发布敏感的业务,不要把希望全压在“刷新一下缓存”。

DNS分地区解析接CDN时,常见架构有两种

一种是用户域名直接CNAME到CDN

这种方式最常见。www.example.com CNAME到CDN厂商提供的域名,用户解析时进入CDN调度系统。CDN根据用户IP、Local DNS、EDNS Client Subnet、节点负载、运营商线路等因素调度到边缘节点。

回源时,CDN再根据配置选择源站。源站可以是单个IP,也可以是多个IP,也可以按区域回不同源。这里的关键是:DNS分地区解析主要由CDN接管,业务自己的智能DNS作用会弱很多。

如果还想保留分地区回源能力,应该在CDN的“分区域回源”“回源Host”“源站组”“线路回源”里配置,而不是指望用户侧DNS解析结果继续影响CDN回源。因为用户请求进入CDN后,源站看到的是CDN回源节点,不再是原始用户直连。

另一种是智能DNS把不同地区用户分到不同CDN域名

比如:

华东用户:www.example.com -> cdn-east.example-cdn.com

华南用户:www.example.com -> cdn-south.example-cdn.com

海外用户:www.example.com -> cdn-global.example-cdn.com

这种玩法在多CDN、跨境访问、游戏更新包、海外业务里会看到。优点是调度掌控更细,可以把不同地区导到不同CDN厂商或不同产品线。缺点是配置复杂,缓存规则、证书、回源、刷新、日志都要分别管理。

这里容易产生一种误判:以为分了不同CDN域名,缓存就一定隔离。要看CDN厂商内部是否共享缓存层,也要看配置的Host是否一致。如果最终回源Host和缓存Host都归一成www.example.com,某些平台仍可能在上层缓存形成共享。大部分正规CDN会按加速域名隔离缓存,但多CDN平台、代理层、自建Nginx缓存层要自己确认。

缓存冲突一般从缓存Key开始查

排查这类问题,不要先盯着DNS。先看命中的到底是哪份缓存。

常见缓存Key组成大概是:

scheme + Host + URI + Query String

也有平台会支持加入Header、Cookie、设备类型、国家地区、运营商等维度。问题是维度越多,缓存越不容易命中;维度越少,越容易串内容。

实际配置要按内容类型拆开看:

静态资源:Host + Path + 完整Query通常够用,文件名最好带hash。

图片裁剪:需要保留宽高、水印、格式等Query参数,不能被CDN忽略。

地区化内容:地区字段必须进入URL、Query或缓存Key。

登录态内容:不要缓存,或按Cookie/User维度做私有缓存,但这类配置非常容易踩坑。

API接口:默认不缓存,除非明确知道返回内容与用户无关,并且TTL很短。

一个现场问题:华南用户命中华北库存

之前碰到过类似问题,电商活动页在CDN上配置了5分钟缓存,URL是:

/activity/detail?id=888

页面里有库存状态。业务源站按用户地区返回不同仓的库存,但URL里没有地区参数,Cookie里有地区信息。CDN配置没有把Cookie纳入缓存Key,于是华北用户先访问,CDN缓存了“有货”;华南用户后访问,同一个活动页直接命中缓存,也看到“有货”,下单时才发现不可售。

这个问题的修法不是“关闭DNS分地区解析”,而是把活动页拆开。活动页静态框架可以缓存,库存状态通过接口实时查;接口不走CDN缓存,或者走短TTL并带region参数。页面缓存和库存接口分离以后,命中率还在,错误库存也消失了。

CDN回源和DNS分地区解析叠加时,要注意回源看到的不是用户

很多人做智能DNS时会按访问者IP判断地区。用户直连源站时,这没问题。但接入CDN后,源站看到的是CDN节点IP。如果源站继续按remote_addr判断用户地区,就会把CDN节点所在地当成用户所在地。

比如用户在广州,CDN边缘节点在深圳,上层回源节点在上海,源站看到上海IP,然后返回华东内容。用户侧看起来是“DNS明明分到华南,CDN也就近了,为什么内容是华东”。根因是源站取错了用户IP。

这时要从CDN传递的Header里拿真实用户IP,常见是:

X-Forwarded-For

X-Real-IP

True-Client-IP

CF-Connecting-IP

具体看CDN厂商。源站还要做可信代理校验,不能直接相信公网传来的X-Forwarded-For,否则很容易被伪造。比较稳的做法是只信任CDN回源IP段传来的真实IP Header。

多源站内容一致性,比DNS配置更容易被忽略

DNS分地区解析通常会对应多个源站:北京、上海、广州、新加坡、洛杉矶。CDN回源如果也按区域走,源站内容一致性就非常关键。

静态文件同步延迟30秒,看起来不长,但如果CDN在这30秒里回源拿到了旧文件,缓存TTL又是1小时,那旧内容就会被放大成1小时的问题。缓存会把源站短暂不一致放大,这是CDN接入后很典型的现象。

所以多源站架构里,建议发布系统先确保所有区域源站文件一致,再开放流量;或者先发布新版本文件名,再切HTML引用。不要在多个源站上覆盖同名文件,然后靠CDN自然过期。

海外和跨境场景里,调度链路还会更复杂

如果业务有中国大陆、东南亚、欧洲、北美用户,DNS分地区解析经常会和CDN、BGP、CN2、GIA线路一起使用。这里不只是缓存问题,还有访问质量问题。

例如游戏更新包、启动器资源、官网静态资源,一般会把国内用户送国内CDN,东南亚用户送新加坡或马来西亚节点,欧美用户送欧洲或美国节点。源站可以放在不同区域,但资源版本必须一致。否则CDN缓存会把版本差异扩散出去。

如果你也在找这种海外节点、云服务器、G口带宽或高防资源,可以看看129云。像马来西亚-B型适合东南亚访问入口,4C、4G DDR4 ECC、80G SSD、30Mbps峰值,三网优化,适合做轻量业务源站或区域回源节点;美国高防-C型带300G防御,更适合有DDoS压力的海外业务入口;德国双ISP-A型有1Gbps带宽和GTT直连,做欧洲静态资源源站或下载分发也比较常见。需要确认线路和业务匹配时,可以直接打客服热线400-9177118问清楚节点和防御策略。

什么配置最容易引发缓存串内容

忽略Query String

有些CDN为了提高命中率,会配置“忽略全部Query参数”。这对纯静态文件可能没问题,比如:

/logo.png?v=123

但对图片处理、API、地区参数就很危险:

/image?id=1&w=200

/image?id=1&w=800

如果w被忽略,200宽和800宽可能命中同一份缓存。

回源Host和访问Host混用

访问Host是www.example.com,回源Host可能配置成origin.example.com。缓存一般按访问Host算,但有些自建代理或多层Nginx会按回源Host缓存。如果中间层配置不规范,不同业务域名可能被归到同一个缓存目录。

自建CDN或Nginx反向代理尤其要注意proxy_cache_key。不要图省事写成:

proxy_cache_key $uri;

这样不同Host下相同路径会直接串。至少应该包含:

proxy_cache_key $scheme$host$request_uri;

如果有地区维度,还要继续加region变量。

Cookie没有进入判断,但源站按Cookie返回内容

源站按Cookie区分地区、语言、会员等级、实验分组,CDN却没有按Cookie区分缓存,这是事故高发区。

语言站点也常见:

/index.html

用户Cookie里是lang=en,源站返回英文;另一个用户Cookie是lang=zh,源站返回中文。CDN如果缓存了/index.html,后面就可能语言串掉。更稳的方式是把语言放进URL:

/en/index.html

/zh/index.html

Vary Header没被CDN正确处理

HTTP里有Vary机制,比如:

Vary: Accept-Encoding

Vary: Accept-Language

Vary: User-Agent

但不同CDN对Vary支持不完全一样。有些只特殊处理Accept-Encoding,有些可以按配置处理更多Header。不要以为源站返回了Vary,CDN就一定按预期拆缓存。

特别是User-Agent,如果直接纳入缓存Key,缓存碎片会非常严重。移动端和PC端建议用明确规则拆,比如m.example.com、/mobile/路径,或者CDN设备识别变量,而不是把完整User-Agent塞进缓存Key。

TTL不是越长越好,短TTL也不是万能

缓存冲突和TTL有直接关系。TTL越长,错误缓存影响越久。TTL越短,回源压力越大。

静态带hash文件可以设置很长,比如30天、180天甚至1年。因为文件内容变了URL也变。

HTML入口页可以短一点,比如30秒到5分钟,看发布频率和业务容忍度。

接口类内容要谨慎,能不缓存就不缓存;如果缓存,建议从10秒、30秒这种短TTL开始压测,并明确是否允许短时间旧数据。

地区化内容如果没有地区缓存Key,TTL设成1秒也可能串,只是串的时间短一点。配置错了不能靠短TTL兜住,尤其高并发下,1秒内也能影响大量请求。

DNS TTL和CDN缓存TTL也别混在一起看

DNS TTL控制解析结果在递归DNS里的缓存时间,比如60秒、300秒、600秒。CDN缓存TTL控制HTTP对象在CDN节点里的缓存时间。两者没有直接替代关系。

有些人看到用户还访问旧节点,就去刷新CDN缓存;也有人看到内容没更新,就去改DNS TTL。这两个动作经常不对应问题。

用户访问旧IP,查DNS链路:权威DNS、递归DNS、客户端DNS缓存、运营商Local DNS。

用户拿到旧内容,查CDN链路:边缘命中、上层命中、回源命中、源站内容、缓存Key、TTL、刷新状态。

排查时用dig、curl、响应Header一起看。比如:

dig www.example.com @114.114.114.114

curl -I https://www.example.com/static/app.js

curl -H "Host: www.example.com" -I http://源站IP/static/app.js

重点看X-Cache、Age、Via、Server、回源Host、命中节点标识。如果CDN提供日志,直接查request_id更快。

自建Nginx缓存层时,冲突更容易自己写出来

很多团队在CDN后面还会放一层Nginx缓存,或者在多个区域源站前面加反代。这个时候缓存冲突不一定发生在CDN,也可能发生在自己的Nginx。

危险配置示例:

proxy_cache_key "$uri";

这个写法会让:

www.a.com/index.html

www.b.com/index.html

共用同一个缓存Key。更不用说不同Query参数也会被丢掉。

更常见的写法应该至少包含:

proxy_cache_key "$scheme$request_method$host$request_uri";

如果业务按地区返回内容,可以加:

proxy_cache_key "$scheme$request_method$host$request_uri$http_x_region";

但不要随便把所有Header都塞进去。Header太多会导致缓存碎片,命中率掉得很厉害。缓存Key要表达“影响内容差异的字段”,不是越长越安全。

CDN调度可能变化,源站不要依赖某个固定节点

CDN节点调度会受很多因素影响:节点负载、链路质量、运营商故障、DDoS防护策略、区域封禁、成本策略。今天华南用户命中广州节点,明天可能命中深圳或香港节点。跨境场景下,调度变化更明显。

如果源站逻辑依赖“某个CDN节点一定代表某个用户区域”,后面一定会出问题。用户区域应该来自可信的真实用户IP解析,或者由业务侧显式传递并参与缓存Key,而不是从CDN节点IP反推。

CDN回源源站也一样。不要假设某个边缘节点只会回某个源。除非CDN配置里明确做了区域回源,并且故障切换策略也清楚。很多平台在源站异常时会自动切到备用源,如果备用源内容不一致,缓存就会把这个异常变成更大范围的问题。

推荐的配置思路:把可缓存和不可缓存拆干净

实际项目里更稳的做法不是把所有请求都塞进CDN缓存,而是按内容拆。

静态资源走长缓存,文件名版本化。

HTML入口页走短缓存或协商缓存。

用户态接口不缓存。

地区化接口要么不缓存,要么显式带region并进入缓存Key。

下载包、补丁包、视频切片这类大文件适合CDN缓存,但发布流程必须保证源站内容一致。

如果做多地区源站,DNS可以负责大方向调度,CDN负责边缘加速和链路优化。源站内容差异要显式暴露给CDN,不要藏在Cookie、源站IP判断、内部路由里。

线上排查时可以按这个顺序看

先确认用户实际解析到了哪里。不同地区用dig指定递归DNS查一次,也可以让用户本地nslookup截图。注意Local DNS可能和用户真实所在地不完全一致,尤其移动网络、企业出口、海外VPN场景。

再确认请求命中的CDN节点和缓存状态。curl -I看Age和X-Cache。如果Age很大,说明确实命中了旧缓存;如果X-Cache是MISS但内容还是错,问题可能在回源或源站。

接着绕过CDN直连不同源站,对比同一个URL的返回内容。用Host Header访问源站IP,确认上海源、广州源、新加坡源返回是否一致。

然后看缓存Key配置。Query是否被忽略,Cookie是否参与,Header是否参与,CDN是否支持Vary,是否有上层缓存共享。

最后看发布和刷新记录。很多问题发生在发布窗口:源站A已更新、源站B未更新、CDN刚好从源站B回源并缓存。这个时间点对上以后,原因基本就清楚了。

有DDoS和高防场景时,缓存规则更要保守

高防CDN或高防IP前面经常会做清洗、代理、回源隐藏。攻击来时,调度路径可能发生变化,请求可能从清洗节点进入,再到CDN或源站。如果缓存Key设计不稳,攻击流量还可能把异常响应打进缓存。

比如源站在高压下返回了临时错误页,CDN把500页面缓存了30秒,正常用户也跟着看到错误页。这个不算地区缓存冲突,但线上观感很像“CDN缓存错了”。

错误状态码缓存要单独配置。一般200、206可以按规则缓存,301/302看业务,404可以短缓存,500/502/503尽量不缓存或只缓存极短时间。下载类业务要特别注意206 Range请求,缓存策略不对会导致断点续传异常。

什么时候可以放心组合DNS分地区解析和CDN调度

同一个URL在所有地区源站返回完全一致,适合组合。

静态资源文件名带hash,适合组合。

CDN按加速域名隔离缓存,缓存Key包含必要Query,适合组合。

源站不依赖CDN节点IP判断用户地区,适合组合。

地区化内容已经显式写入URL、Query或Header,并且CDN缓存Key确认生效,适合组合。

源站多地区发布有一致性校验,适合组合。

什么时候要谨慎

同一个URL按地区返回不同内容,但CDN缓存Key没有地区维度。

源站按Cookie返回不同页面,但CDN配置忽略Cookie。

多源站发布不同步,还使用固定文件名覆盖发布。

智能DNS把用户分地区,接入CDN后源站仍按remote_addr判断地区。

多CDN共用同一源站和同一域名,但刷新、预热、缓存规则不一致。

自建Nginx缓存层proxy_cache_key只写了URI。

一个比较稳的实际配置样例

www.example.com接CDN,CDN负责全国和海外调度。

/static/、/assets/、/download/走缓存,TTL 30天,文件名必须带版本号或hash。

/api/默认不缓存,Cache-Control设置为no-store或private。

/api/public/config如果需要缓存,URL里必须带region和version,比如:

/api/public/config?region=cn-south&v=20250118

CDN缓存Key保留完整Query,不忽略region和v。

HTML入口页TTL 60秒,发布时配合刷新。

源站通过可信CDN Header获取真实用户IP,不使用CDN回源IP判断用户地区。

多地区源站发布完成后做文件hash校验,再开放CDN回源流量。

错误状态码不缓存,或者500/502/503只缓存1秒以内。

最后说一个容易省事但后面会痛的做法

为了提高命中率,把全站配置成忽略Query、缓存所有200响应、TTL 1小时,这种配置短期看回源压力很漂亮,后面只要业务里出现地区化、登录态、灰度、库存、价格、语言,就会开始串。

CDN缓存不是越激进越好。DNS分地区解析也不是越细越好。两者结合时,关键是让“内容差异”在缓存规则里可见。只要CDN能区分哪些请求应该共享缓存、哪些请求必须隔离,DNS负责地区入口,CDN负责节点调度,这两层可以正常配合。

如果业务已经在线上跑,改之前先拿几个典型URL做验证:静态文件、HTML、地区化接口、登录态接口、下载文件。分别从华东、华南、海外网络访问,对比DNS结果、CDN命中、源站返回和缓存Key。很多缓存冲突不需要猜,curl几次基本就能定位到是哪一层把内容当成同一个对象了。