46

快速了解缓存穿透与缓存雪崩

 5 years ago
source link: https://www.tuicool.com/articles/BNvURj3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

缓存穿透

缓存系统,一般流程都是按照key去查询缓存,如果不存在对应的value,就去后端系统(例如: 持久层数据库)查找。 如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力,这就叫做缓存穿透。

正常请求:

zEV7Vnu.png!web

缓存击穿时:

7reyQvF.png!web

如何避免

1. 缓存空结果

对查询结果为空的情况进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。

2. 布隆过滤器

采用布隆过滤器,guava有实现api,或者使用redis的bitmap。 将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。 布隆过滤器对于固定的数据可以起到很好的效果,但是对于频繁更新的数据,布隆过滤器的构建会面临很多问题。 另外布隆过滤器是有判断误差的,网上有很多详细的介绍,请读者自行搜索即可。

6RJNVrF.png!web

缓存雪崩

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。

如何避免

1. 互斥锁

在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。 比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

3Av6ruN.png!web

如果是单机,可以用synchronized或者lock来处理,如果是分布式环境就需要使用分布式锁。

使用互斥锁,代码如下,仅适用redis2.6.1以后支持setnx的版本。 在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用redis的setnx操作去set一个mutex key。

当操作返回成功时,再进行load db的操作并回设缓存,否则,就重试整个get缓存的方法。

iAz6b2J.jpg!web

public String get(key) {
      List<String> resultList = (List<String>)redisTemplate.opsForValue().get(key);
    if(CollectionUtils.isEmpty(resultList)){
        final String mutexKey = key + "_lock";
        boolean isLock = (Boolean) redisTemplate.execute(new RedisCallback() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                //只在键key不存在的情况下,将键key的值设置为value,若键key已经存在,则 SETNX 命令不做任何动作
                //命令在设置成功时返回 1 , 设置失败时返回 0
                return connection.setNX(mutexKey.getBytes(),"1".getBytes());
            }
        });
        if(isLock){
            //设置成1秒过期
            redisTemplate.expire(mutexKey, 1000, TimeUnit.MILLISECONDS);
            resultList = getValueBySql(key);
            redisTemplate.opsForValue().set(key, resultList, 1000, TimeUnit.SECONDS);
            redisTemplate.delete(mutexKey);
        }else{
            //线程休息50毫秒后重试
            Thread.sleep(50);
            retryCount--;
            System.out.println("=====进行重试,当前次数:" + retryCount);
            if(retryCount == 0){
                System.out.println("====这里发邮件或者记录下获取不到数据的日志,并为key设置一个空置防止重复获取");
                List<String> list = Lists.newArrayList("no find");
                redisTemplate.opsForValue().set(key, list, 1000, TimeUnit.SECONDS);
                return list;
            }
            return getCacheSave2(key,retryCount);
        }
    }
    return resultList;
}

2. 设置随机过期时间

不同的key,设置不同的过期时间,让缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低。

3. 设置二级缓存

做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期

4. “永远不过期”

“永远不过期”包含两层意思:

  1. 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。

  2. 从功能上看,把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期。

ZJzA7vz.png!web

这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。

缓存预热

有效应对缓存的击穿和雪崩的一种方式是缓存预热。 缓存预热就是系统上线前,将相关的缓存数据直接加载到缓存系统。 这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。

解决思路

  1. 直接写个缓存刷新页面,上线时手工操作下。

  2. 数据量不大,可以在项目启动的时候自动进行加载。

  3. 定时刷新缓存。

限流

有效应对缓存的击穿和雪崩的另一种方式是限流。

在缓存失效后,通过队列来控制读数据库写缓存的线程数量。 比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

常见的限流算法

  1. 固定时间窗口算法(计数器)

  2. 滑动时间窗口算法

  3. 令牌桶算法

  4. 漏桶算法

有关限流算法的详细介绍,请 点击查看

总结

缓存穿透、击穿和雪崩是以预防为主、补救为辅,而在应对缓存的问题其实也没有一个完全完美的方案,只有最适合自己业务系统的方案。

更多内容,欢迎关注微信公众号:全菜工程师小辉~

FFz2Ef2.png!web

j2uIny6.gif

“阅读原文” 一起来充电吧!

喜欢就点个“在看”呗 ^_^


Recommend

  • 41

    把redis作为缓存使用已经是司空见惯,但是使用redis后也可能会碰到一系列的问题,尤其是数据量很大的时候,经典的几个问题如下:(一)缓存和数据库间数据一致性问题分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只...

  • 31
    • 微信 mp.weixin.qq.com 5 years ago
    • Cache

    缓存击穿/穿透/雪崩

    缓存击穿/穿透/雪崩 Intro 使用缓存需要了解几个缓存问题,缓存击穿、缓存穿透以及缓存雪崩,需要了解它们产生的原因以及怎么避免,尤其是当你打算设计自己的缓存框架的时候需要考虑如何处理这些问题。 ...

  • 19

    1.前言 当我们设计一个Redis缓存服务时,缓存穿透、缓存击穿、缓存雪崩这三大问题我们不得不考虑。同时参见面试时面试官也会常问这三大问题。本章我们来分析一下这三大问题。 2.缓存穿透 2.1什么叫缓存穿...

  • 20

    点击上方 “ 匠心零度 ” ,选择“

  • 8

    缓存穿透,击穿,雪崩以及解决方案,干货满满发布于 5 月 14 日缓存的设计包含很多技巧,设计不当将会导致严重的后果。redis作为一种非关系型数据库,也总是免不了...

  • 9

    Redis 缓存击穿(失效)、缓存穿透、缓存雪崩怎么解决?原始数据存储在 DB 中(如 MySQL、Hbase 等),但 DB 的读写性能低、延迟高。比如 MySQL 在 4 核 8G 上的 TPS = 5000,QPS =...

  • 5

    大家好,我是七淅(xī)。 作为后端开发,我想缓存是大家再熟悉不过的东西了。 本文会介绍出现缓存雪崩、穿透和击穿的业务背景、解决方案和对业务可靠性处理。事先说明,最佳解决方案一定需要结合实际业务调整,不同业务的处理...

  • 5

    缓存击穿、穿透、雪崩专项测试 作者:京东科技刘须华 2022-11-18 14:34:28 R2M缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。而缓存最常见的问题是缓存穿透、击穿和雪崩,在高并发下这三种...

  • 6

    【专项测试系列】-缓存击穿、穿透、雪崩专项测试 推荐 原创 京东云官方 2022-11-1...

  • 10

    缓存击穿、穿透、雪崩及解决方案 Redis是一种高性能的键值型数据库,它可以用来实现缓存功能,提高应用的响应速度和承载能力。但是,使用Redis缓存也会遇到一些常见的问题,比如缓存击穿、缓存穿透、缓存雪崩。这些问题都...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK