一文告诉你为什么Caffeine Cache的性能,能压过Guava拿下缓存之王的名头

2023-05-16

一、前言

在项目开发中,为提升系统性能,减少 IO 开销,本地缓存是必不可少的。最常见的本地缓存是 Guava 和 Caffeine,本篇文章将为大家介绍 Caffeine。

Caffeine 是基于 Google Guava Cache 设计经验改进的结果,相较于 Guava 在性能和命中率上更具有效率,你可以认为其是 Guava Plus。

毋庸置疑的,你应该尽快将你的本地缓存从 Guava 迁移至 Caffeine,本文将重点和 Guava 对比二者性能占据,给出本地缓存的最佳实践,以及迁移策略。

二、PK Guava

2.1 功能

缓存之王Caffeine Cache,性能比Guava更强

从功能上看,Guava 已经比较完善了,满足了绝大部分本地缓存的需求。Caffine 除了提供 Guava 已有的功能外,同时还加入了一些扩展功能。

2.2 性能

Guava 中其读写操作夹杂着过期时间的处理,也就是你在一次 put 操作中有可能会做淘汰操作,所以其读写性能会受到一定影响。

Caffeine 在读写操作方面完爆 Guava,主要是因为 Caffeine 对这些事件的操作是异步的,将事件提交至队列(使用 Disruptor RingBuffer),然后会通过默认的 ForkJoinPool.commonPool(),或自己配置的线程池,进行取队列操作,然后再进行后续的淘汰、过期操作。

以下性能对比来自 Caffeine 官方提供数据:

(1)在此基准测试中,从配置了最大大小的缓存中,8 个线程并发读:

缓存之王Caffeine Cache,性能比Guava更强

(2)在此基准测试中,从配置了最大大小的缓存中,6个线程并发读、2个线程并发写:

缓存之王Caffeine Cache,性能比Guava更强

image.png

(3)在此基准测试中,从配置了最大大小的缓存中,8 个线程并发写:

缓存之王Caffeine Cache,性能比Guava更强

image.png

2.3 命中率

缓存的淘汰策略是为了预测哪些数据在短期内最可能被再次用到,从而提升缓存的命中率。Guava 使用 S-LRU 分段的最近最少未使用算法,Caffeine 采用了一种结合 LRU、LFU 优点的算法:W-TinyLFU,其特点是:高命中率、低内存占用。

2.3.1 LRU

Least Recently Used:如果数据最近被访问过,将来被访问的概率也更高。每次访问就把这个元素放到队列的头部,队列满了就淘汰队列尾部的数据,即淘汰最长时间没有被访问的。

需要维护每个数据项的访问频率信息,每次访问都需要更新,这个开销是非常大的。

其缺点是,如果某一时刻大量数据到来,很容易将热点数据挤出缓存,留下来的很可能是只访问一次,今后不会再访问的或频率极低的数据。比如外卖中午时候访问量突增、微博爆出某明星糗事就是一个突发性热点事件。当事件结束后,可能没有啥访问量了,但是由于其极高的访问频率,导致其在未来很长一段时间内都不会被淘汰掉。

2.3.2 LFU

Least Frequently Used:如果数据最近被访问过,那么将来被访问的概率也更高。也就是淘汰一定时间内被访问次数最少的数据(时间局部性原理)。

需要用 Queue 来保存访问记录,可以用 LinkedHashMap 来简单实现一个基于 LRU 算法的缓存。

其优点是,避免了 LRU 的缺点,因为根据频率淘汰,不会出现大量进来的挤压掉 老的,如果在数据的访问的模式不随时间变化时候,LFU 能够提供绝佳的命中率。

其缺点是,偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。

2.3.3 TinyLFU

TinyLFU 顾名思义,轻量级LFU,相比于 LFU 算法用更小的内存空间来记录访问频率。

TinyLFU 维护了近期访问记录的频率信息,不同于传统的 LFU 维护整个生命周期的访问记录,所以他可以很好地应对突发性的热点事件(超过一定时间,这些记录不再被维护)。这些访问记录会作为一个过滤器,当新加入的记录(New Item)访问频率高于将被淘汰的缓存记录(Cache Victim)时才会被替换。流程如下:

缓存之王Caffeine Cache,性能比Guava更强

tiny-lfu-arch

尽管维护的是近期的访问记录,但仍然是非常昂贵的,TinyLFU 通过 Count-Min Sketch 算法来记录频率信息,它占用空间小且误报率低,关于 Count-Min Sketch 算法可以参考论文:pproximating Data with the Count-Min Data Structure

2.3.4 W-TinyLFU

W-TinyLFU 是 Caffeine 提出的一种全新算法,它可以解决频率统计不准确以及访问频率衰减的问题。这个方法让我们从空间、效率、以及适配举证的长宽引起的哈希碰撞的错误率上做均衡。

下图是一个运行了 ERP 应用的数据库服务中各种算法的命中率,实验数据来源于 ARC 算法作者,更多场景的性能测试参见官网:

缓存之王Caffeine Cache,性能比Guava更强

database

W-TinyLFU 算法是对 TinyLFU算法的优化,能够很好地解决一些稀疏的突发访问元素。在一些数目很少但突发访问量很大的场景下,TinyLFU将无法保存这类元素,因为它们无法在短时间内积累到足够高的频率,从而被过滤器过滤掉。W-TinyLFU 将新记录暂时放入 Window Cache 里面,只有通过 TinLFU 考察才能进入 Main Cache。大致流程如下图:

缓存之王Caffeine Cache,性能比Guava更强

W-TinyLFU

三、最佳实践

3.1 实践1

配置方式:设置 maxSize、refreshAfterWrite,不设置 expireAfterWrite

存在问题:get 缓存间隔超过 refreshAfterWrite 后,触发缓存异步刷新,此时会获取缓存中的旧值

适用场景

  • 缓存数据量大,限制缓存占用的内存容量
  • 缓存值会变,需要刷新缓存
  • 可以接受任何时间缓存中存在旧数据

缓存之王Caffeine Cache,性能比Guava更强

设置 maxSize、refreshAfterWrite,不设置 expireAfterWrite

3.2 实践2

配置方式:设置 maxSize、expireAfterWrite,不设置 refreshAfterWrite

存在问题:get 缓存间隔超过 expireAfterWrite 后,针对该 key,获取到锁的线程会同步执行 load,其他未获得锁的线程会阻塞等待,获取锁线程执行延时过长会导致其他线程阻塞时间过长

适用场景

  • 缓存数据量大,限制缓存占用的内存容量
  • 缓存值会变,需要刷新缓存
  • 不可以接受缓存中存在旧数据
  • 同步加载数据延迟小(使用 redis 等)

缓存之王Caffeine Cache,性能比Guava更强

设置 maxSize、expireAfterWrite,不设置refreshAfterWrite

3.3 实践3

配置方式:设置 maxSize,不设置 refreshAfterWrite、expireAfterWrite,定时任务异步刷新数据

存在问题:需要手动定时任务异步刷新缓存

适用场景

  • 缓存数据量大,限制缓存占用的内存容量
  • 缓存值会变,需要刷新缓存
  • 不可以接受缓存中存在旧数据
  • 同步加载数据延迟可能会很大

缓存之王Caffeine Cache,性能比Guava更强

g

设置 maxSize,不设置 refreshAfterWrite、expireAfterWrite,定时任务异步刷新数据

3.4 实践4

配置方式:设置 maxSize、refreshAfterWrite、expireAfterWrite,refreshAfterWrite < expireAfterWrite

存在问题

  • get 缓存间隔在 refreshAfterWrite 和 expireAfterWrite 之间,触发缓存异步刷新,此时会获取缓存中的旧值
  • get 缓存间隔大于 expireAfterWrite,针对该 key,获取到锁的线程会同步执行 load,其他未获得锁的线程会阻塞等待,获取锁线程执行延时过长会导致其他线程阻塞时间过长

适用场景

  • 缓存数据量大,限制缓存占用的内存容量
  • 缓存值会变,需要刷新缓存
  • 可以接受有限时间缓存中存在旧数据
  • 同步加载数据延迟小(使用 redis 等)

缓存之王Caffeine Cache,性能比Guava更强

设置 maxSize、refreshAfterWrite、expireAfterWrite

四、迁移指南

4.1 切换至 Caffeine

在 pom 文件中引入 Caffeine 依赖:

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

Caffeine 兼容 Guava API,从 Guava 切换到 Caffeine,仅需要把 CacheBuilder.newBuilder()改成 Caffeine.newBuilder() 即可。

4.2 Get Exception

需要注意的是,在使用 Guava 的 get()方法时,当缓存的 load()方法返回 null 时,会抛出 ExecutionException。切换到 Caffeine 后,get()方法不会抛出异常,但允许返回为 null。

Guava 还提供了一个getUnchecked()方法,它不需要我们显示的去捕捉异常,但是一旦 load()方法返回 null时,就会抛出 UncheckedExecutionException。切换到 Caffeine 后,不再提供 getUnchecked()方法,因此需要做好判空处理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一文告诉你为什么Caffeine Cache的性能,能压过Guava拿下缓存之王的名头 的相关文章

  • Java中的SortedBiTreeMultimap数据结构?

    是否有任何具有类似 TreeMap 数据结构的 Java 库也支持所有这些 按值查找 如 Guava 的 BiMap 的可能性非唯一键以及非唯一值 如 Guava 的 Multimap 跟踪排序值以及排序的键 如果存在 它可能会被称为 So
  • Google Guava 的 CacheLoader loadAll() 方法实现问题

    我有兴趣知道 google guava 11 0 库中引入的 loadAll 方法实现的有效方式是什么 下面是描述加载所有方法实现扩展的代码根据 CachesExplained 的示例 LoadingCache
  • Google Guava isNullOrEmpty 对于集合

    我看到 Guava 有 isNullOrEmpty 字符串实用方法 Strings isNullOrEmpty str 我们有类似的列表吗 就像是 Lists isNullOrEmpty list 这应该相当于 list null list
  • 如何在 java 8 流 api 中使用 guava 谓词作为过滤器

    Guava 谓词 http docs guava libraries googlecode com git javadoc com google common base Predicate html不能直接用作 java 8 流 API 的
  • 用于插入的 Guava 缓存侦听器

    我们会发现拥有一个非常有用AddListener补充RemovalListener在谷歌番石榴中 有没有合适的替代品AddListener这将补充RemovalListener 您可以自己提供 只需将您的自定义侦听器传递到您的计算即可Fun
  • 有番石榴概述吗?

    Guava 库很强大 但我不确定这个库的所有可能性是什么 我发现了一些有趣的教程 例如http scaramoche blogspot com search label guava http scaramoche blogspot com
  • Scala 中的轻量级缓存解决方案?

    我只是想知道 Scala 中是否有可用的缓存解决方案 我正在寻找类似 Java 中的 Guava 提供的东西 我应该在 Scala 中使用 Guava 吗 Scalaz 中是否有包装器 皮条客或类似的东西 还有更适合 Scala 开发人员的
  • 咖啡因缓存 - 指定条目的到期时间

    我正在努力进一步加深我对咖啡因缓存 https github com ben manes caffeine 我想知道是否有一种方法可以为缓存中填充的条目指定超时 但其余记录没有基于时间的到期时间 本质上我希望有以下界面 put key va
  • 潜在的空指针访问

    我遇到了一个目前我不太清楚的奇怪情况 当有潜在的空指针访问在 Eclipse 中启用警告 我收到如下警告 警告位于相应注释之前的行 protected Item findItemByName String itemName Items it
  • Guava Cache 的预加载值

    我有一个要求 我们从数据库加载静态数据以在 Java 应用程序中使用 任何缓存机制都应该具有以下功能 从数据库加载所有静态数据 一旦加载 这些数据就不会改变 从数据库加载新数据 启动时数据库中存在的数据不会改变 但可以添加新数据 延迟加载所
  • JSON:序列化 Guava 可选

    com google common base Optional 是否有 Json 序列化器 反序列化器 开箱即用的这似乎不适用于杰克逊 见下文 package com example import java io IOException i
  • Java Guava Multimap和Cache的结合

    有没有番石榴的组合之类的东西Cache and Multimap功能可用吗 本质上 我需要一个集合 其中条目在给定时间后过期 例如Cache但我有非唯一的密钥 并且我需要条目独立过期 我认为路易斯 沃瑟曼在上面的评论之一中提供了答案 即没有
  • 是否有一种简洁的方法在 Google Guava 中为 InputStream 创建 InputSupplier?

    Google Guava 中有一些工厂方法来创建 InputSuppliers 例如从一个byte ByteStreams newInputStreamSupplier bytes 或者从一个File Files newInputStrea
  • 使用Guava Cache将数据持久化到硬盘

    我是番石榴缓存的新手 如何在缓存中创建以下内容 像平常一样将数据保存在内存中 当缓存的大小超过给定的数字时 它将把缓存中的数据持久保存在硬盘上的可配置文件中 这可能会有所帮助 扩展 Guava 缓存以溢出到磁盘 http www javac
  • Guava CacheBuilder:暗示实体删除的附加条件

    I want 尝试使用静态类是外部条件 方法
  • Guava 地图中的驱逐惰性

    当前的地图驱逐算法相当懒惰 看起来过期的对象只有在访问数据结构时才会被驱逐 例如 从地址到索引器的映射定义为 ConcurrentMap
  • Map 和 ImmutableMap 的区别

    我在支付网关公司工作 在分析我遇到的代码时 ImmutableMap 对象 我想知道有关的所有细节 它是什么 与地图相比有什么优势 core java 支持 ImmuatbleMap 吗 ImmutableMaps是在Guava中引入的 它
  • Guava Joiner 无法添加前缀和后缀[重复]

    这个问题在这里已经有答案了 我要求 Joiner 能够为元素添加前缀和后缀 例如 String str a b c Joiner on prefix suffix join str 预期输出为 a b c 我们有什么替代方案吗 因为番石榴不
  • 当我计划高效缓存多个值时,应该如何实现 Guava 缓存?

    我有一个带有 Guava 的 Java 类LoadingCache
  • 是否可以使用 Java Guava 将函数应用于集合?

    我想使用 Guava 将函数应用于集合 地图等 基本上 我需要调整 a 的行和列的大小Table分别使所有行和列的大小相同 执行如下操作 Table

随机推荐

  • 块存储、文件存储、对象存储这三者和分布式文件存储系统的本质区别(转)

    三类存储从架构上的区别 xff0c 可以见下图 xff1a 为什么需要这三类存储 xff0c 是因为这三类存储适用的应用场景有不一样的地方 块存储 xff1a 要求高性能的应用 xff0c 如数据库需要高IO xff0c 用块存储比较合适
  • ITIL 2011 -- 服务运营的5个流程简介 (上)

    要做一个IT运维管理的项目 xff0c 客户提到了ITIL xff08 IT Infrastructure Library xff09 xff0c 所以谈需求之前我研究了一下ITIL xff0c 发现东西比较多 xff0c 但是里面的服务运
  • html用vue实现模拟聊天窗口

    模拟一个人跟自己聊天 lt html gt 格式省略 lt style type 61 34 text css 34 gt talk con1 width 600px height 500px border 1px solid 666 ma
  • linux下查看所有监听端口

    1 检查所有监听端口 netstat tln awk NR gt 2 sub 4 print 4 2 检查所有监听端口并排序去重 netstat tln awk NR gt 2 sub 4 print 4 sort uniq lsof i
  • django学习笔记(1)

    61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 课程概况 django框架 3天 drf框架 3天 美多商城 12天 61 61 61 61 61 61 61 61 6
  • gzip和deflate的区别

    我们在配置网站GZip压缩的时候 xff0c 会发现有两个模块可以设置的 xff0c 一个是GZip模块的参数配置 xff0c 另一个是Deflate模块的参数配置 xff0c 他们的设置方法是一样的 刚开始时我不太明白 xff0c 这两地
  • (个人笔记)Django学习笔记整理

    64 为遗漏点或者难点 为重点标记 39 若有打眼 欢迎自取 错误之处 还请指教 DAY 11 24 64 url统一资源定位符限定有2到4kb 因为浏览器地址栏里只能输入这么多东西 1 xff0c MVT 2 xff0c 观看中文文档 3
  • mysql中用alter table建立索引

    1 添加PRIMARY KEY xff08 主键索引 xff09 mysql gt ALTER TABLE table name ADD PRIMARY KEY column 2 添加UNIQUE 唯一索引 mysql gt ALTER T
  • 手把手教你用Google Pixel刷机和Root

    点击上方 Python学习开发 xff0c 选择 加为星标 第一时间关注技术干货 xff01 0x1 刷入系统 强烈推荐使用 android8 的系统的 xff0c android10 的因为安全等级增加了问题比较多 0x2 下载对应文件
  • JavaScript基础(DOM)-Document对象、Element 对象、Attribute 对象、动态的创建 删除 添加节点

    JavaScript HTML DOM xff08 文档对象模型 xff09 通过 HTML DOM xff0c 可访问 JavaScript HTML 文档的所有元素 当网页被加载时 xff0c 浏览器会创建页面的文档对象模型 xff08
  • 【HTML】建站成功默认页面

    lt doctype html gt lt html gt lt head gt lt meta charset 61 34 utf 8 34 gt lt title gt 恭喜 xff0c 站点创建成功 xff01 lt title gt
  • IDEA maven配置本地仓库

    1 Maven下载 在maven官网下载maven安装 xff1a http maven apache org download cgi 下载之后解压到安装路径 xff1a 完成安装 2 Maven本地仓库配置 在本地新建本地仓库文件夹 x
  • 树莓派3安装专用Kali-2.1.2并配置远程桌面

    树莓派3安装专用Kali 2 1 2并配置远程桌面 树莓派3 xff08 Raspberry Pi 3 xff09 是用作渗透测试的好工具 xff0c 尤其是用在无线网络渗透测试 Kali有专为树莓派定制的版本 xff0c 但使用时经常遇到
  • 分布式系统开发实战:分布式计算,分布式计算常用技术

    分布式计算 分布式计算就是将一个大的计算任务分解成多个小任务 xff0c 然后分配给多台计算机分别计算 xff0c 再上传运算结果后统一合并得出数据结论 本章详细介绍分布式计算 分布式计算概述 在过去的20年里 xff0c 互联网产生了大量
  • Android 设置壁纸流程

    W InputManager JNI Input channel object 7d2a123 com android wallpaperpicker com android wallpaperpicker WallpaperPickerA
  • 使用SpringBoot整合SpringDataRedis

    SpringBoot整合SpringDataRedis 1 创建项目添加依赖 创建SpringBoot项目 xff0c 并添加如下依赖 lt dependencies gt lt springBoot 的启动器 gt lt dependen
  • 一篇文章告诉你Spring框架和SpringBoot之间的区别

    在本教程中 xff0c 我们将研究标准Spring框架和Spring Boot之间的区别 我们将重点讨论Spring的模块 xff0c 如 MVC 和 Security xff0c 在核心Spring中使用时与在Boot中使用时的区别 Sp
  • 微服务和SOA有什么区别?读完你就懂了!

    微服务是新的SOA吗 xff1f 人们还在谈论SOA吗 xff1f 让我们来研究一下这两个架构之间的区别 在深入探讨微服务和SOA之间的区别之前 xff0c 让我先告诉您单片体系结构 SOA和微服务之间的基本区别 xff1a 用外行的话说
  • JVM预热这个顽疾,我在k8s上终于改进了

    JVM 预热是一个非常头疼而又难解决的问题 本文讨论了在运行在 Kubernetes 集群中的 Java 服务如何解决 JVM 预热问题的一些方法和经验 JVM 预热是一个非常头疼而又难解决的问题 基于 JVM 的应用程序在达到最高性能之前
  • 一文告诉你为什么Caffeine Cache的性能,能压过Guava拿下缓存之王的名头

    一 前言 在项目开发中 xff0c 为提升系统性能 xff0c 减少 IO 开销 xff0c 本地缓存是必不可少的 最常见的本地缓存是 Guava 和 Caffeine xff0c 本篇文章将为大家介绍 Caffeine Caffeine