先确认一件事:多层缓存返回不同版本,通常不是某一层坏了,而是各层对“同一份资源”的判定依据不一致。定位顺序应从最外层往回查,先固定一个可复现的请求,再逐层比对响应头与缓存键,而不是同时改动所有层。保留、改写还是退出某层缓存,取决于这层是否持有独立且可核对的版本标识。
不同角色看到不同版本,往往因为各自请求的入口不同:有人带地区参数,有人走内网,有人命中了预热过的节点。第一步不是争论谁看到的对,而是把请求条件写死:完整 URL、请求方法、关键请求头(如 Accept-Encoding、Cookie 是否存在)、以及是否经过压缩。把这些条件固定后,让每一层都返回同一请求的结果,分歧才会从“版本不同”收敛为“哪一层先偏离”。
一个可执行动作是:在响应头里要求各层输出自己的缓存状态与所持版本标识(例如内容哈希或 ETag)。如果某层无法输出,就说明它没有可核对的版本依据,这一层应优先被怀疑,而不是被当作基准。这个动作的结果会直接决定下一步:能输出标识的层可以两两比对,不能输出的层只能靠旁路请求验证。
多层缓存最容易出问题的地方是缓存键(cache key)构成不同。外层可能把查询字符串、Cookie、设备类型都算进去,内层只认路径。于是同一个 URL 在两层里对应不同的对象,返回不同版本并不奇怪。判断方法是对比两层的键构成,而不是对比返回时间——时间戳只能说明谁更新,不能说明它们是否在描述同一份资源。
假设一个例子:某页面在 CDN 层按“路径 + 查询字符串”缓存,在应用层按“路径 + 用户语言 Cookie”缓存。当请求不带语言 Cookie 时,CDN 命中一份旧对象,应用层则生成默认语言的新对象,两者内容不同。此时正确做法不是清空所有缓存,而是先统一键的构成,或明确哪一层负责语言维度。这里数字只用于说明比较方法:两层键字段数量不同,就足以解释版本差异,不需要引入流量或命中率数据。
三种取舍各有前提,不必都选:
选择哪一种,取决于这层是否持有可核对的版本依据,而不是它看起来有多重要。没有依据的层,保留只会延长排查时间。
当多个角色对同一事实理解不同时,把争论转成清单比继续讨论更有效。清单至少包含:请求条件、每层输出的版本标识、每层的缓存键字段、以及最近一次失效或更新的触发方式。任何一项缺失,就在该项标注“待核对”,而不是用推测填补。
需要提醒的是,抓取量或请求量归零并不能单独证明某层缓存处理正确——它也可能是请求根本没到达该层、被上游拦截、或统计口径变化。同理,robots.txt 的抓取限制不等于可靠的索引移除,站点地图不保证收录。这些现象在排查缓存一致性时属于干扰项,不能当作版本正确的证据。
完成清单后,若各层版本标识一致而页面表现仍不同,问题可能已不在缓存层,而在于压缩、转码或客户端渲染差异。此时应把排查范围移到响应体本身,而不是继续调整缓存策略。