Redis缓存架构实战与优化
约 1067 字大约 4 分钟
2025-08-30
20%的代码实现80%的需求,80%的代码处理20%的特殊场景。
缓存穿透
查询一个根本不存在的数据,导致这个不存在的数据每次请求都无法命中缓存,请求直接穿透到数据库上。
利用在缓存查询不到数据,再去mysql查询的机制(没有查询到数据直接返回),攻击者刻意制造不存在于mysql数据库数据的条件,去高频请求,将大量的请求直接穿过缓存,打向了数据库存储层。
预防解决方案
缓存空数据
从MySQL数据库查询数据为空时,也进行一次缓存,缓存空数据。
加锁
大量线程去数据库请求同一数据,可用互斥锁(RLock),缓存失效的情况,只有拿到锁才可以继续往下去查询数据库,降低了在同一时刻打在数据库上的请求,防止数据库打死。(加锁对系统的影响也就那么一瞬,只要数据从数据库里查询出来放到了缓存里,就能解决)
缓存击穿
某个热点key(访问量巨大)在缓存数据还不存在或过期的时间,同时有大量请求进来,导致所有请求都无法命中缓存,全部去访问数据库。
- 比喻:一家超级网红店(热点key)休息时间(缓存过期)结束了,门口排着长队(大量请求)瞬间涌入,导致收银台(数据库)压力巨大。
- 关键特征:
- 数据在数据库中是存在的。
- 是一个热点key,并发访问量非常大。
- 缓存**恰好在这个时间点“失效”**了。
危害:瞬间的巨大并发压力可能击垮数据库。
预防解决方案
大量请求同时访问数据库?
添加分布式互斥锁(RLock),当缓存失效时,不是所有线程都去查数据库,而是只让 一个线程 去查询数据库并重建缓存,其他线程等待并重试缓存查询。
缓存雪崩
大量的缓存key在同一时间点或时间段内失效,导致所有请求都落到了数据库上,引起数据库压力过大甚至宕机。
大量请求超过redis服务可提供的荷载,导致缓存崩溃宕机等场景(或者突然大量缓存同时间到期删除),将请求压力导向了mysql数据存储层,导致mysql数据库服务压力陡增,从而不可用,然后因为mysql服务不可用,而导致web服务不可用,web服务a的不可用,又会导致业务有关联这个web a服务的其他服务不可用,最终导致扩展到整个系统不可用。
预防解决方案
redis宕机了?
要保证缓存的高可用,用 redis sentinel 和 redis cluster
大量请求?
增加请求限流熔断降级,用sentinel。
缓存同时失效?
不要设置相同的失效时间,失效时间上加上一个随机值,比如1-5分钟随机,避免了因为采用相同的过期时间导致的缓存雪崩。
总结
| 特性 | 缓存穿透 | 缓存击穿 | 缓存雪崩 |
|---|---|---|---|
| 问题本质 | 查询不存在的数据 | 单个热点key过期 | 大量key同时失效或缓存服务宕机 |
| 缓存状态 | 缓存和DB均无数据 | 缓存刚好失效,DB有数据 | 缓存大规模失效,DB有数据 |
| 请求规模 | 大量请求查询不同的不存在key | 大量请求并发查询同一个key | 大量请求查询很多不同的key |
| 危害对象 | 数据库 | 数据库 | 数据库 + 整个系统 |
| 解决方案 | 1. 缓存空值 2. 布隆过滤器 | 1. 互斥锁 2. 逻辑过期 3. 永不过期 | 1. 错开过期时间 2. 高可用集群 3. 服务降级熔断 |
简单记忆:
- 穿透:查无此物,恶意攻击。
- 击穿:热点失效,万箭穿心。
- 雪崩:集体失效,全面崩溃。
