DNS分地区解析和CDN调度结合用会不会产生缓存冲突
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几次基本就能定位到是哪一层把内容当成同一个对象了。