精华内容
下载资源
问答
  • @Cacheable使用两个或多个参数作为缓存的key 常见的如分页查询:使用单引号指定分割符,最终会拼接为一个字符串 @Cacheable(key = "#page+'-'+#pageSize") public List<User> findAllUsers(int page,int ...

    @Cacheable使用两个或多个参数作为缓存的key

    常见的如分页查询:使用单引号指定分割符,最终会拼接为一个字符串

    @Cacheable(key = "#page+'-'+#pageSize")
    public List<User> findAllUsers(int page,int pageSize) {
        int pageStart = (page-1)*pageSize;
        return userMapper.findAllUsers(pageStart,pageSize);
    }
    

    当然还可以使用单引号自定义字符串作为缓存的key值

    @Cacheable(key = "'countUsers'")
    public int countUsers() {
        return userMapper.countUsers();
    }
    

    在redis中效果值如图

    1233356-55ab765df9f4b007.png

    Kotlin 开发者社区

    1233356-4cc10b922a41aa80

    国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。

    越是喧嚣的世界,越需要宁静的思考。

    展开全文
  • 》,今天给大家整理一篇关于Redis经常被问到的问题:缓存雪崩、缓存穿透、缓存预热、缓存更新缓存降级等概念的入门及简单解决方案。 一、缓存雪崩 缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未...

    前面一节说到了《为什么说Redis是单线程的以及Redis为什么这么快!》,今天给大家整理一篇关于Redis经常被问到的问题:缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等概念的入门及简单解决方案。

    一、缓存雪崩

    缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

    缓存正常从Redis中获取,示意图如下:

    这里写图片描述

    缓存失效瞬间示意图如下:

    这里写图片描述

    缓存失效时的雪崩效应对底层系统的冲击非常可怕!大多数系统设计者考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

    以下简单介绍两种实现方式的伪代码:

    (1)碰到这种情况,一般并发量不是特别多的时候,使用最多的解决方案是加锁排队,伪代码如下:

    //伪代码
    public object GetProductListNew() {
        int cacheTime = 30;
        String cacheKey = "product_list";
        String lockKey = cacheKey;
    
        String cacheValue = CacheHelper.get(cacheKey);
        if (cacheValue != null) {
            return cacheValue;
        } else {
            synchronized(lockKey) {
                cacheValue = CacheHelper.get(cacheKey);
                if (cacheValue != null) {
                    return cacheValue;
                } else {
    	            //这里一般是sql查询数据
                    cacheValue = GetProductListFromDB(); 
                    CacheHelper.Add(cacheKey, cacheValue, cacheTime);
                }
            }
            return cacheValue;
        }
    }
    

    加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。同样会导致用户等待超时,这是个治标不治本的方法!

    注意:加锁排队的解决方式分布式环境的并发问题,有可能还要解决分布式锁的问题;线程还会被阻塞,用户体验很差!因此,在真正的高并发场景下很少使用!

    (2)还有一个解决办法解决方案是:给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存,实例伪代码如下:

    //伪代码
    public object GetProductListNew() {
        int cacheTime = 30;
        String cacheKey = "product_list";
        //缓存标记
        String cacheSign = cacheKey + "_sign";
    
        String sign = CacheHelper.Get(cacheSign);
        //获取缓存值
        String cacheValue = CacheHelper.Get(cacheKey);
        if (sign != null) {
            return cacheValue; //未过期,直接返回
        } else {
            CacheHelper.Add(cacheSign, "1", cacheTime);
            ThreadPool.QueueUserWorkItem((arg) -> {
    			//这里一般是 sql查询数据
                cacheValue = GetProductListFromDB(); 
    	        //日期设缓存时间的2倍,用于脏读
    	        CacheHelper.Add(cacheKey, cacheValue, cacheTime * 2);                 
            });
            return cacheValue;
        }
    } 
    

    解释说明:

    1、缓存标记:记录缓存数据是否过期,如果过期会触发通知另外的线程在后台去更新实际key的缓存;

    2、缓存数据:它的过期时间比缓存标记的时间延长1倍,例:标记缓存时间30分钟,数据缓存设置为60分钟。 这样,当缓存标记key过期后,实际缓存还能把旧数据返回给调用端,直到另外的线程在后台更新完成后,才会返回新缓存。

    关于缓存崩溃的解决方法,这里提出了三种方案:使用锁或队列、设置过期标志更新缓存、为key设置不同的缓存失效时间,还有一各被称为“二级缓存”的解决方法,有兴趣的读者可以自行研究。

    二、缓存穿透

    缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。

    有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

    另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴!

    //伪代码
    public object GetProductListNew() {
        int cacheTime = 30;
        String cacheKey = "product_list";
    
        String cacheValue = CacheHelper.Get(cacheKey);
        if (cacheValue != null) {
            return cacheValue;
        }
    
        cacheValue = CacheHelper.Get(cacheKey);
        if (cacheValue != null) {
            return cacheValue;
        } else {
            //数据库查询不到,为空
            cacheValue = GetProductListFromDB();
            if (cacheValue == null) {
                //如果发现为空,设置个默认值,也缓存起来
                cacheValue = string.Empty;
            }
            CacheHelper.Add(cacheKey, cacheValue, cacheTime);
            return cacheValue;
        }
    }
    

    把空结果,也给缓存起来,这样下次同样的请求就可以直接返回空了,即可以避免当查询的值为空时引起的缓存穿透。同时也可以单独设置个缓存区域存储空值,对要查询的key进行预先校验,然后再放行给后面的正常缓存处理逻辑。

    三、缓存预热

    缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

    解决思路:

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

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

    3、定时刷新缓存;

    四、缓存更新

    除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种:

    (1)定时去清理过期的缓存;

    (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

    两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。

    五、缓存降级

    当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

    降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

    在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

    (1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;

    (2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;

    (3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;

    (4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

    六、总结

    这些都是实际项目中,可能碰到的一些问题,也是面试的时候经常会被问到的知识点,实际上还有很多很多各种各样的问题,文中的解决方案,也不可能满足所有的场景,相对来说只是对该问题的入门解决方法。一般正式的业务场景往往要复杂的多,应用场景不同,方法和解决方案也不同,由于上述方案,考虑的问题并不是很全面,因此并不适用于正式的项目开发,但是可以作为概念理解入门,具体解决方案要根据实际情况来确定!


    参考文章:

    1、http://www.cnblogs.com/zhangweizhong/p/6258797.html
    2、http://www.cnblogs.com/zhangweizhong/p/5884761.html
    3、http://blog.csdn.net/zeb_perfect/article/details/54135506

    在这里插入图片描述

    【视频福利】2T免费学习视频,搜索或扫描上述二维码关注微信公众号:Java后端技术(ID: JavaITWork)回复:1024,即可免费获取!内含SSM、Spring全家桶、微服务、MySQL、MyCat、集群、分布式、中间件、Linux、网络、多线程,Jenkins、Nexus、Docker、ELK等等免费学习视频,持续更新!

    展开全文
  • 在使用AE制作特效的时候,准备预览却弹出了:After Effects错误:缓存预览需要2个或多个帧才能播放,该怎么办? 解决步骤: 1、在ae工具栏中点击“编辑”——>“清理”,分别清理“所有内存与磁盘缓存...”和...

    在使用AE制作特效的时候,准备预览却弹出了:After Effects错误:缓存预览需要2个或多个帧才能播放,该怎么办?

    解决步骤:

    1、在ae工具栏中点击“编辑”——>“清理”,分别清理“所有内存与磁盘缓存...”和“所有内存”;

    2、点击“编辑”——>“首选项”——>“内存和多重处理...”;

    3、勾选内存选项“系统内存不足时减少缓存大小”右侧的复选框,点击确定;

    4、打开合成设置,将持续时间调长一点,点击确定即可。

    展开全文
  • 缓存更新机制

    千次阅读 2018-07-16 10:22:46
    试想,两并发操作,一更新操作,另一是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的数据还是老的数据,导致缓存中的...
  • @Cacheable使用两个或多个参数作为缓存的key 常见的如分页查询:使用单引号指定分割符,最终会拼接为一个字符串 @Cacheable(key = "#page+'-'+#pageSize") public List<User> findAllUsers(int page,int ...
  • 到底是先更新数据库还是先更新缓存

    千次阅读 热门讨论 2021-03-19 00:51:19
    如何保证数据库和缓存数据的一致性?
  • 这是一在WPF下的绘图功能代码,能过写位图,实现顶级的绘图性能,线程架构,项目代码完整,可直接运行。可以修改适用于自定义控件。
  • 我们经常通过命中率来衡量缓存机制的好坏和效率,这命中率指的就是请求缓存次数和缓存返回正确结果的次数的一比例,这比例越高,就表明缓存的使用率越高。 正常的缓存命中率也会因为不同的缓存应用而大不相同...
  • 然后再对这个简单生产者消费者问题加大难度。将消费者改成2个,缓冲池改成拥有4个缓冲区的大缓冲... 信号量Semaphore》中的信号量,不难得出用二个信号量就可以解决这种缓冲池有多个缓冲区的情况——用一个信号量A来记
  • 用mybatis的基本上都知道mybatis有两级别的缓存,分别是一级缓存和二级缓存。...当我们了解一级缓存的用途后,我们就会想,我们对数据库的操作又不是仅限于读,很时候我们都要更新数据库的信息...
  • 系统中有多个生产者进程和多个消费者进程,共享一个能存放1000件产品的环形缓冲区(初始为空)。当缓冲区未满时,生产者进程可以放入其生产的一件产品,否则等待;当缓冲区未空时,消费者进程可以从缓冲区取走一件...
  • 如题,buffer是NIO中比较重要的概念,实际程序运行过程中,是在每个线程中创建buffer,还是创建一份buffer,多个线程共享? 如果是每个线程创建自己的buffer,可以理解;如果是多个线程共享一个buffer,那么如何...
  • 缓存更新策略

    千次阅读 2017-03-06 21:03:00
    缓存更新策略 转自:https://www.zhihu.com/question/27738066 更新缓存的的Design Pattern有四种:Cache aside, Read through, Write through, Write behind caching,我们下面一一来看一下这四种Pattern。 ...
  • 搞懂分布式技术15:缓存更新的套路

    千次阅读 2018-06-23 18:02:51
    试想,两并发操作,一更新操作,另一是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的数据还是老的数据,导致缓存中的...
  • 第三节 redis缓存更新的套路

    万次阅读 2018-05-03 17:09:55
    转自 酷壳-陈皓-缓存更新的...试想,两并发操作,一更新操作,另一是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的...
  • 缓存更新的模式

    千次阅读 2019-05-20 20:18:47
    标题缓存更新的设计 1错误案例 场景:更新缓存数据时,先删除cache,然后再更新db 1 a,b线程,a线程删除cache,更新db前,b线程先读数cache,发现chche没有数据,从db读取数,并加载到cache中,执行后a线程...
  • 缓存更新的机制

    千次阅读 2018-02-08 10:40:09
    缓存更新的模式有四种:Cache aside, Read through, Write through, Write behind caching。下面找到了一篇非常好...试想,两并发操作,一更新操作,另一是查询操作,更新操作删除缓存后,查询操作没有命中...
  • redis 缓存更新机制

    千次阅读 2020-04-05 14:11:53
    先做一说明,从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。这种方案下,我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库...
  • 前言当我们在做数据库与缓存数据同步时,究竟更新缓存,还是删除缓存,究竟是先操作数据库,还是先操作缓存?本文带大家深度分析数据库与缓存的双写问题,并且给出了所有方案的实现代码方便大家参考。...
  •     先说一前端开发中会遇到的问题,我们更新已...浏览器缓存是前端优化的一重要问题,缓存可以带来很好处: (1)减少冗余的数据传输,节省带宽; (2)减轻服务器的请求负担,有缓存...
  • 当系统达到一定流量时,一般都会通过增加...当缓存,并且如果业务创建缓存的地方分散在各个功能,各个文件里时,很容易导致有些缓存忘记更新,从而导致一些业务上的错误,而这些错误很难通过测试来发现的。 在实...
  • 更新缓存一致性的问题

    千次阅读 2018-12-04 23:06:11
    看到好些人在写更新缓存数据代码时,先删除缓存,然后再更新数据库,而后续的操作会把数据再装载的缓存中。然而,这个是逻辑是错误的。试想,两个并发操作,一个是更新操作,...所以,这篇文章介绍了几个缓存更新的D...
  • 编辑新建了一篇新闻,在数据库news表中存储新闻信息,但是客户端查询新闻到缓存的时候会关联多个表,有评论信息、浏览量、点赞、点击量等等,如果在数据添加新闻后,然后为了更新缓存查出这么多信息再插入,岂
  • 原因很简单,很多时候,在复杂点的缓存场景,缓存不单单是数据库...如果你频繁修改一个缓存涉及的多个表,缓存也频繁更新。但是问题在于,这个缓存到底会不会被频繁访问到? 举个栗子,一个缓存涉及的表的字段,在 1 分
  • 【Redis】缓存更新的套路

    千次阅读 2018-09-06 10:20:58
    试想,两并发操作,一更新操作,另一是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的数据还是老的数据,导致缓存中的...
  • 这样设置删除或已读标记的时候,是在另一表的 Mapper.xml 中操作的,也就不会更新消息表的缓存。 有人试图在ABMapper.xml 中采用cache-ref才解决这问题,不好使。不知道是不是我配得不对。 ABMapper.xm...
  • 缓存缓存更新策略

    千次阅读 2019-06-15 15:43:28
    缓存用于缓解后端db的压力,策略指的是更新缓存以及db的方式。 主要可以分为两大类: 调用方主动更新缓存以及db: 这种是最最常见也是最最容易想到的方式。即调用端需要同时维护db和缓存的调用,调用端逻辑比较...
  • 缓冲区与缓存(buffer与cache)

    千次阅读 2019-05-30 11:33:35
    文章目录缓冲区与缓存(buffer与cache)1.缓冲区buffer缓存区的作用Python中的缓冲缓存区的类型2.缓存cache缓存的适用场景缓存的三种模式Python中的缓存 缓冲区与缓存(buffer与cache) 1.缓冲区buffer 缓冲区(buffer),...
  • 说明:在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,204,142
精华内容 881,656
关键字:

更新多个缓存