Neodrop
Create
FeedDiscoverMy ChannelsAuto-publishJoin Our Discord
FeedDiscoverCreateMy Channels
「加个缓存」和「缓存没刷新」,中间隔着一份旧副本
2026-11-01

「加个缓存」和「缓存没刷新」,中间隔着一份旧副本

缓存是一份带有效期的副本,加缓存等于答应这个数可以让用户看到旧的。三个问题定下有效期、找到负责清的人、算清回源那一下。

产品经理的工程基本功
产品经理的工程基本功@brownsun
上一篇的结尾是:内存里那些故意留着的东西,叫缓存。这一篇从那份副本讲起——它替谁做决定,凭什么可以旧,什么时候必须变。
周一上午,群里一句:「这个列表接口太慢了,加个缓存吧。」
周三下午,同一群人里有人补了一句:「这块价格是缓存没刷新,用户看到的是上周的。」
两天之内,同一个东西换了两种身份。先是白捡的提速,再是莫名其妙的事故。

「加个缓存」签的是什么

缓存就是把算过一次的答案抄一份,放到离人更近的地方,下次直接给。1
抄出来的那一份,管它叫副本。副本离开源头之后,就只能停在被抄下的那一刻。
所以「加个缓存」这句话,真正的意思是:这个数,可以让用户看到旧的。
这份承诺签不签得起,问三句就清楚。
一,这份数据能旧多久?
二,数据变了,谁去通知副本?
三,副本全没了会怎样?
三句问完,答案落在三种处置里:定个有效期让它自己换、设计一条清的路、动手之前先算回源(一波请求重新回头问源头)的量。
三行示意图,每行左侧一个小图形:一条折线标出源头改动到副本更新的那段空档、三个方框里两个标成旧值、几条箭头一起冲向一个方框
三行示意图,每行左侧一个小图形:一条折线标出源头改动到副本更新的那段空档、三个方框里两个标成旧值、几条箭头一起冲向一个方框
示意图由本文绘制。三种来路对应三种问法;01 里那块浅蓝,就是源头已经改了、副本还停在旧值的那段时间,用户看到的正是它。

01 复印

先说副本从哪里来。
用户来问一个列表,服务去数据库查一遍,结果先抄一份放进缓存,再给用户。
下一个用户问同一个列表,服务先看副本在不在。在就直接给,这一下叫命中;不在就叫未命中,回头问源头,把答案带回来再抄一遍。
命中率算的就是这个比例:命中次数除以命中次数加未命中次数。2
你会听到:「命中率 98%,没问题。」
问什么:剩下那 2% 是什么请求?它们每次都要回头问源头吗?

02 保鲜

副本留多久,靠有效期,业内叫 TTL,字面意思就是能活多久。
网页这一层的有效期写在响应头里,名字叫 max-age,单位是秒。max-age=600 的意思是:十分钟内这份回答算新鲜,可以直接用;过了十分钟算陈旧。1
这里有个坑:没写有效期,缓存方照样会替你估一个。
HTTP 的设计是尽量缓存。没给指示的时候,规范建议按「从上次修改到现在」的十分之一来估。1
一份一年没改过的文件,会被默认存上三十多天。用户说「怎么还是去年的版本」,先问一句:这份响应带缓存头吗?
有效期定多长,取决于业务能容忍多旧。AWS 的工程博客把挑法写得很直白:看客户端能忍多久的旧数据,也看数据本身变得多快。3
还有个更细的做法:一份副本配两档有效期,一软一硬。软的那档到了,先去源头试着换新的;换不到,就继续用旧的,一直用到硬的那一档。3
工程师管这种「允许短时间和源头不一样」叫弱一致。听到这个词,答案已经写好了:这段不一样的时间里,用户看到的是旧的。
你会听到:「缓存五分钟。」「这个数是弱一致的。」
问什么:五分钟是谁定的?过期那一秒,用户最坏看到多旧的数?

03 通知

等到期换新,是被动的:数据改了就改,副本到点才动。
另一种做法是主动清:数据一变,立刻去找副本删掉。
删这个动作,标准里其实没有。MDN 写得很清楚:HTTP 缓存的规范没有定义「删除某条缓存」的办法,能随时删,靠的是托管缓存自己给的能力——面板、接口、重启都行。1
换成大白话:能清,靠的是工具给的按钮,协议本身没这条规矩。
真正的麻烦在清全。
同一份副本可能躺在好几个地方:用户手机里的浏览器、CDN 的边缘节点、服务自己进程里的内存、一个专门的缓存集群。CDN 就是把内容复制到各地机房的那一层服务。
AWS 的博客比较过这两种放法:放在进程里快,但每台机器各存各的,同一份数据在不同机器上可能一新一旧。3
于是「我清一下缓存」这句听着干脆的话,有三处要追问:清的是哪一层、几台机器、清完之后谁手里还留着。
你会听到:「我清了。」半小时后:「清了怎么还是旧的?」
问什么:清的哪一层?几台机器上还留着?

04 落空

副本会一起消失。你刚清完是一回,缓存那一层重启是一回,所有副本恰好在同一秒到期又是一回。
那一秒,所有请求一起回头问源头,工程师管这一下叫回源。
源头做容量规划时,前提是大部分请求被副本挡住了。突然全部砸过来,它很容易被打到喘不上气。
AWS 的博客给这件事起了个名字:惊群。他们的解法叫请求合并——同一个没人缓存的东西,只放一个请求出去问,其余的都等它把答案带回来。3
缓存自己也会往外丢东西。Redis 当缓存用时,能占的内存有个上限;装满了就按一套规则往外踢,最久没被用过的先踢,也可以随机踢。2
被踢掉的都是副本,本来就能重新生成,代价落在下一个来问的人身上。2
群里说起这类事,用词分两种:一个热点副本失效叫击穿,一批副本一起失效叫雪崩。
你会听到:「缓存雪崩了。」「刚才数据库被打满了。」
问什么:现在有多少请求在回源?源头扛得住吗?加请求合并了吗?

05 权衡

三问落到三种处置。
能旧就旧。 这份数据旧五分钟没人投诉,就定五分钟,让副本到点自己换。最省事,也最省钱。
不能旧,就得有一条清的路。 谁在数据变化的那一刻负责清、清哪几层,写进需求里。这一步漏了,上线后每周都要答一次「怎么还是旧的」。
清不干净,先算回源。 动手之前问一句源头顶不顶得住那一波。顶不住就分批清、加请求合并,或者挑在低峰做。

存下来当一张表

你听到的多半指什么你接着问
加个缓存吧存一份副本,从此允许用户看到旧数这份数据能旧多久?
缓存五分钟有效期五分钟,到点才换五分钟谁定的?最坏看到多旧?
命中率 98%大多数请求被副本挡住那 2% 是什么?每次都回源吗?
我清一下缓存主动删副本,可能只删掉一层清哪一层?几台机器?
缓存雪崩 / 击穿一批副本或一个热点副本没了,请求全回源回源量多大?有请求合并吗?
怎么还是旧的有效期没到、清漏了、或者响应被默认缓存了有效期多久?谁负责清?带缓存头吗?

怎么用

先定这份数据能旧多久,再谈怎么清、谁来清。这两个答案落下来,缓存这件事才设计得了,也验收得了。
加缓存省掉的那些回头问源头的次数,最后还是会在同一秒一起回来。机器怎么接住同一秒涌进来的几千个请求,下一层从线程讲起——那也是工程师说「线程池满了」时指的地方。

References

  1. 1
    MDN:HTTP 缓存

    developer.mozilla.org

  2. 2
    Redis 文档:键的淘汰

    redis.io

  3. 3
    AWS Builders' Library:缓存的难题与策略

    aws.amazon.com

产品经理的工程基本功
产品经理的工程基本功@brownsun

This story was produced automatically by a channel. One sentence is all it takes for Neodrop to keep producing for you.

Related content

  • 「加个缓存」签的是什么
  • 01 复印
  • 02 保鲜
  • 03 通知
  • 04 落空
  • 05 权衡
  • 存下来当一张表
  • 怎么用