redis 缓存问题:穿透、雪崩、污染、一致性

缓存穿透(缓存和数据库中都没有的数据)

这种情况,如果不加以处理,请求必然打在数据库,如果请求量过大,DB 就挂了。很容易被恶意攻击,比如频繁查询 id 是 -1 的数据

解决方案

  1. 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截
  2. 从缓存取不到的数据,在数据库中也没有取到,这时也可以将 key-value 对写为 key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
  3. 布隆过滤器。bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小

缓存雪崩(缓存中没有但数据库中有的数据)

还有个概念是缓存击穿,击穿和雪崩的根本原因都是 redis 没有而数据库中有,二者区别就是:击穿只同一条数据高并发导致的访问量大;雪崩是多条数据累计的访问量大

原因可能是热点数据过期(既然是热点,访问就很频繁),或者大批量缓存同时过期。都会到 DB 去查询从而增加 DB 压力。

解决方案

  1. 热点数据永不过期
  2. 非热点数据,过期时间不要太集中,可以使过期时间在默认的过期时间基础上随机加上一点时间,分散过期
  3. 接口限流、熔断、降级处理,重要的接口特殊处理(比如秒杀,肯定并发访问高)

缓存污染(或满了)

指的是缓存中存在大量 不常访问 的数据,随着服务不断运行,很可能发生这种情况
解决方案也很简单,配置合适的淘汰策略,要么增加服务器内存

一致性

对于查询没什么好说的,都是先查缓存,有就返回;没有就查询数据库,再把数据库的写入缓存

数据不一致往往都是更新操作导致的,更新无非两种方式,要么先删除缓存再更新数据库,要么先更新数据库再删除缓存

  1. 先更新数据库再删缓存的问题
线程a 线程b
更新数据库
查询(这时查询的是缓存数据,还未更新)
删除缓存
  1. 先删缓存再更新数据库的问题
线程a 线程b 线程c
删除缓存
查询(缓存没数据,查数据库,然后写入缓存)
更新数据库
查询(线程b已经写入了缓存,此时缓存和数据库就不一致了)

解决方案:延时双删

  1. 先删除缓存
  2. 更新数据库
  3. 延迟 n 秒,再次删除缓存
线程a 线程b 线程c
删除缓存
查询(缓存没数据,查数据库,然后写入缓存)
更新数据库
延迟几秒后再次删除缓存
查询(缓存没数据,查数据库,然后写入缓存)

为什么要延时?延时多久合适?

  • 数据库主从同步或多节点同步需要时间,先让数据库都一致
  • 再次删除的目的是删除 线程b 写入的缓存数据,所以要等线程b写完,不然再次删除就没东西删
  • 一般3-5秒足够了

redis 服务也可能不可靠,比如删除 redis 失败,解决方案可以是 队列+重试 或者订阅 binlog 两种

  • 队列+重试

    1. 更新数据库数据
    2. 缓存因为种种问题删除失败
    3. 将需要删除的key发送至消息队列
    4. 自己消费消息,获得需要删除的key
    5. 继续重试删除操作,直到成功
  • 订阅 binlog

队列+重试 会导致业务侧代码侵入比较严重,一个简单的缓存 key 操作,要使用消息队列还要不断重试。于是有了订阅 mysql 的 binlog 方式,就是类似 mysql 的主从同步思想,使用 binlog 来刷缓存

posted @ 2023-06-28 17:55  CyrusHuang  阅读(13)  评论(0编辑  收藏  举报