先给结论:多数“不同版本”不是缓存坏了,而是各层对同一资源的版本标识不一致。定位时不要先清缓存,而要先固定一个请求,把经过的每一层响应头、缓存键和回源结果逐层记录下来,找出第一个与源站不一致的层。只有确认某层返回了旧内容且其缓存键无法区分新旧版本,才需要针对该层处理。
常见场景是浏览器或边缘节点显示新页面,而另一条链路仍返回旧页面。表面看是“缓存没刷新”,但至少有两种合理解释。
两种解释对应的动作完全不同。前者要修缓存键或刷新策略,后者要修源站的内容协商逻辑。若不加区分就全量清缓存,旧内容可能暂时消失,但下一次请求仍会重新缓存错误变体。
区分的关键是看“谁先产生差异”。可以按请求链路从外到内逐层检查,并记录每层的响应头与回源标记。
ETag、Last-Modified、Age、Vary 以及自定义的版本头。若同一时刻不同层给出的 ETag 不同,差异发生在更靠内的层。这里有一个容易误判的点:请求量或抓取量突然归零,并不能单独证明缓存处理正确。它也可能是访问路径改变、监控采样调整或上游限流造成的。要结合版本标识和回源记录一起判断。
确认差异层之后,通常面临两个选择。
做法一:扩大缓存键,把版本维度纳入键。当差异确实来自查询参数、Cookie或语言等可枚举维度,且这些维度数量可控时,这种做法成立。代价是缓存命中率下降、存储条目增多,边缘节点需要更多内存和回源带宽。动作上,可以先只对出现问题的路径增加维度,观察命中率和回源量的变化,再决定是否推广到其他路径。
做法二:保持缓存键不变,改为在源站统一版本输出。当差异来自源站内部多个应用实例或灰度发布,且这些实例本应返回同一版本时,这种做法成立。代价是需要先修发布流程或内容协商逻辑,短期内旧版本仍可能被缓存。动作上,可以先在源站增加版本标识并让各实例对齐,再逐层刷新,而不是先清空所有缓存。
假设某站点有两个边缘节点,节点A返回新版本,节点B返回旧版本。若检查发现节点B的缓存键忽略了某个查询参数,而该参数正是版本切换开关,那么扩大缓存键是合理选择。若检查发现两个节点请求源站时,源站因负载均衡到了不同应用实例,而实例间版本未同步,那么应先统一源站输出,再处理缓存。这个例子只用于说明比较方法,不代表真实项目结果。
处理之后,不要只看单个页面是否恢复。应选择一组具有代表性的URL,覆盖不同缓存层级和内容类型,分别记录处理前后的版本标识、命中状态和回源状态。若某层仍返回旧版本,继续按“第一个不一致层”的方法向内排查。
验证时还要注意,某些响应头可能被中间层改写或剥离,因此不能只依赖一个头判断。至少结合两个独立信号,例如版本标识加回源日志,才能降低误判。若问题涉及多个搜索引擎或平台,其对缓存和版本标识的支持情况需分别核查,不能用一个渠道的结论套用到全部渠道。
最终判断标准是:同一请求在每一层看到的版本标识与源站当前值一致,且这种一致在多次请求和不同入口下保持稳定。达到这个状态后,再决定是否调整缓存时长或监测频率,而不是反过来用缓存时长掩盖版本不一致。