精华内容
下载资源
问答
  • 常见缓存策略

    2020-04-01 14:51:29
    一. 为什么要使用缓存? 二. 什么样的数据适合缓存? 三. 常见缓存策略有哪些? 四. 缓存的主要问题和解决办法有哪些?

    一. 为什么要使用缓存?

    如果说要对一个站点或者应用程序进行优化,那么我们最先想到的优化方式应该是使用缓存,使用缓存是最快也是效果最明显的方式。
    优点:
    1.减轻数据库压力(核心)
    2.提高用户体验
    3.增强系统的并发能力
    缺点:
    1.数据不一致(延迟更新)
    2.使用不当容易引发其他问题

    二. 什么样的数据适合缓存?

    1.数据量不是很大
    2.访问频率高
    3.数据不经常更新

    三. 常见的缓存策略有哪些?

    1.Cache-Aside

    一种比较简单实用的方式,可能是最常用的策略。
    在这里插入图片描述
    1.首先,应用程序先确定数据是否保留在缓存中;
    2.如果数据在缓存中,也即 Cache hit (缓存命中)。数据直接从缓存中读取并返回给客户端应用程序;
    3.如果数据不在缓存中,也即 Cache miss(缓存未命中)。应用程序会从数据库中读取该数据,并将数据存储在缓存中,然后将其返回给客户端。
    4.如果需要要更新某个数据,也是先去更新数据库中的数据,更新完成之后,则通过指令让缓存Cache中的数据失效。
    该策略特别适合“读多”的应用场景。可以在一定程度上抵抗缓存故障。如果缓存服务发生故障,系统仍然可以通过直接访问数据库进行操作。
    这种策略并不能保证数据存储和缓存之间的一致性,也会有可能产生脏数据,但是概率很小,属于极端情况。(假如同时有2个请求A和请求B并发执行。A是首次访问缓存中没有数据,就会去数据库中读数据,读到了数据准备写入缓存中,在A把数据写入缓存之前,B更新了数据库,并且设置了缓存失效。然后A才开始把数据写回缓存中,那么最终就会导致,缓存中的数据与数据库的数据不一致,造成了脏数据)通常都是要给缓存加上一个过期的时间,如果必须考虑删除缓存失败的问题,可以使用消息队列实现重试机制(从消息队列取出这些key再次进行删除,失败再次加入到消息队列中,超过一定次数以上则人工介入)–越搞越复杂。
    因为首次请求数据需要先把数据加载到缓存中,开发人员可以通过手动触发查询操作来对数据进行“预热”。

    2.Read/Write-Through

    这个模式其实就是由缓存服务完成数据同步的工作。
    1.应用要读数据和更新数据都直接访问缓存服务
    2.缓存服务同步的将数据更新到数据库
    这个模式出现脏数据的概率就比较低,但是就强依赖缓存了,对缓存服务的稳定性有较大要求,另外,增加新缓存节点时还会有初始状态空数据问题。

    3.Write-Behind

    这个模式就是 Read/Write Through 模式 的一个变种。区别就是 Read/Write Through 模式的缓存写数据库的时候是同步的,而 Write Behind 模式 的缓存操作数据库是异步的。
    1.应用要读数据和更新数据都直接访问缓存服务
    2.缓存服务异步的将数据更新到数据库(通过异步任务)
    这个模式的特点就是速度很快,效率会非常高,但是数据的一致性比较差,还可能会有数据的丢失情况,实现逻辑也较为复杂。

    以上就是目前三种主流的缓存更新策略,另外还有Refrsh-Ahead模式等由于使用的不是很常见就不详细介绍了。

    参考链接:https://www.jianshu.com/p/22c7e9ab5d15

    四. 缓存的主要问题和解决办法有哪些?

    1.缓存穿透

    描述:
    缓存穿透是指用户请求的数据在缓存和数据库中都不存在。例如恶意攻击者发起id为“-1”的数据或id为特别大不存在的数据,如果不加以校验,请求会透过缓存直接去访问数据库,导致数据库压力过大。

    解决办法:
    1.id做基础校验
    2.缓存和数据库都查不到数据时,缓存内容为null或者其他默认值,并设置较短的过期时间(根据项目情况合理设置,时间不宜过长)。
    3.限流

    2.缓存击穿

    描述:
    缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

    解决办法:
    1.缓存预热。
    2.使用互斥锁(mutex key)
    3.让热点数据“永不过期”(配合其他策略在项目容错允许的范围内保证数据一致,也可通过一个后台的异步线程进行缓存的构建)
    4.限流+降级

    3.缓存雪崩

    描述:
    缓存雪崩是指缓存中的大批量key同一时段过期,而该时段内查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发同一个key。

    解决办法:
    1.缓存预热。
    2.将缓存失效时间分散开(随机时长+基础时长)。
    3.让热点数据“永不过期”(配合其他策略在项目容错允许的范围内保证数据一致,也可通过一个后台的异步线程进行缓存的构建)
    4.限流+降级

    参考文档地址:
    链接: https://blog.csdn.net/kongtiao5/article/details/82771694.

    展开全文
  • 常见缓存算法和缓存策略

    千次阅读 2013-01-10 11:40:28
    缓存策略缓存策略主要三方面: 缓存什么内容何时进行缓存当缓存空间已满时如何进行替换,即缓存替换算法。 对于第二方面,大部分缓存算法使用预取策略来提前将部分磁盘数据放入缓存,以进一

    缓存算法:缓存法通过设计良好的数据分块、预取、顺序预取、缓存替换等算法来提高对缓存内容的命中率。缓存算法可以分为基于访问时间的策略、基于访问频率的策略、访问时间与频率兼顾策略、时间距离分布策略等类型。

    缓存策略:缓存策略主要三方面:

    • 缓存什么内容
    • 何时进行缓存
    • 当缓存空间已满时如何进行替换,即缓存替换算法。

    对于第二方面,大部分缓存算法使用预取策略来提前将部分磁盘数据放入缓存,以进一步减少磁盘I/O,加大缓存命中率。通过记录、分析以往的数据请求模式来预测将来可能被请求到的数据段,将访问可能性大的数据段放入缓存。

    缓存策略的数据块分割:

    首部缓存和分块缓存策略普遍应用于VoD影片文件。首部缓存将影片文件开始的一部分放入缓存以减小点播用户的启动延迟,对于影片文件其他部分的访问需要直接读取磁盘。

    分块缓存通过将影片文件切分成小块,以块为单位进行缓存操作。分块缓存分为定长分块与变长分块。定长分块将文件切分为大小相同的块,变长分块,变长算法是基于影片文件越靠后的部分被访问的概率越低的推断,将文件按照首尾位置分块,各块大小按指数递增。

    但以上定长与变长分块均忽略了两点:

    • 影片文件会存在一些“热点片段”而这些热点片段并不均处于影片首部
    • 同一影片内“热点片段”的热度会随着时间不断改变,不同影片的热度也随时间不断变化。

    需设计良好的算法自适应影片热点的不同位置与变化。

    缓存策略的分类:

    由于不同系统的数据访问模式不尽相同,同一种缓存策难以在各种数据访问模式下均取得满意性能,研究人员提出不同缓存策略以适应不同需求。

    缓存策略可分为以下几类:

    • 基于访问时间:此类算法按各缓存项的被访问时间来组织缓存队列,决定替换对象。如LRU。
    • 基于访问频率:此类算法用缓存项的被访问频率来组织缓存。如LFU、LRU-2、2Q、LIRS。
    • 访问时间与频率兼顾:通过兼顾访问时间与频率,使得在数据访问模式变化时缓存策略仍有较好性能。如FBR、LRFU、ALRFU。多数此类算法具有一个可调或自适应参数,通过该参数的调节使缓存策略在基于访问时间与频率间取得一定平衡。
    • 基于访问模式:某些应用有较明确的的数据访问特点,进而产生与其相适应的缓存策略。如专为VoD系统设计的A&L缓存策略,同时适应随机、顺序两种访问模式的SARC策略。
    • 基于访问时间的缓存策略:LRU (LeastRecentlyUsed)是一种应用广泛的缓存算法。该算法维护一个缓存项队列,队列中的缓存项按每项的最后被访问时间排序。当缓存空间已满时,将处于队尾,即删除最后一次被访问时间距现在最久的项,将新的区段放入队列首部。但LRU算法仅维护了缓存块的访问时间信息,没有考虑被访问频率等因素,在某些访问模式下无法获得理想命中率。例如对于VoD系统,在没有VCR操作的情况下,数据被由前至后顺序访问,已访问过的数据不会被再次访问。所以LRU算法将最新被访问的数据最后被替换不适用于VoD系统。
    • 基于访问频率的缓存策略:LFU (LeastFrequentlyUsed)按每个缓存块的被访问频率将缓存中的各块排序,当缓存空间已满时,替换掉缓存队列中访问频率最低的一项。与LRU的缺点类似, LFU仅维护各项的被访问频率信息,对于某缓存项,如果该项在过去有着极高的访问频率而最近访问频率较低,当缓存空间已满时该项很难被从缓存中替换出来,进而导致命中率下降。
      LRU-2[2, 3]算法记录下每个缓存页面最后两次被访问的时间。替换页面时替换掉倒数第二次访问时间距现在最久的一项。在IRM (IndependentReferenceModel)访问模式下,LRU-2有着最好的预期命中率,由于LRU-2算法要维护一个优先级队列,所以该算法复杂度为logN(N为缓存队列中缓存项的数量)。
      2Q[4](2 Queues)使用LRU队列替换了LRU-2中的优先级队列,将算法时间复杂度由logN降为常数,且维护缓存项的各信息所需空间比LRU-2小。
      LIRS[5](Low Inter-ReferenceRecency Set)维护一个变长的LRU队列,该队列的LRU端为最近至少被访问过2次的第Llirs项(Llirs为算法参数)。LIRS算法在IRM访问模式下可以获得很高的命中率,但对于SDD访问模式效率明显下降。
      对于VoD系统,基于访问频率的策略可以捕捉到热点影片片段,使得对热点片段的大量请求免于进行缓慢的磁盘I/O。但影片热点会随时间不断变化,且此类策略无法利用VoD的顺序访问模式,所以纯粹以访问频率为度量来进行替换操作不适合VoD系统。
    • 兼顾访问时间与频率:FBR[6](Frequency Based Replacement)维护一个LRU队列,并将该队列分为New、Middle、Old三段。对队列中的每一缓存项均维护一个计数值。当缓存中的一项被命中时,被命中的缓存项被移至New段的MRU端,如果该项原本位于Old或Middle段,则其计数值加1,原位于New段则计数值不变。当进行替换操作时,删除Old段计数值最小的一项(LRU端)。
      LRFU[7](LeastRecently FrequentlyUsed)为每一个缓存项维护一个权值C(x),其初始值为0, C(x)按以下公式变化。
      在t时刻, C(x) =1+2-λC(x): x被访问到2-λC(x) : x未被访问进行替换操作时,C(x)值最小的一项被删除。当时, LRFU算法行为类似于LFU;而当时,该算法行为逼近LRU算法。该算法通过选用合适的λ值来获得时间与频率因素的平衡。
      LRFU虽然通过一个值来兼顾访问时间与频率因素,但由于值固定,当访问模式变化时,该算法无法做出相应的调整而造成性能下降。ALRFU[8](Adaptive LRFU)在此方面对LRFU进行了改进。通过对数据访问模式的历史进行监控,ALRFU动态调整值来适应数据访问模式的改变,表现出比LRFU更好的适应性。
    • 基于访问模式的缓存策略:针对VoD系统的特点提出A&L算法。该算法通过记录每个缓存项的历史访问时间与访问数量来估计该项的未来被访问频率。以该频率值为度量,在进行缓存替换时替换掉该值最小的一项。由于该算法考虑了VoD系统的数据访问特点,所以广泛应用于VoD系统。
      但A&L算法通过直接计算缓存区段生成以来的总的访问数量、频率来生成缓存权值,没有考虑VoD影片的访问热点会随时间不断变化。当某些缓存区段历史访问频率较高但最近访问频率下降时,仍保持较大权值,影响新的热点片段的缓存,无法适应影片热点的动态变化。
      IBM提出的SARC[10]是针对于大型服务器的缓存算法,可在随机访问与顺序访问的数据访问模式下做出动态适应。SARC通过将随机访问与顺序访问分为两个队列分别管理来实现对两种不同访问模式的适应。并通过分析缓存大小-命中率的仿真试验数据曲线,得出对随机队列与顺序队列项分别进行替换的代价函数。当进行缓存替换时,通过两队列的代价函数来对代价小的队列进行替换。
    展开全文
  • PWA常见缓存策略

    千次阅读 2018-05-29 03:42:03
    下面介绍常见用的几种缓存策略: cache-first Cache-First策略会在有缓存的时候返回缓存,没有缓存才会去请求并且把请求结果缓存。也就是说,第一次页面加载跟普通页面加载没有任何区别的,第二次访问的资源是...

    技术背景

    大家都知道H5相比原生应用,无论是加载速度和用户体验都会差很多,具体原因有如下几点:

    1. 留白时间过长。移动端网络非常不稳定,经常会出现弱网环境(如:电梯,地铁,大山里面),这样会导致资源加载速度非常慢,留白时间相对原生会慢很多。
    2. 无法离线访问。因为资源都在线上服务器,每次访问H5的页面强烈依赖网络,原生因为资源都在应用包里面,就算断网也会给一个相对友好的展示界面和用户提醒。
    3. 无法全屏访问。H5绝大部分都是跟浏览打交道,但是各大浏览器厂商都会有一个讨厌的头部和一个讨厌的尾部,导致用户的可视区域大大被压缩。原生大家都知道可视区域随意控制。
    4. 无法消息推送。
    5. 没有自己的启动图标,每次都需要输入网址或者依靠搜索引擎引流。

    这个时候拯救H5的英雄就出现了,他就是PWA。

    什么是PWA

    PWA全称是: Progressive Web Apps(渐进式网页应用)。这是 2016 年,Google I/O 大会上提出一个下一代web应用的概念。这并不是描述一个技术,而是一些技术的合集,能让你在使用 Web的时候感觉像是在使用 APP。

    PWA能干什么

    • 消息推送
    • 主屏ICON,全屏访问
    • 离线存储

    是不是跟原生就很像了?本文要写的主要是离线存储也就是缓存这一块的内容,消息推送和主屏幕ICON以后再讲。

    pwa缓存的原理

    为什么能实现缓存或者叫离线存储?核心就是利用浏览器service-worker另启一个线程,这个线程负责去监听所有https请求(注意是https),当发现某些资源是需要缓存下来的他会把资源拉取到浏览器本地,访问的时候拦截请求,不走网络请求,直接读取本地资源。这样资源相当于都是用户本地的资源,响应速度肯定飞快,还有就是资源都在用户浏览器里面,就算断了网,资源也都是能正常访问。

    pwa缓存到底有多快

    下面是现有PLUS会员业务中的同一个页面pwa缓存和非pwa缓存加载资源的对比图:

    下图是3G网络不用PWA缓存的资源请求时间:

    下图是3G网络采用PWA缓存的资源请求时间:

    下图是4G网络不用PWA缓存的资源请求时间:
    下图是4G网络采用PWA缓存的资源请求时间:

    采用PWA缓存和不采用PWA缓存数据对比:

    3G不用PWA缓存 3G采用PWA缓存 4G不采用PWA缓存 4G采用PWA缓存
    页面加载时间 4.16s 989ms 1.8s 975ms
    单个资源
    平均加载时间
    1s左右 60ms左右 550ms左右 60ms左右

    从上面数据可以看出来,pwa缓存的提速效果是非常明显,能保证你的页面在弱网环境下秒开,资源大部分都是在50毫秒左右的的响应时间。我们在safari浏览器测试的响应时间更加快速,基本都是在15ms左右!!!

    pwa缓存的缓存策略

    上面说到某些资源是需要缓存。缓存多长时间?是永远走缓存还是永远走网络?还是一些特定的缓存策略的?下面介绍常见用的几种缓存策略:

    cache-first

    Cache-First策略会在有缓存的时候返回缓存,没有缓存才会去请求并且把请求结果缓存。也就是说,第一次页面加载跟普通页面加载没有任何区别的,第二次访问的资源是直接走了本地缓存数据的。

    无缓存加载流程图

    有缓存加载流程图

    这种策略适用于:css,js,背景图片,这种实时变化频率比较低的静态资源比较适合!这种策略有一种不好的地方就是,缓存可能一只存在你得浏览器,如果发现某些文件需要替换,这个时候就依赖发版要不缓存就一直存在。有什么好的办法吗?配置缓存时间可以避免这种问题,定一个时间更新一次缓存。比如一个小时,或者三个小时,也可以通过缓存某些变动频率比较低接口的数据,这个时间主要看自己的业务需求了。(PS:新的版本改成Service-Worker一天会主动拉新一次。)

    network-first

    network-first 是一个比较复杂的策略。资源优先走网络,成功以后会把资源添加到缓存里面,当发现网失败就会回退读取缓存。这里面有一个点就是,多长时间算网络请求失败?这时候就需要配置一个超时时间,如果不配置回退缓存的时间就会比较长。这个时间根据自身项目而定。

    这种策略适用于:频繁更新的资源,比如天气的数据,文章,游戏排行榜的接口资源,正常情况下跟普通网页没有任何区别,当出现弱网或者断网资源响应时间比较长用户体验比价差的情况下给的一种资源回退策略,这种方式可以提高弱网环境下的用户体验。

    stale-while-revalidate

    这种策略比较接近cache-first,他们的区别在于他会先走缓存,走完缓存以后它会发出请求,请求的结果会用来更新缓存,也就是说你的下一次访问的如果时间足够请求返回的话,你就能拿到最新的数据了。

    适合于:频繁更新最新版本并非必需的资源,html,头像。

    cache-only

    只会去缓存里拿数据,缓存没有就失败了。

    这种非常简单应用场景可能就是一万年不变的静态页面可能比较适合。

    network-only

    network-only 只请求线上,不读写缓存。

    这种策略的应用场景非常少,特殊情况下偶尔能用用吧,当发现线上的缓存失控,在这种紧急情况下全部不走缓存,全部走网络一种紧急应对情况吧。

    以上就是常用的五中缓存策略,不同的场景对应不同的缓存策略,如何去编写这些策略或者如何去生成这些策略会在接下来的文章再去详解

    pwa的未来

    现在国内的浏览器厂商最新的版本都支持PWA的service-worker 缓存这一块,以前吐槽的IOS不会支持,但是在最新版本的IOS11.3也支持service-worker了。

    pwa是一堆技术的合集,app shell,消息推送,单页式的应用都有包含,缓存只是其中的一部分,还有很多技术需要我们去探索去实践,相信这项技术会给H5带来更加好的未来。

    展开全文
  • 作者:opLW ...注意:郭霖大神的文章是Glide3.7版本,最新的Glide可能与文章内容会有不同。 目录 1.Glide的缓存策略 2.常见的与Glide缓存策略相关的问题 1.Glide的缓存策略 内存缓存 内存缓存主要分为两个...

    作者:opLW
    参考:郭神的Glide系列文章Android图片加载框架最全解析(三),深入探究Glide的缓存机制
    注意:郭神的文章是Glide3.7版本,最新的Glide可能与文章内容会有不同。

    目录

    1.Glide的缓存策略
    2.常见的与Glide缓存策略相关的问题

    1.Glide的缓存策略
    • 内存缓存
      • 内存缓存主要分为两个方面:弱引用缓存和 LruCache缓存。
      • 下面的代码是Glide4.8的,不同点:4.8的是先查看弱引用缓存中有没有,没有就再查看LruCache缓存,而3.7则刚好相反。
      public <R> LoadStatus load(//省略大量参数...) {
        //省略部分代码
        EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
        if (active != null) {
          cb.onResourceReady(active, DataSource.MEMORY_CACHE);
          if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Loaded resource from active resources", startTime, key);
          }
          return null;
        }
      
        EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
        if (cached != null) {
          cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
          if (VERBOSE_IS_LOGGABLE) {
            logWithTimeAndKey("Loaded resource from cache", startTime, key);
          }
          return null;
        }
        //省略部分代码
      }
      
      • 从弱引用缓存,LruCache缓存获取目标资源
       //Engine.java
       private EngineResource<?> loadFromActiveResources(Key key, 
       boolean isMemoryCacheable) {
       	if (!isMemoryCacheable) {
        		 return null;
       	}
       	EngineResource<?> active = activeResources.get(key); // 1
       	if (active != null) {
        		 active.acquire();
      	    }
      	    return active;  
       }
       
       private final ActiveResources activeResources; // 2
       
       //ActiveResources.java
       EngineResource<?> get(Key key) {
       ResourceWeakReference activeRef = activeEngineResources.get(key); // 3
       //省略部分代码
       }
       
       final Map<Key, ResourceWeakReference> activeEngineResources // 4
       = new HashMap<>();
      
       static final class ResourceWeakReference 
       extends WeakReference<EngineResource<?>> { //省略部分代码 }
       
       //Engine.java
       private EngineResource<?> loadFromCache(Key key, 
       boolean isMemoryCacheable) {
       	if (!isMemoryCacheable) {
        	    return null; // 设置不要内存缓存发挥作用的地方
      	    }
      
       	EngineResource<?> cached = getEngineResourceFromCache(key);
       	if (cached != null) {
               cached.acquire();
               activeResources.activate(key, cached); // 5
       	}
           return cached;
       }
       
       private EngineResource<?> getEngineResourceFromCache(Key key) {
       	Resource<?> cached = cache.remove(key); // 6
       	//省略部分代码
       	return result;
       }
       private final MemoryCache cache; //LruCache原理
      

      注意 上述的代码摘抄自多份文件。
      弱引用缓存 1,2,3,4序号可以清晰的看出弱引用缓存是从一个Map中获取,而这个Map存放着弱引用对象,即存放当前正在使用的目标资源。
      LruCache缓存 可以查看郭神的另外一篇文章:LruCache6从LruCache缓存中取得并移除目标资源。5 可知当调用Lrucache缓存获取到目标资源时,会将资源存放到弱引用缓存中。

      • 向弱引用缓存,LruCache缓存添加目标资源
       //Engine.java
       @Synthetic
      void handleResultOnMainThread() {
        //省略部分代码
        engineResource.acquire(); // 7
        listener.onEngineJobComplete(this, key, engineResource)//省略部分代码
        engineResource.release(); // 7
      }
      
      @Override
      public void onEngineJobComplete(EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
        Util.assertMainThread();
        // A null resource indicates that the load failed, usually due to an exception.
        if (resource != null) {
          resource.setResourceListener(key, this);
      
          if (resource.isCacheable()) {
            activeResources.activate(key, resource); // 8
          }
        }
      
        jobs.removeIfCurrent(key, engineJob);
      }
      
      private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
        if (!isMemoryCacheable) {
          return null;
        }
      
        EngineResource<?> cached = getEngineResourceFromCache(key);
        if (cached != null) {
          cached.acquire();
          activeResources.activate(key, cached); // 9
        }
        return cached;
      }
      
      //ActiveResources.java
      void activate(Key key, EngineResource<?> resource) {
        ResourceWeakReference toPut =
            new ResourceWeakReference(
                key,
                resource,
                getReferenceQueue(),
                isActiveResourceRetentionAllowed);
      
        ResourceWeakReference removed = activeEngineResources.put(key, toPut);  // 10
        if (removed != null) {
          removed.reset();
        }
      }
      

      向弱引用缓存添加内容 8,9调用了弱引用缓存的active方法,而10active方法最终是向弱引用缓存Map添加弱引用对象。
      那么何时调用activite方法呢?handleResultOnMainThread,该方法是在异步加载完图片之后通知主线程更新结果,那么这个时候就会调用onEngineJobComplete,进而调用activite方法。②loadFromCache,该方法是在从LruCache获取内容之后调用的。这两个方法都将刚用到的图片添加到弱引用缓存。
      细心的朋友可能发现了上面还有个7序号,下面我们将由这个7引入向LruCache缓存添加内容的操作

      //EngineResource.java
      void acquire() {
        if (isRecycled) {
          throw new IllegalStateException("Cannot acquire a recycled resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
          throw new IllegalThreadStateException("Must call acquire on the main thread");
        }
        ++acquired;
      }
      void release() {
        if (acquired <= 0) {
          throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
        }
        if (!Looper.getMainLooper().equals(Looper.myLooper())) {
          throw new IllegalThreadStateException("Must call release on the main thread");
        }
        if (--acquired == 0) {
          listener.onResourceReleased(key, this);
        }
      }
       //Engine.java
       private final MemoryCache cache; //LruCache原理
       
       public void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
        Util.assertMainThread();
        activeResources.deactivate(cacheKey);
        if (resource.isCacheable()) {
          cache.put(cacheKey, resource); // 11
        } else {
          resourceRecycler.recycle(resource);
        }
      }
      

      向LruCache缓存添加内容 EngineResource是用一个acquired变量用来记录图片被引用的次数,调用acquire()方法会让变量加1,调用release()方法会让变量减1,也就是说,当acquired变量大于0的时候,说明图片正在使用中,也就应该放到activeResources弱引用缓存当中。而经过release()之后,如果acquired变量等于0了,说明图片已经不再被使用了,那么此时会在第24行调用listener的onResourceReleased()方法来释放资源,这个listener就是Engine对象。我们可以看到在11处向LruCache的实例对象cache添加资源。

    • 硬盘缓存
      • 硬盘缓存策略
        • DiskCacheStrategy.NONE: 表示不缓存任何内容。
        • DiskCacheStrategy.RESOURCE: 在资源解码将数据写入磁盘缓存,即经过缩放等转换后的图片资源。
        • DiskCacheStrategy.DATA: 在资源解码将原始数据写入磁盘缓存。
        • DiskCacheStrategy.ALL : 使用DATA和RESOURCE缓存远程数据,仅使用RESOURCE来缓存本地数据。
        • DiskCacheStrategy.AUTOMATIC:它会尝试对本地和远程图片使用最佳的策略。当你加载远程数据(比如,从URL下载)时,AUTOMATIC 策略仅会存储未被你的加载过程修改过(比如,变换,裁剪–译者注)的原始数据,因为下载远程数据相比调整磁盘上已经存在的数据要昂贵得多。对于本地数据,AUTOMATIC 策略则会仅存储变换过的缩略图,因为即使你需要再次生成另一个尺寸或类型的图片,取回原始数据也很容易。默认使用这种缓存策略
        • 通过RequestOptions的.diskCacheStrategy(参数)方法设置。

      硬盘缓存这块在4.8已经大不相同了,看的一脸**,暂时先缓缓后面有机会补上。?

    • 总结
      • 引用简书上另外一篇文章Glide4.9缓存策略的图片在这里插入图片描述
      • 至于为什么要有弱引用缓存?

      郭神的看法是: LruCache算法的实现,你会发现它其实是用一个Set来缓存对象的,每次内存超出缓存设定触发trim操作的时候,其实是对这个Set进行遍历,然后移除缓存。但是我们都知道Set是无序的,因此遍历的时候有可能会把正在使用的缓存给误伤了,我还在用着它呢就给移出去了。因此这个弱引用可能是对正在使用中的图片的一种保护,使用的时候先从LruCache里面移出去,用完了再把它重新加到缓存里面。

    2.常见的与Glide缓存策略相关的问题

    声明 下面仅收集一些大体的解决思路,尚未实际操作过,如有纰漏,还望指出。

    • 省流量模式
      • 情景: 省流量模式的应用情景就是减少不必要图片的加载。
      • 解决办法: 我们可以在RequestOptions中加入onlyRetrieveFromCache(true)的选项。那么图片就只会从缓存中读取,如果没有缓存则不加载图片,从而达到减少流量消耗的目的。
    • 头像没有及时更新的问题
      • 情景: 开发一款有头像的APP,我们修改了头像并且更新到了服务端,可是当我们点击查看大图时加载出来的还是原来的头像。
      • 解决办法: 这是Glide强大的缓存带来的副作用,我们可以在RequestOptions中加入.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)的选项。那么缓存的功能就会全部关闭,从而使得每次都是从服务端加载,所以头像会是最新。
    • URL重定向
      • 情景: 没有实际遇到过,但大体的意思应该是同一个URL在不同的时间可能会指向不同的资源,所以同样需要实时更新。
      • 解决办法: 同上。
    • 通过URL获取到该资源在本地的缓存

      该办法是Glide3.7版本的,尚未使用过,暂做一个记录。

    • 降低缓存图片的质量,减少内存消耗
      • 情景: 相册类的App经常需要同时展示大量的图片,这种情况下图片的质量可以低一点,因为加载速度优先于图片的质量。
      • 解决办法: 我们可以设置译码的格式,在RequestOptions中加入.encodeFormat(Bitmap.CompressFormat.WEBP).encodeQuality(10))的选项,①encodeFormat的参数有Bitmap.CompressFormat.PNG,Bitmap.CompressFormat.JPEG,Bitmap.CompressFormat.WEBP(质量从高到低);②encodeQuality设置的是0-100的int类型,一个质量百分比参数,越小质量越低。

    万水千山总是情,麻烦手下别留情。
    如若讲得有不妥,文末留言告知我,
    如若觉得还可以,收藏点赞要一起。

    opLW原创七言律诗,转载请注明出处

    展开全文
  • 常见缓存失效策略

    千次阅读 2018-09-19 13:30:03
    常见缓存失效策略 常见的几种缓存失效策略,总结一下: FIFO ,first in first out ,最先进入缓存的数据在缓存空间不够情况下(超出最大元素限制时)会被首先清理出去 LFU , Less Frequently Used ,一直以来最少...
  • 常见缓存算法设计策略
  • 通常,对于长列表加载的场景,... 下面总结对两种常见的分页缓存策略, 适用场景以及各自的优缺点。 策略一: 直接对分页结果缓存 顾名思义,就是直接缓存每次分页查询的结果。 适用场景 列表...
  • 缓存系列--缓存策略

    2017-11-26 00:47:29
    常用的缓存策略 LRU, FIFO,LFU, 等
  • 安卓网络数据缓存策略

    千次阅读 2016-12-04 00:43:09
    Json/Xml数据缓存策略对于文本类数据,同一个地址的请求结果一般是会随着时间而变化的,所以需要根据应用的需求来做缓存。 数据实时性高 常见的此类应用:新闻、朋友圈、股票、社区等。 无论是否存在缓存,都应该...
  • 浏览器缓存机制介绍与缓存策略剖析 缓存可以减少网络 IO 消耗,提高访问速度。浏览器缓存是一种操作简单、效果显著的前端性能优化手段。对于这个操作的必要性,Chrome 官方给出的解释似乎更有说服力一些: 通过网络...
  • 长列表分页缓存策略

    万次阅读 2017-01-13 20:40:25
    下面总结对两种常见的分页缓存策略, 适用场景以及各自的优缺点。 策略一: 直接对分页结果缓存 顾名思义,就是直接缓存每次分页查询的结果。 适用场景 列表长度无法提前预知 数据和排序不...
  • http缓存策略之强缓存与协商缓存

    千次阅读 2020-04-11 16:45:25
    因此通过浏览器的缓存机制,协同服务器让浏览器缓存那些不需要频繁变动的资源就可以有效地降低流量消耗和响应时间。 一、什么是http缓存 http缓存指的是:当客户端向服务器请求资源时,会先查...
  • HTTP web缓存策略

    千次阅读 2012-09-28 15:08:46
    1. web缓存基本作用: ... 其次缓存能减少客户端对远端server的依赖,从而...下面着重阐述的是缓存的一些基本知识以及缓存策略给HTTP用户请求带来的改变。 2. 缓存的基本知识: (1) 命中(hit)以及缺失(miss)  衡
  • 一种头像缓存策略

    2016-04-07 13:15:38
    一种头像缓存策略 在具体场景下设计缓存的逻辑。 作者:@nixzhu 许多 App 都有用户系统,不论是自己实现还是使用第三方,大概都需要显示用户的头像。比较常见的情景下,头像会在某些列表里出现,例如联系人列表...
  • 作者:kevinylzhao,腾讯音乐前端开发工程师浏览器缓存策略对于前端开发同学来说不陌生,大家都有一定的了解,但如果没有系统的归纳总结,可能三言两语很难说明白,甚至说错,尤其在面试过...
  • 大型web站点缓存策略设计概要

    千次阅读 2009-10-19 20:57:00
    上篇对疯狂代码缓存配置进行了概要的设计,可能说的有点模糊了,有几个朋友发了几个问题探讨了下,这里有必要先澄清一个问题,和常见缓存策略不同,我们的缓存策略将重点放在更新策略而不是只读策略上。...
  • HTTP缓存策略有效提高网络效率,开发一个网络库或者图片加载库都需要用到它,用于判断存储的数据是否过期,是否需要重新请求服务器。 简单介绍HTTP缓存策略 HTTP 1.0 缓存 Pragma :表示是否缓存 Expires : ...
  • java中的缓存策略

    2017-03-13 10:17:15
    资源: 在JDK1.2版本开始,把对象的引用分为四种级别,这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。 剖解资源: 强引用:例如 String str=new String();这个str就具有强引用 ...
  • 上篇对疯狂代码缓存配置进行了概要的设计,可能说的有点模糊了,有几个朋友发了几个问题探讨了下,这里有必要先澄清一个问题,和常见缓存策略不同,我们的缓存策略将重点放在更新策略而不是只读策略上。...
  • 这里我将总结浏览器端的缓存策略的不同,浏览器的缓存机制使用场景并不是很多…这里我在设计用户的管理中接触到的相关,以作学习、总结 这里是三大浏览器的缓存策略,都是相同的东西 下面简单介绍常见的 3 种机制的...
  • 常见策略: FIFO(First In First Out):先进先出算法,即先放入缓存的先被移除; LRU(Least Recently Used):最久未使用算法,使用时间距离现在最久的那个被移除; LFU(Least Frequently Used):最近最少使用...
  • 什么是缓存,缓存策略有哪些?

    千次阅读 2019-06-30 15:46:22
    1、什么是缓存? ☞ 缓存就是数据交换的缓冲区(称作:Cache),当某一硬件要读取数据时,会首先从缓存汇总查询数据,有则直接执行,不存在时从内存中获取。由于缓存的数据比内存快的多,所以缓存的作用就是帮助...
  • OkHttp3源码分析[缓存策略]

    千次阅读 2016-06-27 21:24:23
    OkHttp3源码分析[综述]OkHttp3源码分析[复用连接池]OkHttp3源码分析[缓存策略]OkHttp3源码分析[DiskLruCache]OkHttp3源码分析[任务队列] 本文专门分析OkHttp的缓存策略,应该是okhttp分析中最简单的一篇了 HTTP...
  • Sprite缓存策略总结

    2011-12-14 12:04:35
    它通过简单的读写锁保证整个系统的缓存一致性,并且通过和虚拟存储部分交互,尽量多地缓存数据。本文主要调研了Sprite File System缓存的设计方案,以及讲SFS缓存一致性的思想移植到NFS的具体做法。 1 Sprit
  • 分发路径中的组件均可以缓存内容来加速后续的请求,这受控于对该内容所声明的缓存策略。在这份指南中,我们将讨论一些Web内容缓存的基本概念。这主要包括如何选择缓存策略以保证互联网范围内的缓存能够正确的处理您...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 138,126
精华内容 55,250
关键字:

常见的缓存策略