目录
[2.1 主动更新的三种策略](#2.1 主动更新的三种策略)
一、什么是缓存?
缓存就是将极其耗时、计算昂贵或频繁访问的数据,临时存储在访问速度更快的介质中(通常是内存),以便下次请求时能直接获取,而无需重新计算或从慢速设备(如硬盘数据库)中读取。

二、缓存更新策略

业务场景:
- 低一致性需求:使用内存淘汰机制。例如店铺类型的查询缓存。
- 高一致性需求:主动更新,并以超时剔除作为兜底方案。例如店铺详情查询的缓存。
2.1 主动更新的三种策略
缓存主动更新有三种策略:Cache Aside Pattern、Read/Write Through Pattern、Write Behind Caching Pattern。
| 策略名称 | 核心运作机制 | 主要优点 | 主要缺点 | 适用场景 |
|---|---|---|---|---|
| Cache Aside (旁路缓存) | 应用程序同时负责维护数据库和缓存。读 :先读缓存,未命中则读数据库并回写缓存。写:先更新数据库,再删除缓存。 | 1. 资源利用率高:缓存中只保留实际被请求的数据。 2. 实现灵活:不依赖特定的缓存中间件特性,适用范围最广。 | 1. 代码侵入性强:应用层需要编写大量操作数据库和缓存的控制逻辑。 2. 极端并发风险:在特定的读写并发场景下,仍有极小概率出现数据不一致。 | 绝大多数常规 Web 业务(如电商、社交网络),是目前 Redis + MySQL 架构下最标准的做法。 |
| Read/Write Through (读写穿透) | 应用程序只和缓存层交互。缓存层封装了对数据库的操作。读 :缓存未命中时,由缓存服务去查数据库并加载数据。写 :应用更新缓存,缓存服务同步更新数据库。 | 1. 代码极简:对应用程序透明,应用层无需关心数据库同步逻辑。 2. 数据一致性高:缓存和数据库由底层服务同步控制。 | 1. 性能损耗:写操作需要同步等待数据库落盘,写入速度受制于数据库。 2. 门槛较高:大多数主流分布式缓存(如原生 Redis)不直接支持此模式,需要自行开发代理层或使用特定组件。 | 对数据一致性要求极高,且读多写少的场景;或者使用了原生支持该模式的本地/分布式缓存组件(如 Guava/Ehcache)。 |
| Write Behind (异步写回) | 应用程序只更新缓存,操作直接返回。缓存服务随后在后台异步、批量地将数据更新到数据库。 | 1. 极致写性能:应用所有的写操作都在内存中完成,响应极快。 2. 保护数据库:通过合并和批量写入,大幅降低数据库的 IO 压力。 | 1. 数据丢失风险极高:如果缓存节点在数据异步刷盘前宕机,数据将彻底丢失。 2. 强一致性无法保证:由于是异步写入,数据库数据会有明显的延迟。 3. 实现异常复杂:需要解决冲突、重试、持久化队列等问题。 | 写操作极其频繁且允许一定数据丢失的场景。例如:视频播放量统计、大规模实时日志收集、高并发秒杀系统中的初级计数。 |
使用Cache Aside来更新缓存有三个问题需要我们考虑:
1.删除缓存还是更新缓存?
- 更新缓存:每次更新数据库都更新缓存,无效写操作较多
- 删除缓存:更新数据库时让缓存失效,查询时再更新缓存
2.如何保证缓存和数据库的操作的同时成功和失败?
- 单体系统,将缓存与数据库操作放在一个事务
- 分布式系统,利用TCC等分布式事务方案
3.先操作缓存还是先操作数据库?
方案一中的线程一执行更新数据库的操作是很耗时的,在此时线程二要来查询数据库并写入缓存的可能性是很大的,很容易造成数据不一致的情况。方案二中线程一查询缓存并写入缓存的速度极快,此时线程二来更新数据库需要很长时间,当线程一将旧的数据库数据写入缓存后,尽管线程二同时更新数据库了,也是线程一先写入缓存的概率更大一些。因此我们选择先操作数据库,在删除缓存。
三、缓存穿透
缓存穿透就是指,请求的数据根本不存在,比如用户一直查 id=-1 或不存在的商品 id。
下图展示了两种解决缓存穿透的方案:
- 缓存空值,比如 product:999 = null,TTL 设置短一点。
- 使用布隆过滤器,先判断 id 是否可能存在。

四、缓存雪崩
缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

解决方案:
- 给不同的Key的TTL添加随机值
- 利用Redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
五、缓存击穿
缓存击穿也叫热点key问题,某个热点 key 过期瞬间,大量请求同时访问,全部打到数据库。
例如热门商品详情缓存刚好失效:
10万个请求 -> Redis 未命中 -> 数据库压力暴涨

解决办法:
- 加互斥锁,只允许一个请求回源数据库。
- 热点 key 不设置短 TTL,改为后台刷新。
- 逻辑过期:缓存里存过期时间,过期后先返回旧值,同时异步刷新。
- 随机提前刷新,避免集中失效。

