Redis基础

缓存穿透

缓存穿透,是指查询一个数据库不存在的数据。对于系统A,假设一秒 5000 个请求,结果其中 4000 个请求是黑客发出的恶意攻击。黑客发出的那 4000 个攻击,缓存中查不到,每次你去数据库里查,也查不到。

举个栗子。数据库 id 是从 1 开始的,结果黑客发过来的请求 id 全部都是负数。这样的话,缓存中不会有,请求每次都“视缓存于无物”,直接查询数据库。这种恶意攻击场景的缓存穿透就会直接把数据库给打死。

image-20200308172751346

处理方案

  1. 判断合法的请求:接口层增加校验,比如用户鉴权,参数做校验,不合法的校验直接 return,比如 id 做基础校验,id<=0 直接拦截。
  2. 当查询不存在时,也将结果保存在缓存中。但是这可能会存在一种问题:大量没有查询结果的请求保存在缓存中,这时我们就可以将这些请求的key设置得更短一些。
  3. 布隆过滤器(Bloom Filter):利用高效的数据结构和算法快速判断出你这个 Key 是否在数据库中存在。

缓存击穿

缓存击穿指的是:一个 Key 非常热点,在不停地扛着大量的请求,大并发集中对这一个点进行访问,当这个 Key 在失效的瞬间,持续的大并发直接落到了数据库上,就在这个 Key 的点上击穿了缓存。

处理方案

  1. 多级缓存:结合redis缓存和本地缓存(ehcache、guava等)搭建多级缓存。将一些重要的热点数据存储到本地缓存中,有效缓解redis的压力。

  2. 用加锁或者队列的方式保证缓存的单线程(进程)写,在加锁方法内先从缓存中再获取一次,没有再查DB写入缓存。

    public static String getData(String key) throws InterruptedException {
            //从Redis查询数据
            String result = getDataByKV(key);
            //参数校验
            if (StringUtils.isBlank(result)) {
                try {
                    //获得锁
                    if (reenLock.tryLock()) {
                        //去数据库查询
                        result = getDataByDB(key);
                        //校验
                        if (StringUtils.isNotBlank(result)) {
                            //插进缓存
                            setDataToKV(key, result);
                        }
                    } else {
                        //睡一会再拿
                        Thread.sleep(100L);
                        result = getData(key);
                    }
                } finally {
                    //释放锁
                    reenLock.unlock();
                }
            }
            return result;
        }
    

缓存雪崩

缓存雪崩是指:大量缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩,导致数据库CPU和内存负载过高,甚至宕机。

缓存雪崩和缓存击穿情况比较相似,缓存击穿是一个高并发的热点key,在失效的瞬间,大量同个数据查询的请求直接落到数据库。而缓存雪崩是指大面积缓存在同一短时间内失效,导致大量各种数据请求落到数据库上。

缓存击穿是点击破,缓存雪崩是面击破。

举个栗子:如果首页所有 Key 的失效时间都是 12 小时,中午 12 点刷新的,我零点有个大促活动大量用户涌入,假设每秒 6000 个请求,本来缓存可以抗住每秒 5000 个请求,但是缓存中所有 Key 都失效了。此时 6000 个/秒的请求全部落在了数据库上,数据库必然扛不住,真实情况可能 DBA 都没反应过来直接挂了。

image-20200308173225345

处理方案

  1. 缓存高可用:搭建高可用redis集群,主从+哨兵,redis cluster,避免全盘崩溃。

    即使个别节点、个别机器、甚至是机房宕掉,依然可以提供服务,例如 Redis Sentinel 和 Redis Cluster 都实现了高可用。

  2. 限流&降级: hystrix 限流&降级。

    当访问量剧增、服务出现问题仍然需要保证服务还是可用的。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级,这里会涉及到运维的配合。降级的最终目的是保证核心服务可用,即使是有损的。

  3. 多级缓存:结合redis缓存和本地缓存(ehcache、guava等)搭建多级缓存。将一些重要的热点数据存储到本地缓存中,有效缓解redis的压力。

  4. 缓存失效时间点均匀分布:尽量让失效时间点均匀分布,设置不同的过期时间。在设置Redis键的过期时间时,加上一个随机数,这样可以避免。

  5. Redis持久化和快速预热:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

总的来说缓存雪崩,可能是同一时间内大面积缓存时效,或者是缓存服务故障。大面积失效情况,可以在设置缓存有效期是增加一个随机值,使得缓存有效期分布均匀;或则设置多级缓存机制,多一层本地缓存机制,但是要考虑哪些数据适合放入本地缓存。缓存服务器故障情况,需要考虑缓存集群的高可用、缓存的持久化和快速预热。

总结

  • 事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。
  • 事中:本地 ehcache 缓存 + Hystrix 限流+降级,避免MySQL 被打死。
  • 事后:Redis 持久化 RDB+AOF,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

参考资料

https://www.cnblogs.com/yoishion/p/10791501.html

面试前,我们要复习多少Redis知识点?

https://juejin.im/post/5db66ed9e51d452a2f15d833

原文地址:https://www.cnblogs.com/zhaooo/p/13973890.html