3.5 缓存机制:契约的加速条款


3.5 缓存机制:契约的加速条款

本节摘要:缓存让读得多的接口跑得更快、扛得更久,是 REST 可缓存约束在工程层的兑现。本节讲清 Cache-Control 标记与 ETag/If-None-Match 条件请求的原理,并用一次"资讯接口加了缓存"的绩效演练,看命中率和新鲜度的平衡怎么拿,以及缓存失效如何防止脏数据。

学习目标

阅读完本节,你应当能够:

  1. Cache-Control 标记响应可缓存性并设置有效期。
  2. 理解 ETag 与 If-None-Match 条件请求如何用 304 节省流量。
  3. 在缓存命中率与数据新鲜度之间做平衡(max-age 的取舍)。
  4. 设计缓存失效策略,避免返回脏数据。

一、可缓存契约为什么值得精心设计

第 1.2 节的可缓存约束只是"要求响应明确标记可否缓存"。而到工程层,缓存做对能显著降压,做错会返回脏数据——所以要有专门的一套"加速条款"来管它。

先布一块共同直觉:缓存的本质是"把读的计算结果存起来,命中时不再重算"。它对读多写少(资讯、目录、静态配置)的接口收益最大;对强实时、强一致的写敏接口,则要小心。

二、Cache-Control:给响应贴有效期标签

服务器决定"这份响应能不能被缓存、能存多久",用 Cache-Control 头最直接:

HTTP/1.1 200 OK Cache-Control: public, max-age=3600 Content-Type: application/json {"title":"今日快讯","body":"..."}

max-age=3600 告诉客户端与中介:"这一小时内这份响应可以直接复用,别再来问服务器。"public 表示连共享缓存(CDN、代理)也可存。不同策略对照:

含义 适用
no-cache 可用但用前必须校验 时效敏感的响应
no-store 完全不可存 含敏感信息、支付响应的响应
public, max-age=N 可被共享缓存存 N 秒 公开读多的资源
private, max-age=N 仅客户端可存 含用户个性化数据的响应

选择是"命中率 vs 新鲜度"的权衡:max-age 越长命中越高、服务器越省,但数据新鲜度越差;短了反之。用哪个,取决于业务容忍多久的"旧"。

三、ETag 条件请求:只有变化才重传

前面是"存 N 秒不管中间变没变",如果想"变了才重发、没变就 304",就用 ETag 条件请求:

流程:服务器给每份资源算一个 ETag(资源版本的指纹,通常基于内容哈希)。客户端带 If-None-Match 问"我这个版本还在吗",服务器比对后,若没变回 304(不重复发身体)、变了回 200 带新体与新 ETag。这样既保证新鲜又省了大量重复传输。

GET /article/1 HTTP/1.1 If-None-Match: "eTag-9a12b" HTTP/1.1 304 Not Modified

这个 304 没有响应体,客户端本地还用旧版即可——"每次请求都问,但只有真变了才下重的"。

把"谁能旧、谁必须新"的分流用一张决策图定下来:

03-05-fig01

图:缓存策略决策图

四、一次绩效演练:资讯接口加缓存

把前面合起来做一次可度量的演练。

背景:一个热点资讯接口,高峰 QPS 冲到 5000,数据库被读打爆,平均延迟 240 毫秒。

操作:为公开的文章接口加 Cache-Control: public, max-age=60,并在网关配了 CDN 缓存,同时给文章资源带上 ETag。

结果:CDN 与客户端命中大部分热点请求,落库的只有真正变化或首次的请求,数据库 QPS 从 5000 降到约 600,平均延迟从 240 毫秒降到 40 毫秒以内。

解读:max-age=60 意味着文章最多延迟 60 秒可见新增内容,对热点资讯可接受;ETag 保证了"编辑器改完一条,改后的一秒就能拿到新版"。命中率与新鲜度在这里达到了平衡。

变式:若这是"用户余额"接口,max-age=60 会让人看到 60 秒前的余额——不可接受,必须 no-store 或短缓存 + ETag 校验,把命中率让给新鲜度。

⚠️ 常见坑:给写操作的响应也套了缓存,导致"改了没生效"。缓存只作用于 GET 这类读路径;POST/PUT/PATCH/DELETE 的响应与副作用路径要么 no-store,要么用失效机制显式清除相关资源缓存,否则就出脏数据。

五、失效策略:别让脏数据住进去

缓存帮了大忙,也埋了"加速过期"的雷:存量缓存如果不失效,服务器已更新的资源在 max-age 内仍被旧值撑着。失效策略通常三选一或并用:

  • 时间到期:max-age 自然过期,最简单,但"改完立即可见"保证不了。
  • 显式失效:写操作时主动清除/刷新该资源的缓存键(配合 ETag),改完即稳定更新。
  • 版本化键:URL 或资源版本上带时间戳/ETag,变更即换新键。

💡 关键直觉:缓存是把"读"的可伸缩性压到网络层。赢的是命中率,赌的是什么能容忍旧。先分清"谁能旧、谁必须新",再定 max-age 和是否上 ETag,就不会两头踩空。

本节要点回顾

  • 要点一:Cache-Control 标记可缓存性与有效期,max-age 是命中率与新鲜度的旋钮。
  • 要点二:ETag 配 If-None-Match 用 304 省流量,只有真变化才下重。
  • 要点三:读多写少的公共资源吃缓存红利,灵敏数据(余额)要 no-store。
  • 要点四:缓存只作用于读路径,写操作响应不能乱套缓存。
  • 要点五:失效策略用时间到期、显式失效、版本化键三选一或其组合。
  • 要点六:先分清"谁能旧、谁必须新",再定缓存参数,避免脏数据。

读性能解决,轮到"一次干多件事还保一致"的硬骨头——批量操作与事务处理。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U