技术分享
Redis缓存:缓存雪崩、穿透和击穿
00 分钟
2024-4-11
2024-4-21
type
status
date
Apr 21, 2024 09:50 AM
slug
summary
category
tags
password
icon

什么是缓存?

缓存就是数据交换的缓冲区(称作Cache),是存贮数据的临时地方,一般读写性能较高。
notion image

缓存的作用

  • 降低后端负载
  • 提高读写效率,降低响应时间

缓存的成本

  • 数据一致性成本
  • 代码维护成本
  • 运维成本

Redis缓存模型

notion image

根据id查询商铺缓存的流程

notion image

缓存首页的店铺类型

缓存的原理与前面根据 id 查询店铺信息相同。

缓存更新策略

  • 内存淘汰
  • 超时剔除
  • 主动更新
notion image
业务场景:
  • 低一致性需求:使用内存淘汰机制。例如店铺类型的查询缓存
  • 高一致性需求:主动更新,并以超时剔除作为兜底方案。例如店铺详情查询的缓存

主动更新的三种策略

  • Cache Aside Pattern(胜出)
    • 由缓存的调用者,在更新数据库的同时更新缓存
  • Read/Write Through Pattern 缓存与数据 库整合为一个服务,由服务来维护一致性。调用者调用该服务,无需关心缓存一致性问题。
  • Write Behind Caching Pattern 调用者只操作缓存,由其它线程异步的将缓存数据持久化到数据库,保证最终一致。

操作缓存和数据库时需要考虑三个问题

虽然在实际工作中使用Cache Aside Pattern(胜出),但是还需要考虑下面的三个问题。

删除缓存还是更新缓存?

  • 更新缓存:每次更新数据库都更新缓存,无效写操作较多(pass)
  • 删除缓存:更新数据库时让缓存失效,查询时再更新缓存(胜出)

如何保证缓存与数据库的操作的同时成功或失败?

  • 单体系统, 将缓存与数据库操作放在一个事务
  • 分布式系统,利用TCC等分布式事务方案

先操作缓存还是先操作数据库?

  • 先删除缓存,再操作数据库(pass)
  • 先操作数据库,再删除缓存(胜出)
    • notion image
notion image
💡
总结——缓存更新策略的最佳实践方案:
  1. 低一致性需求:使用Redis自带的内存淘汰机制
  1. 高一致性需求:主动更新,并以超时剔除作为兜底方案
      • 读操作:
        • 缓存命中则直接返回
        • 缓存未命中则查询数据库,并写入缓存,同时设定超时时间
      • 写操作:
        • 先写数据库,然后再删除缓存
        • 要确保数据库与缓存操作的原子性
  • 解决商铺缓存与数据库的写一致问题

缓存穿透

定义

缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。

解决方案

常见的解决方案有两种:缓存空对象和布隆过滤

缓存空对象

  • 优点:
    • 实现简单,维护方便
  • 缺点:
    • 额外的内存消耗
    • 可能造成短期的不致

布隆过滤

  • 优点:
    • 内存占用较少,没有多余key
  • 缺点:
    • 实现复杂
    • 存在误判可能
notion image

解决缓存穿透流程图

notion image
💡
总结:
  • 缓存穿透产生的原因是什么?
    • 用户请求的数据在缓存中和数据库中都不存在,不断发起这样的请求,给数据库带来巨大压力
  • 缓存穿透的解决方案有哪些?
    • 缓存null
    • 布隆过滤
    • 增强id的复杂度,避免被猜测id规律
    • 做好数据的基础格式校验
    • 加强用户权限校验
    • 做好热点参数的限流

缓存雪崩

定义

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

解决方案

  • 给不同的Key的TTL添加随机值
  • 利用Redis集群提高服务的可用性
  • 给缓存业务添加降级限流策略
  • 给业务添加多级缓存
notion image

缓存击穿

定义

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了, 无数的请求访问会在瞬间给数据库带来巨大的冲击。
notion image

解决方案

互斥锁

notion image
  • 基于互斥锁解决缓存击穿问题流程图
notion image
  1. 这里使用Redis中的setnx指令实现互斥锁,只有当值不存在时才能进行set操作
  1. 锁的有效期更具体业务有关,需要灵活变动,一般锁的有效期是业务处理时长10~20倍
  1. 线程获取锁后,还需要查询缓存(也就是所谓的双检),这样才能够真正有效保障缓存不被击穿
  • 测试
    • 通过 Jmeter 进行压测,在5秒内发送2000个请求(理论上QPS可以达到400/sec,也就是每秒钟发送400个请求),最终吞吐量是331.3(也就是每秒钟处理134.8个请求),系统性能还是比较好的
      notion image

逻辑过期

notion image
  • 基于逻辑过期解决缓存击穿问题流程图
notion image

两种方案的优缺点

notion image

总结

RedisTemplate封装工具类

缓存解决数据不一致

  • 为了解决数据一致性问题,我们可以选择适当的缓存更新策略:
    • 缓存主动更新双写方案+删除缓存模式+先操作数据库后操作缓存+事务)为主,超时剔除为辅
        1. 查询时,先查询缓存,缓存命中直接返回,缓存未命中查询数据库并重建缓存,返回查询结果
        1. 更新时,先修改数据删除缓存,使用事务保证缓存和数据操作两者的原子性
  • 除了会遇到数据一致性问题意外,我们还会遇到缓存穿透、缓存雪崩、缓存击穿等问题
      1. 对于缓存穿透,我们采用了缓存空对象解决
      1. 对于缓存击穿,我们分别演示了互斥锁setnx实现方式)和逻辑过期两种方式解决。
 

评论
Loading...