新一代缓存之王Caffeine,甩了Guava几条街!

文章来源:https://juejin.cn/post/7125674984562753572

目录

  • 为什么说Caffeine好?

  • 测试命中率

  • 异步载入

6a34ca0440d9080101c0b6bc94d7f223.png

我想把记忆缓存起来,等再次见到你,就能够很快认出你。

能够说出这么有哲理的话,得益于我对缓存的理解,以及对它的看重。没有了缓存,我的人生就没有了意义。

ebf87654c5a08716cf384863e4e845c5.png

缓存是非常重要的,工作中大部分工作可以说是和缓存打交道。由于使用广泛,所以针对缓存系统的任何优化,如果能够提高一丁点儿性能,就会让人无比兴奋。

很长一段时间,我都在用GuavaLoadingCache。它和ConcurrentHashMap是非常像的,但在其上封装了一些好用的逐出策略和并发优化,就显得好用的多。

今天主要说的是Caffeine,中文名就是咖啡因,一种容易让人精神亢奋的物质。它可以说是Guava的重写,但是效率却非常的高,青出于蓝而胜于蓝。

下图是Caffeine的一张性能测试图。可以看到它的性能,甩了GuavaCache老远。

这是为什么呢?

outside_default.png

首先要从它的作者开始说起。作者的github是( github.com/ben-manes ),曾经写了ConcurrentLinkedHashMap这个类,而这个类又是GuavaCache的基础。Ben Manes 一拍脑袋,决定更上层楼。


为什么说Caffeine好?

后浪Caffeine一来,GuavaCache就已经OUT了。

Caffeine支持异步加载方式,直接返回CompletableFutures,相对于GuavaCache的同步方式,它不用阻塞等待数据的载入。另外,它的编程模型是友好的,省去了很多重复的工作。

GuavaCache是基于LRU的,而Caffeine是基于LRU和LFU的,结合了两者的优点。对这两个算法不太清楚的同学,可以参考xjjdog之前的文章:《3种堆内缓存算法,赠源码和设计思路》

两者合体之后,变成了新的W-TinyLFU算法,它的命中率非常高,内存占用更加的小,这是主要原因所在。

Caffeine另外一个比较快的原因,就是很多操作都使用了异步,把这些事件提交到队列里。队列使用的RingBuffer,看到这个名词,我不自觉的想到了lmaxDisruptor,它已经成了无锁高并发的代名词。


测试命中率

我们决定拿线上的数据进行验证一下。事实上,大部分比较重要的Cache,我都已经使用Caffeine替换了,完成了骚气的升级。

由于它们的API长得非常像,这个过程是无痛的,连麻药都不需要打。

其中有个业务,有一个大的堆内缓存,缓存了用户数据。里面包含用户名、性别、地址、积分等属性,形成了一个JSON对象,但大小不超过1KB。通过灰度,根据不同的策略,我们测试了它的实际命中率。

策略1

  • 最大缓存1w用户

  • 数据进入缓存后,5分钟失效(需要重新读取)

命中率:

  • Caffeine 29.22 %

  • Guava 21.95%

策略2

  • 加大缓存数据量到6w用户

  • 数据进入缓存后,20分钟失效,这个和Session有的一拼了

命中率(依然是高一筹):

  • Caffeine 56.04 %

  • Guava 50.01%

策略3

  • 直接加大缓存到15w用户

  • 数据进入缓存后,30分钟失效

此时的命中率

  • Caffeine 71.10 %

  • Guava 62.76%

Caffeine的命中率一直是领先的。命中率高,效率自然也就高。调整到50%以上,我们的缓存作用就很大了。


异步载入

再放上官方的两张测试图:

(1) Read (75%) / Write (25%) outside_default.png

(2)Write (100%)outside_default.png

(3) Read (100%)outside_default.png

我们一直在提Caffeine的异步加载。那代码到底长什么样子呢?异步加载缓存使用了响应式编程模型,返回的是CompletableFuture对象。说实话,代码长得和Guava很像。

public static void main(String[] args) {
        AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
                .maximumSize(1000)
                .buildAsync(key -> slowMethod(key));

        CompletableFuture<String> g = loadingCache.get("test");
        String value = g.get();
    }

    static String slowMethod(String key) throws Exception {
        Thread.sleep(1000);
        return key + ".result";
    }

我记得前段时间翻Spring的源码时,也看到过它。outside_default.png

在SpringBoot里,通过提供一个CacheManager的Bean,即可与Springboot-cache进行集成,可以说是很方便了。

关键代码

//bean生成
@Bean("caffeineCacheManager")
public CacheManager cacheManager() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager();
    cacheManager.setCaffeine(Caffeine.newBuilder() .maximumSize(1000));
    return cacheManager;
}

//使用注入
@CacheConfig(cacheNames = "caffeineCacheManager")

//信息缓存
@Cacheable(key = "#id")
技术框架这么多,何时是尽头。

8e7c20b80d0b2d5fa363c780ae891803.png

e383d09dc272914e3c9c772111c51cdc.png

欢迎扫码加入儒猿技术交流群,每天晚上20:00都有Java面试、Redis、MySQL、RocketMQ、SpringCloudAlibaba、Java架构等技术答疑分享,更能跟小伙伴们一起交流技术

60a710a8761f3ea1d7f2b46cc86e7540.png

另外推荐儒猿课堂的1元系列课程给您,欢迎加入一起学习~

互联网Java工程师面试突击课

(1元专享)

bbd73b69dde0967aa7f5246383de16bf.png

SpringCloudAlibaba零基础入门到项目实战

(1元专享)

0e9f03d51d2a398f925b358bb50a1a35.png

亿级流量下的电商详情页系统实战项目

(1元专享)

524574dc79b47df136f9315f37fb51ce.png

Kafka消息中间件内核源码精讲

(1元专享)

191cdbf4cd9537b8440fd7fa97e64a41.png

12个实战案例带你玩转Java并发编程

(1元专享)

749e6c28a00f667160f5b4293f515f02.png

Elasticsearch零基础入门到精通

(1元专享)

3f4955178058dcec8cb4bfe34cda62fd.png

基于Java手写分布式中间件系统实战

(1元专享)

115cb636f9f4e6236fb8007c4e02b837.png

基于ShardingSphere的分库分表实战课

(1元专享)

22d37d8a10d3406b86e280c79bb0ba74.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值