先给结论:不要从“哪一层缓存坏了”入手,而要先固定一个可复现的请求,再逐层比对同一请求在各层返回的响应头、正文指纹和缓存键。多层缓存返回不同版本,最常见的根因不是某一层随机失效,而是各层的缓存键、TTL 或清除范围不一致,导致同一 URL 在不同层被当成不同对象。
第一种解释是版本漂移。页面在源站已经更新,但某层缓存仍保存旧副本,且该层的 TTL 还没到。第二种解释是清除不同步。内容确实被清过一次,但清除动作只命中部分层,或者各层对“同一页面”的键定义不同,于是边缘节点、反向代理和 WordPress 对象缓存各自保留了不同时间的副本。
这两种解释都会表现为“刷新几次结果不一样”,但代价和处置方向不同。版本漂移通常靠缩短 TTL 或主动清除解决;清除不同步则要先统一缓存键和清除传播路径,否则每次发布都会复发。
固定一个带查询串的 URL,连续请求多次,记录每层返回的 Age、Cache-Control、X-Cache 一类头部,以及正文的哈希值。如果同一层在短时间内返回不同哈希,说明该层内部存在多个副本,问题偏向键或分片不一致。如果每层各自稳定,但层与层之间哈希不同,说明是清除传播没有覆盖全部层。
再看缓存键。假设某页面按“主机名+路径”缓存,而另一层按“主机名+路径+语言 Cookie”缓存,那么带与不带 Cookie 的请求会命中不同对象。此时用 curl -I 对比带 Cookie 和不带 Cookie 的响应,若头部与正文同时分叉,基本可以确认是键定义差异,而不是单纯的过期时间问题。
如果只有最外层与基准不同,且该层在主动清除后恢复一致,下一步应检查清除接口是否覆盖该层,而不是继续调整源站。如果多层都不同,但每层内部稳定,下一步应统一缓存键规则,再重新发布一次观察是否收敛。如果同一层内部就不稳定,下一步应检查该层是否存在多节点未同步,而不是先改 TTL。
做法一:统一缓存键并保持较长 TTL。适用条件是发布频率可控、清除链路可验证。代价是首次配置复杂,且任何键规则变更都需要全量清除一次。
做法二:缩短 TTL,靠自然过期收敛。适用条件是发布频繁、清除链路暂时不可靠。代价是回源请求增加,源站压力上升,且旧版本仍可能在 TTL 窗口内被看到。
选择依据不是哪个更“正确”,而是你能否稳定执行清除。若清除动作可被验证并覆盖全部层,优先做法一;若清除经常遗漏某一层,先用做法二控制影响,再补清除链路。
假设某站点有三层:边缘缓存、反向代理、WordPress 对象缓存。发布新文章后,边缘层返回新正文,反向代理返回旧正文,对象缓存返回新正文。连续请求三次,边缘层和对象缓存层哈希稳定,反向代理层哈希也稳定,但与其他两层不同。这个证据更支持“清除未覆盖反向代理层”,而不是“版本随机漂移”。下一步动作是只对反向代理层执行一次定向清除,再复测三层哈希;若三层收敛,说明问题在清除覆盖范围;若反向代理层仍返回旧正文,则要检查它的缓存键是否包含了未被清除的维度。
定位这类一致性问题的关键,是把“不同版本”拆成可观测的层、键和时间三个维度,再用一次定向动作验证假设,而不是同时修改所有缓存参数。