关于91网,我把缓存管理讲清楚后,很多问题都通了(真相有点反常识)
关于91网,我把缓存管理讲清楚后,很多问题都通了(真相有点反常识)

在维护91网这么长时间,碰到最多、最头疼的问题往往不是代码 bug,也不是数据库慢查询,而是“缓存”。一旦缓存层次不清、失效策略混乱、和缓存相关的异常就像幽灵一样,影响用户体验、埋雷运维成本、让开发和产品互相怀疑。把缓存管理理顺后,很多看似复杂、难以复现的问题都迎刃而解。下面把我在91网的实战经验和一些反常识的结论讲清楚,帮你少踩坑、快速定位并稳健改进缓存策略。
先说结论(很关键,省时间)
- 区分“谁读取”“谁修改”以及“被缓存的是什么”,是解决缓存问题的第一步。
- 高命中率的缓存比频繁清空缓存更有用。频繁 purge 极容易制造“抖动”问题。
- 对匿名流量做激进缓存,对登录用户做精细化策略;不要混用二者的缓存键。
- 缓存失效多数不是 CDN 的错,而是 HTTP 头、cookie 或 cache-key 配置不对。
常见症状与实际成因(举例)
- 用户看到陈旧的文章/头像:通常是 CDN 或浏览器缓存没有被正确清除,或者页面缓存键没有包含变更标识(例如 avatar version)。
- 登录后仍然看到未更新的导航或权限:页面在匿名缓存层被缓存并返还给登录用户,说明缓存没有区分登录态。
- 部分用户出现 500/502/503:可能是缓存穿透或 cache stampede,后端被短时间内击穿。
- 数据看起来不同步(页面显示旧数据但 API 返回新数据):浏览器和 CDN 缓存策略不一致,或者前端静态资源被强缓存导致接口与页面模板不匹配。
快速诊断清单(实战工具与步骤) 1) 在浏览器打开开发者工具,查看 Network:
- 关注 response headers 中的 Cache-Control、Age、Expires、ETag、Last-Modified。 2) 用 curl 或 curl -I 检查不同节点的响应头(直接打到负载均衡、打到源站、打到 CDN 节点),确认每层的缓存策略是否一致。 3) 参考 CDN/代理的缓存日志(命中/未命中、purge 请求),找到是否存在大量 purge 或错误的 cache key。 4) 排查 cookie、Authorization、Vary headers:这些会影响缓存分片,非常容易导致缓存不命中或缓存错发。 5) 若怀疑是 cache stampede,用简单脚本并发访问页面,看后端负载是否飙升。
设计缓存策略的实务推荐(91网的可复制做法)
- 匿名页面:使用 CDN + 边缘缓存,Cache-Control: public, max-age=86400(或更长),并配合 stale-while-revalidate 或 stale-if-error。这能保证高并发下用户得到快速响应,还能在后端短暂不可用时优雅退化。
- 动态内容(登录用户、权限相关):不要走公共 CDN 缓存,或使用带有变体的 cache key(例如:X-User-Id 或 Set-Cookie 的 hash)来区分。更稳妥的是在边缘只缓存 fragment(可缓存的静态块),整体页面由应用服务器拼装。
- API 接口:对 GET 接口可设置合理 TTL;对 POST/PUT/DELETE 返回的资源,返回带有 ETag 或 Last-Modified,并在内容更新时主动发起缓存 purge 或向 CDN 发出 API 清理指令。
- 静态资源(JS/CSS/图片):强缓存 + 指纹(文件名带 hash)是王道。避免用时间戳做强制刷新的“巧克力盒”,指纹一旦改变就必然失效,不需要 purge。
- 错误响应策略:对 500/502 设置短 TTL,或不缓存;对 404 可设置较短负缓存(negative cache)以防垃圾路径长期占用缓存空间。
应对缓存穿透与雪崩的技术手段
- 缓存预热(warming):在大规模上线/发布后,先后台触发一批请求把关键页面/资源拉满缓存。
- 缓存锁或互斥(cache lock):当缓存失效时,只有第一个请求去刷新后端,其他请求短时间内使用旧值或等待,避免并发击穿后端。
- 分级降级:当后端压力过高时,引导到只返回基础缓存内容或静态页面,不做实时计算。
- 使用冷却期与限速:对频繁 purge 的路径设置最小 purge 间隔,避免人为频繁清空带来的抖动。
反常识(但实践有效)的几点
- “更短的 TTL 更安全”并不总成立:短 TTL 会造成频繁的后端请求,导致抖动和更高的延迟。合理的长期缓存 + 主动 invalidate(变更时 purge)通常更稳定。
- “缓存越多越好”也不对:多层缓存若缺乏一致的失效机制,只会导致状态不同步、难以排查。要有中心化的失效策略(部署脚本或 webhook)。
- “用户看不到更新是 CDN 问题”常常只是前端没增版本号或后端没发 purge。先从 response header 和 cache-key 排查,比直接怀疑 CDN 更省时间。
- “登录用户不缓存”并不意味着放弃一切缓存:可以缓存页面中可复用的 fragment(如热门推荐列表),通过组合方式提高命中率。
实用命令与样例 header(排查时派得上用场)
- curl -I https://example.com/path 查看响应头(Cache-Control、Age、ETag)。
- curl -H "Accept-Encoding: gzip" -I https://example.com/path 比对边缘是否返回压缩内容与缓存策略。
- 推荐页面头:
- 匿名可缓存页:Cache-Control: public, max-age=86400, stale-while-revalidate=300
- 登录态页面:Cache-Control: private, max-age=0, no-cache(同时将可缓存块单独静态化)
落地流程(一步步来) 1) 制定缓存分层图:浏览器 → CDN → 边缘服务 → 负载均衡 → 应用缓存(Redis/Varnish)→ 源站。标注每层的缓存职责与 TTL。 2) 标出哪些 URL/资源必须实时(例如支付回调、订单详情),哪些可以强缓存。 3) 为关键变更(如头像更新、文章编辑)实现发布钩子:更新后自动触发 CDN purge 或发送变更通知到缓存层。 4) 建立监控:命中率、purge 频率、后端流量、cache-miss 带来的延迟。 5) 培训团队:开发、产品、运维都需要理解缓存语义,避免“随手一 purge”导致连锁反应。
结语 缓存管理不是写几条 header 就完事儿的工程,而是产品、开发、运维共同设计的一套协议。把缓存的职责和边界讲清楚后,很多“怪异”的用户反馈、性能波动和运维事故都会自然消失。91网的经验是:稳健的缓存策略比临时清空更能带来稳定与性能;少用盲目的短 TTL,多用分级缓存与主动失效。按上面的诊断思路和落地清单去做,90% 的缓存相关问题都能在短时间内被定位并解决。

