第 5 章 · 缓存第 3 页,共 4 页
缓存放满了,先清掉什么?
缓存放在内存里,内存又小又贵,放不下所有菜谱。放满以后又来了一道新菜,总得清出去一道。清哪一道,命中的次数就不一样。
为什么需要
缓存快,是因为答案放在里。你平时说的「手机内存 256G」,其实是长期保存照片和 App 的存储空间;这里的内存是电脑干活时临时放东西的地方,很快,却比贵得多(第 2 章)。专门把数据放在内存里的存储服务,叫(in-memory data store),常用的有 Redis,服务器旁的缓存常用它来搭。多占一些昂贵的内存,换来更短的等待,这就是。
所以缓存能放多少有上限,叫,这里的容量指能装多少,不是第 2 章说的每秒能做完多少。缓存不必装下全部菜谱,装得下常被用到的那一部分就够了。可放满以后又来一道新的,就得按规则挑一道清出去,这叫(eviction)。你平时说的淘汰,多指比赛里被刷下去;这里清出去的只是拷贝,数据正本还在数据库里,下次要用再查回来。它和过期、失效也不同:被清出去的可能还没到期,也没人改过,只是地方不够了。
怎样做
常见的规则有两条:
- 最久没用的先清出去:按每道菜上一次被用到的先后排队,命中一次就排到队尾;要腾地方时,清掉排在最前、最久没被用到的那一道。它叫(LRU)。
- 先放进来的先清出去:按放进来的先后排队,命中时不调整;要腾地方时,清掉最早放进来的那一道,不管它后来被用过多少次。它叫(FIFO)。
下图的缓存放得下 4 道菜,读者一道一道点开菜谱。
谁被清出去:缓存只放得下 4 道菜
先看这里先按「下一步」点开第一道菜,看它放进了缓存的哪一格。
缓存里的 4 个格子(排在 1 号的最先被清出去)
- 1 号空
- 2 号空
- 3 号空
- 4 号空
—
点开的顺序
- 1番茄炒蛋
- 2罗宋汤
- 3红烧茄子
- 4番茄炒蛋
- 5清炒菠菜
- 6番茄炒蛋
- 7葱油拌面
- 8番茄炒蛋
- 9蒜蓉粉丝蒸虾
- 10番茄炒蛋
- 11罗宋汤
- 12番茄炒蛋
还没开始。按「下一步」一道一道点开,也可以直接看最后。
示意:缓存一开始是空的,每点开一道都要先查缓存。真实的缓存放得下成千上万份答案,规则也常常简化,例如 Redis 并不把全部数据排成一队,每次只抽查几份,比一比谁最久没用。
在上图里,用两条规则各走一遍「番茄炒蛋常回头」:番茄炒蛋隔一道就被点开一次。按最久没用的先清出去,它总是刚用过,一直留在缓存里;按先放进来的先清出去,第 7 下点开葱油拌面时,番茄炒蛋因为放得最早被清了出去,虽然第 6 下刚用过它,第 8 下再点开,只好再查一遍。再换成「回头再看前几道」:看完四道、回头看一眼番茄炒蛋、点开葱油拌面以后,读者又依次回头看罗宋汤、红烧茄子和番茄炒蛋。这一回结果反过来:最久没用的先清出去,恰好把接下来要看的那两道清了出去,一连两下没命中。
所以两条规则谁好,要看读者怎样访问。Twitter 用真实的访问记录模拟过:缓存不太小时,两条规则的命中率常常差不多(2020)。真实的缓存软件放满以后怎样处理,默认做法也各不相同。
代价是什么
缓存容量越大,命中率通常越高,但内存要花钱;容量小,就有更多请求要回数据库。规则本身也有开销:按最久没用的排,每命中一次都要调整一次顺序,读得越多,这份活越重。
内存还有一个毛病:一断电就清空。缓存所在的机器重启以后,里面往往是空的,几乎每个请求都没命中,要过一段时间才慢慢装回常用的答案,这叫(cold cache)。这期间请求都落到数据库,就像第 1 页那张图把命中率调到 0%,数据库撑不住。
资料来源
以下资料均于 2026-10-23 和 2026-10-26 访问。
- napkin-math
- Caching challenges and strategies
- Key eviction
- Operating Systems: Three Easy Pieces, chapter 22: Beyond Physical Memory: Policies
- A large scale analysis of hundreds of in-memory cache clusters at Twitter
- redis.conf
- Engine specific parameters
- Memory management for Azure Managed Redis
- FIFO Queues are All You Need for Cache Eviction
- Site Reliability Engineering, chapter 22: Addressing Cascading Failures
- Free up storage on your Android device