lusiqi

redis系列-缓存雪崩和缓存穿透问题,简单redis的缓存雪崩和缓存穿透问题,及相应的解决方案。


缓存雪崩

简介

缓存雪崩是指在同一时间,redis缓存大面积失效,后面的请求都会落在数据库上,造成数据库短时间内承受大量的请求而崩掉。

解决办法

  • 部署redis集群,尽量保证集群的高可用性,发现机器宕机尽快补上,选择合适的内存淘汰策略。
  • 本地使用ehcache缓存+hystrix限流降级 ,避免高并发使mysql数据库崩掉。
  • 做好redis持久化,发生缓存雪崩后,利用redis持久化保存的数据,尽快恢复缓存。

使用本地ehcache缓存+hystrix限流降级请求访问过程:系统收到请求后,先查询本地ehcache缓存,如果没查到在查redis。如果ehcache和redis都没有,在查数据库,将数据库中的结果,写入ehcache和redis中。这样做的好处:

  • 数据库不会直接崩掉,限流组件确保了每秒只有多少请求能通过,剩余的等待。
  • 只要数据库不死,对用户来说,五分之二的请求都可以被处理的。
  • 对于用户来说,只是等待的时间长了一点,确保了数据不会丢失。

缓存穿透

简介

缓存穿透说简单点就是大量请求的key不存在于缓存中,导致请求直接落到了数据库上,根本没有经过缓存这一层。大量的并发查询数据库,就会直接让数据库崩掉。

正常的缓存处理:

一般Mysql默认最大连接数在150左右,可以通过命令 show variables like ‘%max_connections%’; 查看。cpu、内存、磁盘、网络都会限制并发能力,一般3000个并发请求就能打死大部分数据库了。

解决办法

  • 缓存无效 key:如果缓存和数据库都查不到,就缓存一个不存在的key,并设置过期时间,命令为:

    1
    SET key value EX 10086

    这种方式可以解决请求的key变化不频繁的情况,如果每次构建不同的key,会导致redis缓存大量的无效key,使用这种方式的话,尽量将无效的key过期的时间设置尽量小一点。

    java代码实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public Object getObjectInclNullById(Integer id) {
    // 从缓存中获取数据
    Object cacheValue = cache.get(id);
    // 缓存为空
    if (cacheValue == null) {
    // 从数据库中获取
    Object storageValue = storage.get(key);
    // 缓存空对象
    cache.set(key, storageValue);
    // 如果存储数据为空,需要设置一个过期时间(300秒)
    if (storageValue == null) {
    // 必须设置过期时间,否则有被攻击的风险
    cache.expire(key, 60 * 5);
    }
    return storageValue;
    }
    return cacheValue;
    }
  • 布隆过滤器:布隆过滤器是由二进制向量(或者数组)和一系列的映射哈希函数 两部分组成的数据结构。把所有存在的请求的值都存放在布隆过滤器中,当请求过来,先判断请求是否在布隆过滤器中,如果不存在直接返回错误提示,存在才会继续走下面流程。

缓存击穿

简介

缓存击穿就是某个key非常热点,访问非常频繁,处于集中高并发访问,当这个key在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。

解决办法

  • 如果缓存的数据基本不会发生更新,尽量不要将热点数据设置过期时间
  • 如果数据更新不频繁,可以采用基于redis、zookeeper等分布式中间件的分布式互斥锁,或者本地互斥锁以保证大量并发访问时,少部分可以访问,其余请求等待锁释放后访问。
  • 如果数据更新频繁,可以利于定时线程在缓存过期前重新构建缓存或者延后过期时间。

 评论