缓存(Cache) 本质上是:把访问频率高、计算或查询代价大的数据,临时存放在离请求更近、访问更快的地方,用"空间换时间",减少对慢速数据源(如数据库、磁盘、远程服务)的重复访问。
可以把它理解成"数据的临时高速副本"。比如查商品详情:
请求 → 先查缓存 → 命中就直接返回;未命中再查数据库 → 把结果写回缓存 → 下次请求命中缓存
1. 缓存解决什么问题
主要解决三类问题:
-
提升访问速度:内存/本地缓存比查数据库、磁盘、远程接口快得多。
-
降低后端压力:大量重复请求被缓存挡住,不都打到 DB。
-
提升系统吞吐和可用性:DB 慢或短暂抖动时,缓存可以兜一部分读流量。
代价是:数据可能不是最新、需要额外内存、要处理失效和一致性。
2. 缓存的核心指标
-
命中率 Hit Rate:请求在缓存中找到数据的比例。命中率越高,缓存价值越大。
-
未命中 Miss:缓存没有,需要回源查 DB 或重新计算。
-
TTL 存活时间:key 多久后过期,比如 30 分钟。
-
淘汰策略:内存满了删谁,如 LRU(最近最少使用)、LFU(最不经常使用)、FIFO、随机淘汰等。
-
容量和成本:缓存不是无限大,要控制内存。
-
一致性:缓存里的值和 DB 是否一致,能容忍多旧。
3. 缓存在系统里的位置
常见分层:
-
客户端/浏览器缓存:HTTP 缓存、LocalStorage。
-
CDN 缓存:静态资源、图片、视频分发。
-
应用本地缓存:进程内缓存,如 Caffeine、Guava Cache,速度快但多实例之间不一致。
-
分布式缓存:如 Redis、Memcached,多个应用实例共享。
-
数据库缓存/缓冲池:MySQL InnoDB Buffer Pool 等。
-
CPU 缓存:L1/L2/L3,最底层但最快。
后端日常说"加缓存",通常指 Redis 这类分布式缓存 + 本地缓存。
4. 缓存 key 和 value
缓存里每条数据通常以键值对存:
-
key :唯一标识,如
user:1001、product:2002。 -
value:序列化后的数据,如 JSON、二进制、对象。
-
TTL:这个 key 多久过期。
"热点 key"就是被高频访问的 key,比如爆款商品、首页配置、秒杀库存。
5. 常见的缓存读写模式
最常用的是 Cache Aside(旁路缓存):
读:
-
先读缓存;
-
命中直接返回;
-
未命中查 DB;
-
把结果写回缓存,设置 TTL。
写:
-
先更新 DB;
-
再删除缓存,而不是直接更新缓存。
因为直接更新缓存容易并发覆盖,删除后下次读再重建更常见。
其他模式:
-
Read Through:缓存层代理读,未命中由缓存组件回源。
-
Write Through:写缓存时同步写后端。
-
Write Behind:先写缓存,异步批量写 DB,性能高但可能丢数据。
6. 缓存为什么会失效
缓存不是永久存储,常见失效原因:
-
TTL 到期:设置了过期时间,时间到就失效。
-
主动删除:数据更新后删缓存。
-
内存淘汰:内存满了,按 LRU/LFU 等策略淘汰。
-
缓存服务故障:Redis 宕机、网络分区。
-
手动清理/发布重启:本地缓存随进程重启丢失。
7. 缓存带来的典型问题
缓存穿透、缓存击穿、缓存雪崩是缓存系统常见的三类故障场景:
| 问题 | 核心原因 | 典型表现 | 常见保护措施 |
|---|---|---|---|
| 缓存穿透 | 查询的数据缓存和数据库都不存在,请求每次都绕过缓存打到数据库 | 恶意用不存在的 ID 高频查询,DB 压力大 | 缓存空值/短 TTL;布隆过滤器提前拦截;参数合法性校验;限流;对异常查询做风控 |
| 缓存击穿 | 某个热点 key 过期瞬间,大量并发请求同时打到数据库 | 热点商品/配置过期时 DB 瞬间压力飙升 | 互斥锁/单飞保证只有一个请求回源;逻辑过期;热点 key 预热或永不过期;限流降级 |
| 缓存雪崩 | 大量 key 同时过期,或缓存服务宕机,导致请求大面积打到数据库 | 大批缓存失效后 DB 被压垮,甚至级联故障 | TTL 加随机值、分批过期;缓存集群高可用;多级缓存;预热;熔断、限流、降级;持久化和故障转移 |
-
穿透 :查不存在的数据,缓存挡不住。
-
击穿 :一个热点 key 过期,瞬间打穿缓存。
-
雪崩 :大量 key 同时失效或缓存整体不可用,数据库被大面积压垮。
此外还有:
-
缓存一致性:DB 更新后缓存旧数据,需要删除/延迟双删/订阅 binlog 等策略。
-
热 key:单个 key 流量过大,可能打爆单个缓存节点,需要分片、本地缓存、限流。
-
大 key:value 太大,影响网络和内存,需要拆分。
8. 一个完整例子
商品详情缓存:
bash
key: product:1001
value: {"id":1001,"name":"手机","price":3999}
TTL: 30分钟
请求流程:
-
用户查商品 1001;
-
先查 Redis
product:1001; -
命中,直接返回,DB 无压力;
-
未命中,查 MySQL;
-
查到后写回 Redis,设置 30 分钟 TTL;
-
下次请求继续命中缓存。
如果 product:1001 是爆款,30 分钟后过期瞬间大量请求同时未命中,就可能发生缓存击穿。
总结
缓存 = 高速临时数据副本 + 命中优先 + 未命中回源 + 过期/淘汰机制。
它的核心价值是加速访问、降低 DB 压力、提升吞吐 ;核心代价是一致性、内存成本、失效和故障处理。后端设计缓存时,不只是"加个 Redis",还要考虑:命中率、TTL、淘汰策略、更新一致性,以及穿透、击穿、雪崩保护。