• 新一代缓存Caffeine,速度确实比Guava的Cache快


    不羡鸳鸯不羡仙,一行代码调半天。

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

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

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

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

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

    下图是Caffeine的一张性能测试图。可以看到它的性能,甩了GuavaCache老远。这是为什么呢? localfile://media/15938442367524/15938448827543.jpg

    首先要从它的作者开始说起。作者的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%) localfile://media/15938442367524/15938463845793.jpg

    (2) Write (100%) localfile://media/15938442367524/15938464259214.jpg

    (3) Read (100%) localfile://media/15938442367524/15938464366776.jpg

    我们一直在提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的源码时,也看到过它。 localfile://media/15938442367524/15938479597151.jpg

    在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")
    复制代码

    技术框架这么多,何时是尽头。

  • 相关阅读:
    牛客练习赛71 F-红蓝图 (kruskal重构树 + 线段树合并)
    2020杭电多校第一场 Finding a MEX
    Codeforces 235C Cyclical Quest (后缀自动机)
    HDu6583 Typewriter (后缀自动机 + dp)
    2020牛客暑期多校训练营(第八场)A All-Star Game
    HDu4416 Good Article Good sentence (后缀自动机)
    icpc小米 A . Intelligent Warehouse
    计数类dp
    主席树
    博弈论
  • 原文地址:https://www.cnblogs.com/Dplus/p/13238109.html
Copyright © 2020-2023  润新知