Redis(三):常见异常及解决方案

缓存使用过程中,我们经常遇到的问题有一下四个:

  • 缓存穿透
  • 缓存雪崩
  • 缓存预热
  • 缓存降级

1、缓存穿透

一般访问缓存的流程,如果缓存中存在查询的商品数据,那么直接返回,如果缓存中不存在,则访问数据库

image.png

由于不恰当的业务功能实现,或者外部恶意攻击不断地请求某些不存在的数据内存,由于缓存中没有保存该数据,导致所有的请求都会落到数据库上,对数据库可能带来一定的压力,甚至奔溃。

解决方案:

针对缓存穿透的情况,简单是对策就是将不存在的数据访问结果,也存储到缓存中,可以有效的避免缓存穿透的风险。但是这样的话,可能会浪费大量的内存,那么可以使用布隆过滤器来节省内存空间,redis本身也是支持这种方式,布隆过滤器的介绍见海量数据下的去重和查重(二):布隆过滤器

同时,为了避免无效key过多,key要有规则,如果不满足key生成规则,则直接返回。

2、缓存雪崩

当缓存重启或者大量的缓存在某一时间段失效,这样就导致大批流量直接访问数据库,对DB造成压力,从而引起DB故障,系统奔溃。

举例来说,我们在准备一项抢购的促销运营活动,活动期间将带来大量的商品信息,库存等相关信息的查询,为了避免商品数据库的压力,将商品数据放入缓存中存储,不巧的是,抢购活动期间,大量的热门商品缓存同时失效过期了,导致很大的查询流量落到了数据库之上,对于数据库造成了很大的压力。

解决方案

  • 将商品根据品类热度分类,购买比价多的类目缓存周期长一些,购买相对冷门的类目商品,缓存周期短一些;
  • 在设置商品具体的缓存生效时间的时候,加上一个随机的区间因子,比如说5—10分钟之间来随意选择失效时间;
  • 提前预估DB能力,如果缓存挂掉,数据库仍可以在一定程度上抗住流量的压力

这三个策略能够有效的避免短时间被,大批量的缓存失效的问题。

3、缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统,这样就避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。

image.png

如果不进行预热,那么redis初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中,对数据库造成流量的压力。

解决方案

  • 1、数据量不大的时候,工程启动的时候进行加载缓存动作
  • 2、数据量大的时候,设置一个定时任务脚本,进行缓存的刷新
  • 3、数据量太大的时候,优先保证热点数据进行提前加载到缓存

4、缓存降级

降级的情况,就是缓存失效或者缓存服务挂掉的情况下,我们也不去访问数据库,我们直接访问内存部分数据缓存,或者直接返回默认数据。

举例来说:
对于应用的首页,一般是访问量非常大的地方,首页里面往往包含了部分推荐商品的展示信息,这些推荐商品都会放到缓存中进行存储,同时我们为了避免缓存的异常情况,对热点数据也存储到了内存中,同时内存中还保留了一些默认的商品信息,

image.png

降级一般是有损的操作,所以尽量减少降级对于业务的影响程度。

5、数据倾斜 bigkey

数据倾斜,是指大量的数据都集中到集群的某一个节点上,导致那个节点内存和cpu使用率都显著升高。
通常都是由于不合理的数据结构设计导致的。

Redis 中大key的危害,大key带来的危害体现在三个方面:

    1. 内存空间不均匀;
    1. 操作耗时,存在线程阻塞风险;
    1. 网络阻塞,每次获取大key产生的网络流量较大。
      如果一个key的大小为1MB,每秒访问量为1000,那么每秒会产生1000MB的流量。这对于普通千兆网卡的服务器来说是灾难性的。

redis-cli --bigkeys 命令可以统计bigkey的分布情况。
如果bigkey是不可避免的,比如string类型的大key,那么建议不要存入Redis,因为每次调用都会产生比较严重的性能影响,而是放到内存中,通过内存缓存。

  • 生产案例1
    有个活动上线后,发现有个redis单节点内存使用率在不断的增加
    在查询相关代码时,发现缓存用户参与数据用的是hash结构,field为userId,那么随着时间的增加,参与用户越来越多,key保持不变,但是value会越来越大

优化:hash结构拆分为string结构,把userId拼接到key上,这样可以把key打散

  • 生产案例2
    有个需要根据策略计算给用户发券的接口,策略可能有几十个(产品配置),把这些策略的基本信息缓存到redis后,发现value有60k大小,在业务高峰期,请求量暴涨,直接把redis cpu打满,导致阻塞。

优化:把这些配置信息放到内存中,优化从内存中获取,只有内存不存在,才去redis中获取

  • 生产案例3
    有个活动必须要把要把用数据设计成hash结构,userId作为field,但是为了降低内存使用率,使用了定时任务每天定时清理一次缓存,即删除key,在有一次业务高峰期时,redis内存预警,为了减少空间,手动把这个bigkey删除了,因为value有7.5G大小,redis 删除操作耗时60s,直接导致线程阻塞,服务不可用。

优化:如果bigkey由于某种原因是必须的,但是当我们用完后需要删除时(比如定时任务预热缓存时,需要先删除bigkey,然后再set数据),不要直接del删除bigkey,因为删除时间会过长,导致阻塞其他客户端命令。

对于4.0版本上且开启了异步删除(即lazyfree-lazy-expire=yes),可以设置过期时间自动删除 Expire KEY 0;

对于4.0版本以下的,就算设置过期时间也会触发del 操作(同步删除),而且因为过期时间触发del导致的阻塞,不会出现在慢查询中,所以有时候遇到问题都不好排查。
bigkey的删除见上一张第九节 Redis(二):实战场景及实现方式

6、热key

在一些临时活动中(比如每年中秋 端午商家都会搞很多活动,每年都不一样),为了保证活动灵活性,我们通常会有大量配置信息(开关,活动参与人数,活动时间),为了性能,这些配置信息通常会放在redis中,但是由于每个参与用户都需要去redis中请求这些配置信息,所以高峰期大量请求还是会打到redis集群某个节点上,这样集群的性能就限制在单点上了。

优化:通常这种热key数据量都不大,可以直接缓存在内存中,然后可以通过定时任务定时去清理,或者在管理端添加一个热key管理页面,这样可以手动去清理。

你可能感兴趣的:(Redis(三):常见异常及解决方案)