先给结论:多层缓存返回不同版本,通常不是“哪一层坏了”,而是各层的键、过期时间和失效通知没有对齐。定位时不要逐层清缓存,而要先固定一个请求,记录每层返回的版本标识,再比较哪一层的键或过期时间与其它层不一致。下面用两个解释和一组可核对证据说明怎么区分。
假设同ip网站上有多个站点共享同一套缓存链路:浏览器、CDN、反向代理、应用内对象缓存。运维清了一遍CDN,用带查询串的地址访问看到新版本,但用不带查询串的地址访问仍是旧版本;换一台没访问过的设备,结果又变。直觉会认为“还有一层没清干净”,但这个现象也可能来自缓存键设计,而不是残留。
关键动作是:选一个页面,在URL后加一个从未用过的查询串作为探针,分别请求各层能直接观察的入口,把每层返回的版本标识(如页脚版本号、响应头里的构建标记)记下来。这个动作的结果决定了下一步:如果探针地址在所有层都返回新版本,问题在缓存键或失效通知;如果探针地址在某层仍返回旧版本,问题在该层的存储或回源。
多层缓存各自计算键。CDN可能把查询串计入键,反向代理可能忽略查询串,应用内缓存可能按用户会话或语言再分键。只要有一层忽略的参数与另一层不同,同一逻辑页面就会在两层里对应不同条目。此时你清掉的是其中一份,另一份自然还在。
能区分这个解释的证据是:用探针查询串请求,若某一层对带串和不带串返回不同版本,而其它层一致,说明差异出在该层的键规则,而不是全局残留。另一个证据是同一URL在不同Accept-Language或不同Cookie下返回不同版本,而缓存配置里这些维度并未统一声明。
把各层的缓存键规则列出来,逐项对照:查询串、请求方法、Cookie、语言、设备类型。若发现只有一层把查询串计入键,就统一规则或统一在回源前归一化URL。做完后重新用探针请求,若各层版本标识一致,下一步才是验证真实用户路径,而不是继续清缓存。
多层缓存通常靠发布时主动失效来更新,而不是等过期。如果发布流程只通知了CDN,没通知反向代理或应用内缓存,那些层会继续提供旧版本,直到各自的TTL到期。这也能解释“清完一层后部分入口变新、部分入口仍旧”。
能区分这个解释的证据是时间行为:如果旧版本在某个固定时长后自行消失,且该时长与某层配置的TTL吻合,指向失效通知缺失;如果旧版本长期不消失,且探针地址也命中旧版本,更可能是键不一致或存储残留。注意,请求量或抓取量归零不能单独证明失效成功,它也可能只是探针没被记录或该入口未被访问。
在发布流程里加一步:记录每层失效接口的调用结果,并立即用探针地址回读各层版本标识。若某层回读仍是旧版本,就把该层加入强制失效清单,而不是靠等待TTL。这个动作的结果会告诉你失效链路缺哪一环,下一步是补通知还是改键规则。
假设某同ip网站有三层:CDN、Nginx反向代理、应用内缓存。发布新版本后,CDN已失效,Nginx仍返回旧版,应用内缓存也返回旧版。用探针查询串请求,CDN返回新版,Nginx返回旧版,应用内缓存返回旧版。这个结果把问题指向Nginx的键规则或失效通知,而不是CDN。若Nginx配置里忽略了查询串,那么带串和不带串会命中同一旧条目;若Nginx没收到失效通知,则旧条目会等到TTL。两者可用“旧版是否在固定时长后消失”进一步区分。这个例子只用于说明比较方法,不代表任何真实项目结果。
先固定探针请求,再逐层记录版本标识,最后才决定清哪一层。不要一上来就全量清缓存,那会同时改变多个变量,让你无法判断哪一层原本有问题。也不要把robots.txt的抓取限制当成索引移除手段,它和缓存版本一致性是两件事。站点地图不保证收录,HTTPS也不保证缓存行为一致,这些都不能替代逐层核对。
当各层版本标识对齐后,再用真实用户路径复测一次;若仍不一致,把范围缩到浏览器缓存或本地网络,而不是继续在服务端缓存里找原因。