网易首页 > 网易号 > 正文 申请入驻

来自未来的缓存 Caffeine,带你揭开它的神秘面纱

0
分享至

作者 | Garnett

来源 | Garnett的Java之路(ID:gh_009246af52d4)

头图 | CSDN 下载自东方IC

caffeine是什么,它和redis什么区别,有哪些作用,那么让我们带着疑问让Garnett来告诉你这个来自未来的缓存-Caffeine!

Caffeine和Redis的区别是什么?

1、相同点:

两个都是缓存的方式

2、 不同点:

  • redis是将数据存储到内存里
    caffeine是将数据存储在本地应用里

  • caffeine和redis相比,没有了网络IO上的消耗

3、联系:

一般将两者结合起来,形成一二级缓存。使用流程大致如下:
去一级缓存中查找数据(caffeine-本地应用内)
如果没有的话,去二级缓存中查找数据(redis-内存)
再没有,再去数据库中查找数据(数据库-磁盘)

到这里大家应该清楚了其实redis和caffeine都是缓存,但是他们并不相同,所以别混淆了!

那么什么是 Caffeine

1、Caffeine简介

Caffeine是基于jdk 1.8 Version的高性能缓存库。Caffeine提供的内存缓存使用参考Google guava的API。Caffeine是基于Google Guava Cache设计经验上改进的成果。

Caffeine是使用jdk 1.8对Guava cache的重写版本,基于LRU算法实现,支持多种缓存过期策略。

那么先来看看Caffeine和其他的进程缓存的区别,为什么叫它来自未来的缓存呢?

2、其他进程缓存的简单介绍

Google Guava工具包中的一个非常方便易用的本地化缓存实现,基于LRU算法实现,支持多种缓存过期策略。

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。

3、性能比较

场景1:8个线程读,100%的读操作

场景二:6个线程读,2个线程写,也就是75%的读操作,25%的写操作

场景三:8个线程写,100%的写操作

可以清楚的看到Caffeine效率明显的高于其他缓存。

4、如何使用

public static void main(String[] args) {LoadingCache build = CacheBuilder.newBuilder().initialCapacity(1).maximumSize(100).expireAfterWrite(1, TimeUnit.DAYS).build(new CacheLoader() {//默认的数据加载实现,当调用get取值的时候,如果key没有对应的值,就调用这个方法进行加载@Overridepublic String load(String key) {return "";}});}

5、参数方法

  • initialCapacity(1) 初始缓存长度为1

  • maximumSize(100) 最大长度为100

  • expireAfterWrite(1, TimeUnit.DAYS) 设置缓存策略在1天未写入过期缓存

Caffeine的原理

1、W-TinyLFU

既然说到了淘汰策略,那么就简单说说redis的吧

继续说Caffeine的淘汰策略

传统的LFU受时间周期的影响比较大。所以各种LFU的变种出现了,基于时间周期进行衰减,或者在最近某个时间段内的频率。同样的LFU也会使用额外空间记录每一个数据访问的频率,即使数据没有在缓存中也需要记录,所以需要维护的额外空间很大。

可以试想我们对这个维护空间建立一个hashMap,每个数据项都会存在这个hashMap中,当数据量特别大的时候,这个hashMap也会特别大。

再回到LRU,我们的LRU也不是那么一无是处,LRU可以很好的应对突发流量的情况,因为他不需要累计数据频率。

所以W-TinyLFU结合了LRU和LFU,以及其他的算法的一些特点。

为了改进上述 LRU 和 LFU 存在的问题,前Google工程师在 TinyLfu的基础上发明了 W-TinyLFU 缓存算法。Caffine 就是基于此算法开发的。

Caffeine 因使用 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率。

TinyLFU维护了近期访问记录的频率信息,作为一个过滤器,当新记录来时,只有满足TinyLFU要求的记录才可以被插入缓存。

TinyLFU借助了数据流Sketching技术,它可以用小得多的空间存放频次信息。TinyLFU采用了一种基于滑动窗口的时间衰减设计机制,借助于一种简易的 reset 操作:每次添加一条记录到Sketch的时候,都会给一个计数器上加 1,当计数器达到一个尺寸 W 的时候,把所有记录的 Sketch 数值都除以 2,该 reset 操作可以起到衰减的作用 。

W-TinyLFU主要用来解决一些稀疏的突发访问元素。在一些数目很少但突发访问量很大的场景下,TinyLFU将无法保存这类元素,因为它们无法在给定时间内积累到足够高的频率。因此 W-TinyLFU 就是结合 LFU 和LRU,前者用来应对大多数场景,而 LRU 用来处理突发流量。

在处理频次记录方面,采用 Bloom Filter,对于每个key,用 n 个 byte 每个存储一个标志用来判断 key 是否在集合中。原理就是使用 k 个 hash 函数来将 key 散列成一个整数。

在 W-TinyLFU 中使用 Count-Min Sketch 记录 key 的访问频次,而它就是布隆过滤器的一个变种。

2、读写性能

在guava cache中我们说过其读写操作中夹杂着过期时间的处理,也就是你在一次Put操作中有可能还会做淘汰操作,所以其读写性能会受到一定影响,可以看上面的图中,caffeine的确在读写操作上面完爆guava cache。主要是因为在caffeine,对这些事件的操作是通过异步操作,他将事件提交至队列,这里的队列的数据结构是RingBuffer。然后会通过默认的ForkJoinPool.commonPool(),或者自己配置线程池,进行取队列操作,然后在进行后续的淘汰,过期操作。

当然读写也是有不同的队列,在caffeine中认为缓存读比写多很多,所以对于写操作是所有线程共享一个Ringbuffer。

对于读操作比写操作更加频繁,进一步减少竞争,其为每个线程配备了一个RingBuffer:

3、数据淘汰策略

在caffeine所有的数据都在ConcurrentHashMap中,这个和guava cache不同,guava cache是自己实现了个类似ConcurrentHashMap的结构。在caffeine中有三个记录引用的LRU队列:

  • Eden队列:在caffeine中规定只能为缓存容量的%1,如果size=100,那这个队列的有效大小就等于1。这个队列中记录的是新到的数据,防止突发流量由于之前没有访问频率,而导致被淘汰。比如有一部新剧上线,在最开始其实是没有访问频率的,防止上线之后被其他缓存淘汰出去,而加入这个区域。伊甸区,最舒服最安逸的区域,在这里很难被其他数据淘汰。

  • Probation队列:叫做缓刑队列,在这个队列就代表你的数据相对比较冷,马上就要被淘汰了。这个有效大小为size减去eden减去protected。

  • Protected队列:在这个队列中,可以稍微放心一下了,你暂时不会被淘汰,但是别急,如果Probation队列没有数据了或者Protected数据满了,你也将会被面临淘汰的尴尬局面。当然想要变成这个队列,需要把Probation访问一次之后,就会提升为Protected队列。这个有效大小为(size减去eden) X 80% 如果size =100,就会是79。

这三个队列关系如下:

  1. 所有的新数据都会进入Eden。

  2. Eden满了,淘汰进入Probation。

  3. 如果在Probation中访问了其中某个数据,则这个数据升级为Protected。

  4. 如果Protected满了又会继续降级为Probation。

对于发生数据淘汰的时候,会从Probation中进行淘汰。会把这个队列中的数据队头称为受害者,这个队头肯定是最早进入的,按照LRU队列的算法的话那他其实他就应该被淘汰,但是在这里只能叫他受害者,这个队列是缓刑队列,代表马上要给他行刑了。这里会取出队尾叫候选者,也叫攻击者。这里受害者会和攻击者PK决出我们应该被淘汰的。

通过我们的Count-Min Sketch中的记录的频率数据有以下几个判断:

  • 如果攻击者大于受害者,那么受害者就直接被淘汰。

  • 如果攻击者<=5,那么直接淘汰攻击者。这个逻辑在他的注释中有解释:

    他认为设置一个预热的门槛会让整体命中率更高。

  • 其他情况,随机淘汰。

Caffeine功能剖析

1、转瞬即逝-过期策略

在Caffeine中分为两种缓存,一个是有界缓存,一个是无界缓存,无界缓存不需要过期并且没有界限。在有界缓存中提供了三个过期API:

  • expireAfterWrite:代表着写了之后多久过期。(上面列子就是这种方式)

  • expireAfterAccess:代表着最后一次访问了之后多久过期。

  • expireAfter:在expireAfter中需要自己实现Expiry接口,这个接口支持create,update,以及access了之后多久过期。注意这个API和前面两个API是互斥的。这里和前面两个API不同的是,需要你告诉缓存框架,他应该在具体的某个时间过期,也就是通过前面的重写create,update,以及access的方法,获取具体的过期时间。

2、除旧布新-更新策略

何为更新策略?就是在设定多长时间后会自动刷新缓存。

Caffeine提供了refreshAfterWrite()方法来让我们进行写后多久更新策略:

LoadingCache build = CacheBuilder.newBuilder().refreshAfterWrite(1, TimeUnit.DAYS).build(new CacheLoader() {@Overridepublic String load(String key) {return "";}});}

上面的代码我们需要建立一个CacheLodaer来进行刷新,这里是同步进行的,可以通过buildAsync方法进行异步构建。在实际业务中这里可以把我们代码中的mapper传入进去,进行数据源的刷新。

但是实际使用中,你设置了一天刷新,但是一天后你发现缓存并没有刷新。这是因为必有在1天后这个缓存再次访问才能刷新,如果没人访问,那么永远也不会刷新。你明白了吗?

我们来看看自动刷新他是怎么做的呢?自动刷新只存在读操作之后,也就是我们afterRead()这个方法,其中有个方法叫refreshIfNeeded,他会根据你是同步还是异步然后进行刷新处理。

3、知己知彼-打点监控

在Caffeine中提供了一些的打点监控策略,通过recordStats()Api进行开启,默认是使用Caffeine自带的,也可以自己进行实现。在StatsCounter接口中,定义了需要打点的方法目前来说有如下几个:

  • recordHits:记录缓存命中

  • recordMisses:记录缓存未命中

  • recordLoadSuccess:记录加载成功(指的是CacheLoader加载成功)

  • recordLoadFailure:记录加载失败

  • recordEviction:记录淘汰数据

通过上面的监听,我们可以实时监控缓存当前的状态,以评估缓存的健康程度以及缓存命中率等,方便后续调整参数。

4、有始有终-淘汰监听

有很多时候我们需要知道Caffeine中的缓存为什么被淘汰了呢,从而进行一些优化?这个时候我们就需要一个监听器,代码如下所示:

Cache cache = Caffeine.newBuilder().removaListener(((key,value,cause) -> {System.out.println(cause); })).build();

在Caffeine中被淘汰的原因有很多种:

  • EXPLICIT: 这个原因是,用户造成的,通过调用remove方法从而进行删除。

  • REPLACED: 更新的时候,其实相当于把老的value给删了。

  • COLLECTED: 用于我们的垃圾收集器,也就是我们上面减少的软引用,弱引用。

  • EXPIRED:过期淘汰。

  • SIZE: 大小淘汰,当超过最大的时候就会进行淘汰。

当我们进行淘汰的时候就会进行回调,我们可以打印出日志,对数据淘汰进行实时监控。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
平顶山打人夫妻结婚照曝光:男的酷似昆山龙哥,女的刻薄像燕冬萍

平顶山打人夫妻结婚照曝光:男的酷似昆山龙哥,女的刻薄像燕冬萍

江山挥笔
2026-02-22 09:48:58
俄媒称美俄达成了超级交易,俄罗斯配合反华,换取美国出卖乌克兰

俄媒称美俄达成了超级交易,俄罗斯配合反华,换取美国出卖乌克兰

贾文彬的史书
2026-02-22 11:55:52
不再希望理解?谷爱凌夺金后疏忽了地上的红旗,但没忘记披上国旗

不再希望理解?谷爱凌夺金后疏忽了地上的红旗,但没忘记披上国旗

真理是我亲戚
2026-02-22 19:51:59
郭松民新理论:穿汉服违反宪法……

郭松民新理论:穿汉服违反宪法……

西域都护
2026-02-22 19:28:01
2-1绝杀夺冠!美国助攻日本:保住奖牌榜前十 中国排名高韩国1位

2-1绝杀夺冠!美国助攻日本:保住奖牌榜前十 中国排名高韩国1位

侃球熊弟
2026-02-23 00:29:36
从33.27亿跌到1.56亿,我感慨:离开沈腾,马丽也扛不起票房

从33.27亿跌到1.56亿,我感慨:离开沈腾,马丽也扛不起票房

白公子探剧
2026-02-21 23:34:43
重大错误,印制错误的韩国国旗被升起4次,国际奥委会已致歉

重大错误,印制错误的韩国国旗被升起4次,国际奥委会已致歉

懂球帝
2026-02-22 17:22:25
《镖人:风起大漠》成影史武侠片票房冠军

《镖人:风起大漠》成影史武侠片票房冠军

界面新闻
2026-02-22 20:00:38
中国游客遗体被打捞出,俄外长致哀

中国游客遗体被打捞出,俄外长致哀

观察者网
2026-02-22 15:35:13
贝加尔湖溺亡中国游客遗体均被打捞上岸!车上成员含一家四口

贝加尔湖溺亡中国游客遗体均被打捞上岸!车上成员含一家四口

南方都市报
2026-02-22 17:40:24
从感冒到离世仅5天!唐山“钓帝”安大爷去世,儿子:太快了没来得及留遗言,父亲本打算开春去钓鱼

从感冒到离世仅5天!唐山“钓帝”安大爷去世,儿子:太快了没来得及留遗言,父亲本打算开春去钓鱼

芒果都市
2026-02-22 12:31:26
暴涨!有相机价格翻10倍,根本抢不到,杭州姑娘傻眼,马上翻出家里老古董

暴涨!有相机价格翻10倍,根本抢不到,杭州姑娘傻眼,马上翻出家里老古董

环球网资讯
2026-02-22 16:48:12
湛江东海岛“许老板”许荣兴:低调背后,到底藏着多少生意?

湛江东海岛“许老板”许荣兴:低调背后,到底藏着多少生意?

奇思妙想草叶君
2026-02-22 19:14:23
反转!妈祖巡游所换为女童,从着装能看出,被换女孩疑炒作删视频

反转!妈祖巡游所换为女童,从着装能看出,被换女孩疑炒作删视频

古希腊掌管松饼的神
2026-02-22 22:21:04
90位外嫁女集体回村过年,当事人:这是娘家最珍贵的礼物,见到了多年未见的小伙伴

90位外嫁女集体回村过年,当事人:这是娘家最珍贵的礼物,见到了多年未见的小伙伴

极目新闻
2026-02-22 18:58:23
22岁已成冬奥传奇!谷爱凌一战刷爆6大纪录,2届复刻王濛神迹

22岁已成冬奥传奇!谷爱凌一战刷爆6大纪录,2届复刻王濛神迹

奥拜尔
2026-02-22 19:42:24
万幸!家庭聚会男子中途去屋外透气,抬头见3岁儿子坠楼,立马伸手接住!“后怕极了”

万幸!家庭聚会男子中途去屋外透气,抬头见3岁儿子坠楼,立马伸手接住!“后怕极了”

极目新闻
2026-02-22 11:50:28
女人默许你“得手”从不主动靠近:这三种默许,已是最明确的信号

女人默许你“得手”从不主动靠近:这三种默许,已是最明确的信号

青苹果sht
2026-02-22 06:58:10
0-2到2-2!赵心童决赛翻身:连得187分,希金斯连续2局颗粒无收!

0-2到2-2!赵心童决赛翻身:连得187分,希金斯连续2局颗粒无收!

刘姚尧的文字城堡
2026-02-22 22:16:12
前乌军总司令扎卢日内爆出猛料,俄乌开战前夕,泽连斯基多次误判

前乌军总司令扎卢日内爆出猛料,俄乌开战前夕,泽连斯基多次误判

碳基生物关怀组织
2026-02-21 22:57:12
2026-02-23 02:28:49
CSDN incentive-icons
CSDN
成就一亿技术人
26330文章数 242233关注度
往期回顾 全部

科技要闻

马斯克:星舰每年将发射超过10000颗卫星

头条要闻

男子持霰弹枪燃烧罐闯特朗普私宅被击毙 细节披露

头条要闻

男子持霰弹枪燃烧罐闯特朗普私宅被击毙 细节披露

体育要闻

谷爱凌:6次参赛6次夺牌 我对自己非常自豪

娱乐要闻

谷爱凌:真正的强大 敢接纳生命的节奏

财经要闻

特朗普新加征关税税率从10%提升至15%

汽车要闻

续航1810km!smart精灵#6 EHD超级电混2026年上市

态度原创

教育
房产
游戏
本地
军事航空

教育要闻

两所大学,合并!

房产要闻

窗前即地标!独占三亚湾C位 自贸港总裁行宫亮相

《GTA6》的第二天发售?《宝可梦》新作爆料来了!

本地新闻

春花齐放2026:《骏马奔腾迎新岁》

军事要闻

约旦基地美军战机骤增 包括F-35隐形战斗机

无障碍浏览 进入关怀版