
在网站建设与开发过程中,缓存是一把双刃剑。用得好,页面打开速度可以提升数倍,服务器压力大幅下降;用得不好,用户看到的永远是过时的内容,更新迟迟不生效,甚至出现数据错乱。如何在“快”与“新”之间找到平衡,是每个开发者都需要掌握的核心技能。本文从缓存的分层结构、策略设计、失效机制和实战配置思路四个维度展开,帮助你把缓存设置得既快又准。
网站缓存不是单一开关,而是从浏览器到服务器之间的一条链路。常见层级包括:
浏览器缓存:资源保存在用户本地,重复访问时无需请求服务器。适合静态资源,如样式表、脚本、图片、字体。
CDN 缓存:边缘节点缓存内容,用户就近获取。适合全球或跨地域访问的静态资源和部分动态内容。
反向代理缓存:位于源站前方,缓存完整页面或接口响应,减轻后端压力。
应用层缓存:在代码中缓存数据库查询结果、计算产物、片段页面等,常用内存缓存或分布式缓存。
数据库缓存:数据库自身的查询缓存、缓冲池等,属于底层优化。
理解分层后,核心思路就清晰了:越靠近用户的缓存,越适合放“不常变”的内容;越靠近源站的缓存,越适合放“变化快但可短时复用”的内容。
不同类型的资源,更新频率和实时性要求完全不同。建议按以下分类处理:
长期不变的静态资源:带哈希指纹的文件名,如 app.a1b2c3.js。这类资源内容变更时文件名也会变,因此可以设置极长的强缓存,例如一年,并标记为不可变。浏览器和 CDN 都可以放心缓存。
可能更新的静态资源:如 favicon、某些固定路径的图片。适合设置中等时长的强缓存,例如一天到一周,同时配合协商缓存,到期后由服务器判断是否更新。
HTML 页面:这是实时性要求最高的部分。建议设置为不缓存或极短缓存,例如几十秒,并强制每次协商验证。这样用户总能拿到最新的页面结构,而页面内引用的静态资源仍然走长缓存。
接口数据:根据业务敏感度分级。商品列表、文章列表可缓存几十秒到几分钟;用户个人信息、订单状态、库存数量则不应缓存或只缓存极短时间。
动态片段:可以采用片段缓存,将页面中变化频率不同的区域分开处理。例如页头、页脚缓存较久,正文区域缓存较短。
关键原则是:强缓存负责快,协商缓存负责新,指纹文件名负责安全更新。
强缓存通过响应头中的过期时间和最大生存期控制。在有效期内,浏览器直接使用本地副本,不发出任何请求,速度最快。但一旦内容更新,用户可能在过期前一直看到旧版本。因此强缓存只适合文件名会随内容变化、或确实允许延迟更新的资源。
协商缓存通过最后修改时间和实体标签控制。浏览器每次都会向服务器验证,若内容未变,服务器返回无内容响应,浏览器继续使用本地副本;若内容已变,则返回新内容。协商缓存保证实时性,但每次都有一次网络往返。
最佳实践是组合使用:静态资源走长强缓存加指纹文件名;HTML 和接口走协商缓存或极短强缓存。 这样既能让大部分资源零请求加载,又能保证页面入口和数据及时更新。
缓存设置得再好,也必须有一套可靠的失效机制。常见手段包括:
版本化路径:内容更新时改变资源路径或文件名,旧缓存自然失效,新请求命中新资源。
主动清除:当后台发布内容时,调用缓存服务的清除接口,删除相关页面或接口的缓存副本。适合反向代理和 CDN 层。
标签化清除:给缓存打上业务标签,例如“文章列表”“商品详情”,更新某一类数据时按标签批量清除,避免全量刷新。
短过期加后台刷新:对实时性要求中等的接口,设置较短过期时间,同时在后台异步刷新缓存,用户请求时先返回旧值再更新,兼顾速度和最终一致。
版本号或时间戳参数:在请求地址中附带版本号,版本变化即视为新请求。这种方式简单有效,但要注意不要滥用,以免缓存命中率过低。
需要特别注意的是,清除缓存和刷新缓存是两件事。清除是删除旧副本,下次请求回源重建;刷新是主动拉取新内容覆盖旧副本。高并发场景下,如果大量请求同时回源,可能造成源站压力骤增,因此要配合锁机制或队列,避免缓存击穿。
以下给出一个通用的配置框架,不涉及具体产品名称,只讲思路。
对于静态资源:
文件名包含内容哈希。
响应头设置长强缓存,并标记为不可变。
同时设置协商缓存字段,作为兜底。
CDN 层同样遵循长缓存策略,回源时校验指纹。
对于 HTML 页面:
设置不缓存或极短强缓存。
必须携带协商缓存字段。
CDN 层可设置较短边缘缓存,例如几十秒,并允许 stale-while-revalidate 行为:在后台验证新版本的同时,先返回旧版本,避免用户等待。
对于接口:
公开只读接口:可设置短强缓存,例如三十秒到五分钟,并附加协商缓存。
用户相关接口:设置为私有缓存或不缓存,避免中间层缓存泄露数据。
写操作后:主动清除相关读接口的缓存。
对于应用层缓存:
热点数据设置合理过期时间,并加随机抖动,避免同时失效。
使用互斥锁防止缓存击穿。
对空结果也进行短时缓存,防止缓存穿透。
对大量 key 设置不同过期时间,防止缓存雪崩。
缓存策略不是一次设定就一劳永逸。需要持续监控命中率、回源率、过期分布、用户看到旧内容的投诉率等指标。如果命中率过低,说明缓存时间太短或粒度太细;如果更新延迟投诉多,说明强缓存过长或清除不及时。通过灰度发布、按比例放量、对比不同策略的效果,逐步找到适合自身业务的平衡点。
此外,还要注意缓存与登录态、个性化推荐、A/B 测试之间的冲突。对于个性化内容,尽量在边缘层做拆分,公共部分缓存,个性部分回源或由客户端组装。对于 A/B 测试,可以通过不同的路径或参数区分缓存副本,避免实验组和对照组互相污染。
缓存的核心矛盾是“快”与“新”。解决思路不是二选一,而是分层、分类、分策略:
静态资源长缓存,靠指纹文件名保证更新;
HTML 和接口短缓存或不缓存,靠协商机制保证实时;
应用层缓存热点数据,靠过期和清除机制保证一致;
全链路配合主动失效和监控调优,保证最终效果。
把这套思路落地后,网站既能获得接近静态页面的打开速度,又能在内容发布后让用户及时看到最新版本。缓存不是简单的响应头设置,而是一套贯穿浏览器、边缘节点、反向代理、应用和数据库的系统工程。理解每一层的职责,才能让速度与实时更新兼得。