0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

缓存之美——如何选择合适的本地缓存?

京东云 来源:jf_75140285 作者:jf_75140285 2024-11-17 14:24 次阅读

作者:京东保险 郭盼

1、简介

小编最近在使用系统的时候,发现尽管应用已经使用了redis缓存提高查询效率,但是仍然有进一步优化的空间,于是想到了比分布式缓存性能更好的本地缓存,因此对领域内常用的本地缓存进行了一番调研,有早期的Guava缓存、在Guava上进一步传承的Caffine以及自称在Java中使用最广泛的EhCache,那么我们该怎么选择适合自己应用的缓存呢,小编下面会简单介绍,并将以上缓存进行一个对比,希望帮助大家选择最适合自己系统的本地缓存。

2、Guava缓存简介

Guava cache是Google开发的Guava工具包中一套完善的JVM本地缓存框架,底层实现的数据结构类似于ConcurrentHashMap,但是进行了更多的能力拓展,包括缓存过期时间设置、缓存容量设置、多种淘汰策略、缓存监控等,下面简单介绍下这些功能及其使用方式。

2.1、缓存过期时间设置

Guava的过期时间设置有基于创建时间和最后一次访问时间两种策略.

(1) 基于创建时间

通过对比缓存记录的插入时间来判断,比如设置过期时间为5分钟,不管中间有没有访问,到时过期。

public Cache< String, String > createCache() {    
    return CacheBuilder.newBuilder()
    .expireAfterWrite(5L, TimeUnit.MINUTES)
    .build();
}

(2) 基于过期时间

通过对比最近最后一次的访问时间,比如设置5分钟,每次访问之后都会刷新过期时间为5分钟,只有持续5分钟没有被访问到才会过期。

public Cache< String, String > createCache() {    
    return CacheBuilder.newBuilder()
    .expireAfterAccess(5L, TimeUnit.MINUTES)
    .build();
}

2.2、缓存容量和淘汰策略设置

Guava cache是内存型缓存,有内存溢出风险,因此需要设置缓存的最大存储上限,通过缓存的条数或每条缓存的权重来判断是否达到了设定阈值,当缓存的数据量达到设定阈值之后,Guava cache支持使用FIFO和LRU的策略对缓存记录采取淘汰的措施。

(1)限制缓存记录条数

public Cache< String, User > createCache() {    
    return CacheBuilder.newBuilder()
    .maximumSize(100L)
    .build();
}

(2)限制缓存记录权重

public Cache< String, User > createCache() {    
    return CacheBuilder.newBuilder()
    .maximumWeight(100L)
    .weigher((key, value) -> (int) Math.ceil(instrumentation.getObjectSize(value) / 1024L))       
    .build();
}

使用限制缓存记录权重时要先计算weight的value对象的字节数,每1kb字节作为一个权重,对比限制缓存记录,我们就能将缓存的总占用限制在100kb左右。

2.3缓存监控

缓存记录的加载和命中情况是评价缓存处理能力的重要指标,Guava cache提供了stat统计日志对这两个指标进行了统计,我们只需要在创建缓存容器的时候加上recordStats就可以开启统计。

public Cache< String, User > createCache() {    
    return CacheBuilder.newBuilder()
    .recordStats()
    .build();
}

2.4 Guava cache的优劣势和适用场景

优劣势:Guava cache通过内存处理数据,具有减少IO请求,读写性能快的优势,但是受内存容量限制,只能处理少量数据的读写,还有可能对本机内存造成压力,并且在分布式部署中,会存在不同机器节点数据不一致的情况,即缓存漂移。

适用场景:读多写少,对数据一致性要求不高的场景。

3、Caffeine简介

Caffeine同样是Google开发的,是在Guava cache的基础上改良而来的,底层设计思路、功能和使用方式与Guava非常类似,但是各方面的性能都要远远超过前者,可以看做是Guava cache的升级版,因此,之前使用过Guava cache,也能够很快的上手Caffeine,下面是Caffeine和Guava cache的缓存创建对比,基本可以无门槛过渡。

public Cache< String, String > createCache() {
    return Caffeine.newBuilder()
        .initialCapacity(1000)
        .maximumSize(100L)
        .expireAfterWrite(5L, TimeUnit.MINUTES)

        .recordStats()
        .build();
}

public Cache< String, String > createCache() {    
    return CacheBuilder.newBuilder()
    .initialCapacity(1000)
    .maximumSize(100L)
    .expireAfterWrite(5L, TimeUnit.MINUTES)
    .recordStats()
    .build();
}

那么Caffeine底层又做了哪些优化,才能让其性能高于Guava cache呢?主要包含以下三点:

3.1、对比Guava cache的性能主要优化项

(1)异步策略

Guava cache在读操作中可能会触发淘汰数据的清理操作,虽然自身也做了一些优化来减少读的时候的清理操作,但是一旦触发,就会降低查询效率,对缓存性能产生影响。而在Caffeine支持异步操作,采用异步处理的策略,查询请求在触发淘汰数据的清理操作后,会将清理数据的任务添加到独立的线程池中进行异步操作,不会阻塞查询请求,提高了查询性能。

wKgaomcyxP6Adt0qAABfE5AgeRg699.png



(2)ConcurrentHashMap优化

Caffeine底层都是通过ConcurrentHashMap来进行数据的存储,因此随着Java8中对ConcurrentHashMap的调整,数组+链表的结构升级为数组+链表+红黑树的结构以及分段锁升级为syschronized+CAS,降低了锁的粒度,减少了锁的竞争,这两个优化显著提高了Caffeine在读多写少场景下的查询性能。



(3)新型淘汰算法W-TinyLFU

传统的淘汰算法,如LRU、LFU、FIFO,在实际的缓存场景中都存在一些弊端,如FIFO算法,如果缓存使用的频率较高,那么缓存数据会一直处在进进出出的状态,间接影响到缓存命中率。LRU算法,在批量刷新缓存数据的场景下,可能会将其他缓存数据淘汰掉,从而带来缓存击穿的风险。LFU算法,需要保存缓存记录的访问次数,带来内存空间的损耗。

因此,Caffeine引入了W-TinyLFU算法,由窗口缓存、过滤器、主缓存组成。缓存数据刚进入时会停留在窗口缓存中,这个部分只占总缓存的1%,当被挤出窗口缓存时,会在过滤器汇总和主缓存中淘汰的数据进行比较,如果频率更高,则进入主缓存,否则就被淘汰,主缓存被分为淘汰段和保护段,两段都是LRU算法,第一次被访问的元素会进入淘汰段,第二次被访问会进入保护段,保护段中被淘汰的元素会进入淘汰段,这种算法实现了高命中率和低内存占用。更详细的解释可以参考论文:https://arxiv.org/pdf/1512.00727.pdf

wKgaomcyxP-ADcJTAAJCh7bEYmk122.png

3.2、Caffeine的优劣势和适用场景

优势:对比Guava cache有更高的缓存性能,劣势:仍然存在缓存漂移的问题;JDK版本过低无法使用

适用场景:1、适用场景:读多写少,对数据一致性要求不高的场景;2、纯内存缓存,JDK8及更高版本中,追求比Guava cache更高的性能。

4、Ehcache简介

Guava cache和Caffeine都是JVM缓存,会受到内存大小的制约,最新的Ehcache采用堆内缓存+堆外缓存+磁盘的方式,打破了这一制约。堆内缓存就是被JVM管理的那一部分缓存,而堆外缓存,就是在内存中另外在开辟一块不被JVM管理的部分。堆外缓存这部分既可以享受内存的高速读写能力,而且又避免的JVM频繁的GC,缺点是需要自行清理数据。

wKgaomcyxQCAKJKUAADHz0yrAFg116.png

下面是Ehcache缓存的创建,指定了堆内、堆外缓存和磁盘缓存的大小。

ResourcePoolsBuilder.newResourcePoolsBuilder()
    .heap(20, MemoryUnit.MB)
    .offheap(10, MemoryUnit.MB)
    .disk(5, MemoryUnit.GB);

为了解决缓存漂移的问题,Ehcache支持通过集群的方式,实现了分布式节点之间的数据互通。关于Ehcache的集群策略,后续文章再详细阐述。

5、不同本地缓存对比

框架 命中率 速度 回收算法 使用难度 集群 适用场景
Guava cache 第三 LRU、LFU、FIFO 不支持 读多写少,允许少量缓存偏移
Caffeine 第一 W-TinyLFU 不支持 读多写少,允许少量缓存偏移,能用Caffeine就别用Guava cache
Ehcache 第二 LRU、LFU、FIFO 支持 分布式系统中对数据一致性要求高



声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2954

    浏览量

    104510
  • 缓存
    +关注

    关注

    1

    文章

    231

    浏览量

    26640
  • Redis
    +关注

    关注

    0

    文章

    371

    浏览量

    10834
收藏 人收藏

    评论

    相关推荐

    高并发系统中的缓存 缓存系统存在的三大问题

    缓存在计算机系统是无处不在,在CPU层面有L1-L3的Cache,在Linux中有TLB加速虚拟地址和物理地址的转换,在浏览器有本地缓存、手机有本地
    的头像 发表于 07-15 11:03 4202次阅读

    本地缓存的技术实践

    一、摘要 说到缓存,面试官基本上会绕不开以下几个话题! 项目中哪些地方用到了缓存?为什么要使用缓存?怎么使用它的?引入缓存后会带来哪些问题? 这些问题,基本上是互联网公司面试时必问的一
    的头像 发表于 09-30 15:29 655次阅读
    <b class='flag-5'>本地</b><b class='flag-5'>缓存</b>的技术实践

    如何选择合适本地缓存

    的 Guava 缓存、在 Guava 上进一步传承的 Caffine 以及自称在 Java 中使用最广泛的 EhCache,那么我们该怎么选择适合自己应用的缓存呢,小编下面会简单介绍,并将以上
    的头像 发表于 01-18 11:19 801次阅读
    如何<b class='flag-5'>选择</b><b class='flag-5'>合适</b>的<b class='flag-5'>本地</b><b class='flag-5'>缓存</b>?

    157.157、缓存 缓存使用 本地锁在分布式下的问题

    缓存
    充八万
    发布于 :2023年07月18日 04:44:59

    刻录机缓存容量多大才合适呢?

    刻录机缓存容量多大才合适呢?   多大缓存合适呢? 刻录机在刻录过程中,包括激光刻录头在内的刻录系统的工作是连续的,它
    发表于 12-26 10:06 4110次阅读

    Mybatis缓存之一级缓存

    本文主要讲mybatis的一级缓存,一级缓存是SqlSession级别的缓存。mybatis提供查询缓存,用于减轻数据压力,提高数据库性能。mybaits提供一级
    发表于 11-27 20:44 1216次阅读
    Mybatis<b class='flag-5'>缓存</b>之一级<b class='flag-5'>缓存</b>

    渲染中的帧缓存和深度缓存

    渲染涉及大量的缓存,这里缓存只是一个简单的存有像素数据的矩形内存块,最重要缓存是帧缓存和深度缓存
    的头像 发表于 05-14 11:44 6286次阅读
    渲染中的帧<b class='flag-5'>缓存</b>和深度<b class='flag-5'>缓存</b>

    内容中心网络中基于用户偏好的协作缓存策略

    用户本地偏好度指标,实现缓存内容的选择;然后,对需要缓存内容执行差异化缓存策略,全局活跃的内容则缓存
    发表于 12-19 15:23 4次下载
    内容中心网络中基于用户偏好的协作<b class='flag-5'>缓存</b>策略

    什么是Web缓存,HTTP缓存和浏览器缓存的区别

    前端缓存主要是分为HTTP缓存和浏览器缓存。其中HTTP缓存是在HTTP请求传输时用到的缓存,主要在服务器代码上设置;而浏览器
    发表于 09-13 04:17 9419次阅读
    什么是Web<b class='flag-5'>缓存</b>,HTTP<b class='flag-5'>缓存</b>和浏览器<b class='flag-5'>缓存</b>的区别

    缓存的基本原理 缓存的分类

    缓存的主要手段有:浏览器缓存、CDN、反向代理、本地缓存、分布式缓存、数据库缓存
    发表于 06-13 12:04 4637次阅读

    ThingJS平台推出3D场景本地缓存技术

    为提升用户访问体验,缩短项目加载时间,ThingJS平台推出3D场景本地缓存技术:IndexedDB,也称客户端缓存持久化技术。通俗来说,IndexedDB 就是浏览器提供的本地数据库
    发表于 03-13 11:19 1772次阅读

    聊聊本地缓存和分布式缓存

    本地缓存 :应用中的缓存组件,缓存组件和应用在同一进程中,缓存的读写非常快,没有网络开销。但各应用或集群的各节点都需要维护自己的单独
    发表于 06-11 15:12 803次阅读
    聊聊<b class='flag-5'>本地</b><b class='flag-5'>缓存</b>和分布式<b class='flag-5'>缓存</b>

    如何在 Linux 上查看本地 DNS 缓存

      刷新本地 DNS 缓存可以解决 HTTP 错误并保护您免受 DNS 欺骗。以下是在 Linux 上执行此操作的方法。 当您使用域名访问网站时,您的系统会向 DNS 服务器发送请求以获取该域
    的头像 发表于 06-26 10:52 3293次阅读
    如何在 Linux 上查看<b class='flag-5'>本地</b> DNS <b class='flag-5'>缓存</b>

    Ehcache!这才是Java本地缓存之王!

    就Java而言,其常用的缓存解决方案有很多,例如数据库缓存框架EhCache,分布式缓存Memcached等,这些缓存方案实际上都是为了提升吞吐效率,避免持久层压力过大。
    的头像 发表于 07-29 11:21 1658次阅读
    Ehcache!这才是Java<b class='flag-5'>本地</b><b class='flag-5'>缓存</b>之王!

    Redis缓存预热+缓存雪崩+缓存击穿+缓存穿透要点简析

    缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统。
    的头像 发表于 12-25 09:41 839次阅读
    Redis<b class='flag-5'>缓存</b>预热+<b class='flag-5'>缓存</b>雪崩+<b class='flag-5'>缓存</b>击穿+<b class='flag-5'>缓存</b>穿透要点简析