Redis学习笔记(7)——常见问题

缓存击穿

  • 热点key在某一时间过期,此时大量并发请求进来,直达DB,造成DB压力骤增。
  • 解决方案:
    • 使用互斥锁,同时只能有1个线程能得到锁并访问DB,其他线程等待缓存构建完,再从缓存获取数据。
    • 设置热点数据永不过期,由定时任务异步加载数据,更新缓存。

缓存雪崩

  • 缓存同一时间大面积失效,请求直接落到DB上,造成DB压力骤增。
  • 解决方案:
    • 给缓存过期时间加上一个随机值,防止key在同一时刻失效。
    • 设置热点数据永不过期。
    • 缓存限流(互斥锁)、降级(启用备用缓存)。

缓存穿透

  • 大量请求访问DB中不存在的key,请求直接落到DB上,且无法写入缓存,造成缓存起不到作用。
  • 解决方案:
    • 接口校验,直接对无效key过滤。
    • 将这些无效key写入缓存,设置其值为null,设置很短的过期时间。
    • 布隆过滤器。布隆过滤器由一个二进制数组bitMap和若干哈希函数组成,向容器中添加数据时,将key通过哈希函数映射到bitMap,将对应的值置为1。判断一个key是否存在,只需要判断这个key通过哈希函数映射到的各个值是否全部为1,只要存在某个值不为1,则它一定不在容器的键空间中。布隆过滤器只能判断一个key一定不存在,但无法保证一定存在。

双写不一致

  • 在同一时刻,Redis和DB中的数据不一致。
  • 解决方案:
    • 如果不要求强一致性,只要保证最终一致性,可以给key设置较短的过期时间。
    • 先删缓存,再更新数据库。这样可能产生的问题:线程1删除缓存,线程2读取数据库并写入缓存,线程1更新数据库,此时缓存中缓存了脏数据。因此,需要延时双删等策略防止脏数据。
    • 先更新数据库,再删除缓存。这里可能由于删缓存失败导致产生脏数据。解决方法:使用消息队列不断重试确保删除缓存成功;订阅DB的binlog,尝试删除缓存,如果失败放入消息队列不断重试。

缓存无底洞

  • 分布式缓存数据量特别大时,批量获取多个key由于分布在多个不同实例,需要多次网络IO,性能下降。
  • 解决方案:
    • 串行MGET,将N个key拆解为N次GET操作。实现简单,性能较低。
    • 串行IO,计算每个key对应的节点,分别访问对应的节点。实现简单,性能较低。
    • 并行IO,将串行IO的网络请求改为多线程执行,实现复杂,性能较好。
    • hash-tag,强制将多个key分配到一个节点上,性能最高,但维护成本高,容易出现数据倾斜。