Java Notes

Home首页 / Question 3: cache第 3 题:缓存

Question 3 · must know · cache第 3 题 · 必背 · 缓存

One Redis in front, P95 from 800 ms down to 240 ms.前面加一层 Redis,P95 从 800 毫秒降到 240 毫秒。

A cache does only one thing: stop the same read from reaching the database twice. Everything else is about the three ways it can leak.缓存只干一件事:让重复的读不再打到数据库。剩下的全是“它有哪三种漏法”。

Short answer先给答案Hit rate decides the win: 92% hits means the database sees 8% of the traffic. Penetration, breakdown and avalanche are three different leaks with three different fixes. Consistency is Cache Aside — delete after writing, delete again with a delay — and if you need strong consistency, do not cache that path at all.收益看命中率:命中 92%,数据库只剩 8% 的流量。穿透、击穿、雪崩是三种不同的漏法,各有各的治。一致性走 Cache Aside:写完删缓存,再延迟删一次;要强一致的那条链路,干脆不缓存。

Must know必背cache缓存speak 60s60 秒口播4 follow-ups追问 4 个

01Why the query was slow in the first place先说为什么会慢

A list page does not run one query. It runs one query per row, then a count, then a dictionary lookup — the same data again and again, on every page view.一个列表页不是一条 SQL。它是一行一条查询,再加一个 count,再加几次字典查询——同一批数据,每次刷新都重算一遍。

Say both halves. If you only say “I added Redis”, the interviewer will assume you cannot tell the cache apart from the query.两半都要说。只说“我加了 Redis”,面试官会认为你分不清缓存和查询。

02Three ways a cache leaks三种漏法,一张表

They sound similar and are asked together, so keep them apart by what is missing.这三个名字听着像,面试也爱连着问。按“缺的是什么”来分就不会混。

Leak漏的是什么What happens发生什么Fix怎么治
Penetration穿透A key that exists nowhere: cache misses, the database misses too, and every request repeats it.查一个根本不存在的数据:缓存没有,数据库也没有,每次请求都空跑一遍。Cache the empty result (`null` with a short TTL) + a bloom filter in front.空结果也缓存(存 null,短 TTL)+ 前面加布隆过滤器。
Breakdown击穿One hot key expires, and a thousand threads rebuild it at once.某个热点 key 刚好过期,一瞬间上千个线程同时回源重建。Let one thread rebuild (Redisson `tryLock`), or keep the key logically expired and refresh in the background.只让一个线程重建(Redisson tryLock),或者逻辑过期+后台异步刷新。
Avalanche雪崩Many keys expire at the same second, or Redis itself goes down.大批 key 在同一秒过期,或者 Redis 整个挂了。Randomise the TTL; add a local (Caffeine) layer; add a circuit breaker so the database is not flooded either.过期时间加随机;加一层本地缓存(Caffeine);加限流熔断,别让数据库跟着被冲垮。

The one-word answer that lands: penetration is fake data, breakdown is one hot key, avalanche is many keys at once.一句话记住:穿透是假数据,击穿是一个热 key,雪崩是一大批 key。

03Keeping it consistent和数据库怎么保持一致

The pattern is Cache Aside: read the cache first; on a miss, read the database and write the value into the cache; on a write, delete the cache — do not update it.模式叫 Cache Aside:读先看缓存,没有就查库并回填;写的时候删缓存,不是更新缓存。

-- read
v = redis.get(k)
if v == nil:
    v = db.select(k)
    redis.set(k, v, ttl=300 + random(0, 60))   -- ★ 加随机,防雪崩
    if v == nil: redis.set(k, '\0', ttl=60)    -- ★ 空值也缓存,防穿透

-- write
db.update(...)
redis.del(k)
delay(500ms); redis.del(k)                       -- ★ 再删一次

If you claim “double delete solves it completely”, you will be caught: there is still a window. Say “it narrows the window to milliseconds, and money paths do not cache”.别说“双删就彻底解决了”,还有窗口。说“它把窗口压到毫秒级,而且钱的链路不缓存”。

04So where did 800 → 240 come from那 800 到 240 是怎么来的

Do the arithmetic out loud; interviewers like numbers that can be checked.把算术说出来,面试官喜欢能对得上的数。

Change改了什么Effect效果
Hot list into Redis, 92% hit rate, TTL 5 min + jitter热点列表进 Redis,命中率 92%,TTL 5 分钟 + 随机≈ 92% of reads never touch MySQL; the database goes from 1000 QPS to ~80.约 92% 的读不再碰 MySQL,数据库从 1000 QPS 降到 80 左右。
N+1 queries collapsed into one join + covering indexN+1 查询合成一次 join + 覆盖索引The 8% that still reach MySQL each got 3–5× cheaper.仍然打到 MySQL 的那 8%,单次便宜了 3–5 倍。
Connection pool raised, slow SQL removed连接池调大,慢 SQL 清掉Tail latency drops most — P95 is exactly about the tail.尾延迟降得最明显——P95 说的就是尾部。

If you only ever quote “70% faster” without a baseline, it sounds invented. 800 → 240 with the mechanism behind it sounds measured.只报“快了 70%”像编的;说出 800 → 240 加上机制,才像量过。

05Four follow-ups, one line each四个追问,一句话一个

Follow-up追问One-line answer一句话答案
What if Redis is down?Redis 挂了怎么办?Local cache + circuit breaker; degrade read-only pages, do not let it hit the database.本地缓存 + 熔断限流;只读页降级,别全冲到数据库。
Hot key on one shard?热 key 打到一个分片上怎么办?Local cache first, then split the key with a random suffix, or use a read replica.先本地缓存,再把 key 加随机后缀打散,或者上只读副本。
Warm up the cache after a deploy?发布后要预热吗?Yes: top-N keys loaded at startup, otherwise the first minute is a stampede.要:启动时把 Top N 的热 key 预加载,否则第一分钟就是雪崩。
Cache the page or the object?缓存页面还是缓存对象?Objects. A page cache is invalidated by any change and gets stale fast.对象。整页缓存一改就失效,很快就是脏数据。

0660-second script60 秒照念稿

Read this out loud照这段说,60 秒

We put the hot list into Redis with a 5-minute TTL plus random jitter, and the hit rate settled around 92%, so MySQL only sees about 8% of the reads. On top of that we collapsed the N+1 queries into one join with a covering index, so the remaining 8% got cheaper too. That took the list API's P95 from ~800 ms to ~240 ms. The three failure modes have three fixes: penetration is fake data, so we cache the empty result and put a bloom filter in front; breakdown is one hot key, so only one thread rebuilds it under a Redisson lock; avalanche is many keys or a dead Redis, so TTLs are randomised, there is a local Caffeine layer, and the read path is circuit-broken. For consistency we use Cache Aside: delete after writing, delete again after 500 ms. Anything about money or stock does not go through the cache at all.我们把热点列表放进 Redis,TTL 5 分钟加随机,命中率稳定在 92% 左右,MySQL 只看到 8% 的读。同时把 N+1 查询合成一次 join 加覆盖索引,剩下这 8% 单次也便宜了。列表接口 P95 从 800 毫秒降到 240 毫秒左右。 三种异常三种治法:穿透是假数据,空结果也缓存、前面加布隆过滤器;击穿是一个热 key,只让一个线程在 Redisson 锁下重建;雪崩是大批 key 或 Redis 挂了,所以 TTL 加随机、本地放一层 Caffeine、读链路加熔断。 一致性走 Cache Aside:写完删缓存,500 毫秒后再删一次。钱和库存那条链路不走缓存。

07Where this sits on my résumé怎么挂到我自己的项目上

Only what is actually in the résumé. This question hangs on two projects:只挑简历里真有的。这道题挂在两个项目上:

08Three things not to say三句别说

→New words新词

cache /kæʃ/ A fast copy kept in front of the slow store.缓存:放在慢库前面的快副本 ▶ hear it
hit rate /hɪt reɪt/ How often the copy is already there.命中率:多大概率直接命中 ▶ hear it
penetration /ˌpenəˈtreɪʃn/ Asking for data that does not exist, over and over.穿透:反复查不存在的数据 ▶ hear it
breakdown /ˈbreɪkdaʊn/ One hot key expires and everyone rebuilds it at once.击穿:一个热 key 过期被同时重建 ▶ hear it
avalanche /ˈævəlæntʃ/ Many keys expire together, or the cache dies.雪崩:大批 key 同时失效或缓存挂掉 ▶ hear it
stale /steɪl/ Old data that is still being served.脏数据/过期副本 ▶ hear it
jitter /ˈdʒɪtər/ A small random offset added to a TTL.抖动:给过期时间加一点随机 ▶ hear it

Click ▶ hear it for real people on YouGlish; 🔊 for the computer voice.点 ▶ hear it 听真人发音(YouGlish);点 🔊 听电脑朗读。

✓Check yourself自测

  1. 穿透、击穿、雪崩,各自缺的是什么?穿透、击穿、雪崩,各自缺的是什么?
  2. 为什么写的时候删缓存,而不是更新缓存?为什么写的时候删缓存,而不是更新缓存?
  3. 说出 800 → 240 的两半原因。说出 800 → 240 的两半原因。

Answer out loud first. Only then open the answers below.先自己出声答,再看下面的答案。

Answers参考答案

09New questions this one opened这道题又长出了哪些新问题

A good answer opens the next question. That is the whole method.一个答案会生出下一个问题——方法就是这个。

Question新问题One line一句话
缓存和数据库怎么保持一致?Cache Aside:写完删缓存,500 毫秒后再删一次。待写
穿透和击穿到底差在哪?穿透查的是不存在的数据;击穿是一个热 key 过期被同时重建。待写
Redis 挂了怎么办?本地缓存 + 熔断限流,只读页降级。待写
热 key 打到一个分片怎么办?本地缓存,再把 key 加随机后缀打散。待写
发布后要不要预热?要,启动加载 Top N,否则第一分钟就是雪崩。待写
缓的是页面还是对象?对象;整页缓存一改就失效。待写

← Question 2: payment callback← 第 2 题:支付回调