先停掉“哪一层坏了”的猜测,把同一路径在每一层缓存里的响应版本分别取出来,比对版本标识和缓存键,而不是比对页面文字。缺少完整数据或权限时,你能做的最小动作是:只针对一个URL,记录浏览器、CDN、反向代理、应用缓存各自返回的响应头与正文摘要,找出第一个版本开始分叉的位置。这个动作能缩小范围,但不能单独证明根因,因为分叉也可能来自请求头差异、地域节点或缓存键设计,而不是某一层配置错误。
选一个已经出现版本不一致的URL,不要一次抓全站。记录完整请求:协议、主机名、路径、查询串、Cookie是否携带、User-Agent、Accept-Encoding,以及请求发出的位置。多层缓存返回不同版本,最常见的原因不是缓存没刷新,而是不同层用了不同的缓存键。比如CDN把查询串计入键,反向代理忽略查询串,两者就会各自返回不同版本。
把这条请求在每一层各取一次响应,保存响应头中的版本线索:ETag、Last-Modified、Age、Cache-Control、Vary、X-Cache类字段(字段名以你实际环境为准)。正文只取可哈希的摘要,比如开头若干字节或整页哈希,避免把动态时间戳当成版本差异。
这一步的结果直接决定下一步:如果所有层的ETag一致、只有正文摘要不同,问题更可能在边缘做了内容改写或压缩差异;如果ETag在某一层之后开始变化,就该把注意力放在那一层及其上游。
按请求经过的顺序排列各层:浏览器缓存、CDN边缘、CDN中间层、反向代理、应用内缓存、源站。逐层比对两个东西:缓存键是否相同,版本标识是否相同。
一个假设例子:某URL带?v=2,CDN把查询串计入键,反向代理配置为忽略查询串。此时CDN返回v2,反向代理把请求归并到无查询串的旧对象,返回v1。这个例子只用于说明比较方法,不代表任何真实环境的结果。判断依据是两层的键定义,而不是谁“应该”更新。
如果只能看到响应头、看不到缓存配置,就反过来用证据推断:对比带查询串与不带查询串的两次请求,若某一层对两者返回同一ETag,说明该层很可能忽略了查询串。这个推断有边界,它不能排除该层只是恰好持有相同对象。
多层返回不同版本,还有一类合理解释:请求根本没走到同一条路径。以下现象都会造成版本差异,且与缓存刷新无关。
Vary,比如语言、编码、设备类型被计入变体。要区分这些原因,做一个受控对比:固定除一个变量外的所有请求条件,只改这一个变量,观察哪一层开始出现版本差异。改查询串、改Cookie、改地域,分别测一次。哪次改动让分叉出现,哪次就是候选原因。注意,请求量或抓取量归零、某个统计指标下降,都不能单独证明缓存处理正确,它们也可能是流量变化、采集口径调整或监控中断造成的。
在数据不全、权限不足时,可执行的最小闭环是:
这个动作的结果会直接改变下一步:如果分叉点前移或消失,说明该层参与了一致性问题,可以继续向上游验证;如果分叉点不变,说明当前层不是主因,应把资源转向更上游或请求条件本身。每次只改一个变量,否则无法归因。
同时要明确不能推出的结论:看到某一层返回旧版本,不等于该层配置错误;看到刷新后一致,不等于问题已修复,可能只是旧对象恰好过期;看到多个层版本一致,不等于所有地域、所有变体都一致。缓存一致性需要按缓存键和变体分别验证,单次取样只能说明这一条请求路径的状态。
定位到候选层后,记录四件事:固定请求的完整条件、每层返回的版本标识、第一个分叉点、你做的受控变更及其结果。这份记录能让后续有权限的人直接复现,而不必重新猜测。若问题涉及具体平台或服务的配置入口,应以该服务当前官方文档为准,不要根据旧经验假设入口位置或功能存续状态。
一致性问题的处理顺序是先统一缓存键,再统一版本标识,最后才谈刷新策略。顺序颠倒时,刷新只会暂时掩盖分叉,下一次请求条件变化后仍会复现。