为什么选择Caffeine作为本地缓存?Caffeine与Guava的差异有哪些?

底层数据结构

Guava:双向链表

Caffeine: ConcurrentHashMap + Count-Min Sketch(哈希表 + 二维数组)

相比 Guava,Caffeine 主要在底层数据结构淘汰算法上进行优化

对比Guava

一、淘汰算法
  1. Guava Cache: LRU变体

    Guava Cache 维护了一个访问队列(通常是双向链表)和一个写队列,每当一个条目被访问get),它就会被移动到访问队列的头部(或尾部,取决于实现),当需要淘汰数据时,直接从队列的另一端(尾部)移除最久未被访问的条目。

    缺点

    • 无法应对突发流量:被高频访问的数据可能因为短时间的突发流量被淘汰掉

    • 无法分辨频率:只记录最近使用,不记录频次

    • 高开销:每次访问都需要修改链表(移动节点),高并发下需要加锁,带来性能损耗

  2. Caffeine: W-TinyLFU

    一种算法

    1. 解决频率统计问题

      底层原理:维护一个Count-Min Sketch 的概率数据结构来近似统计访问频率。Count-Min Sketch布隆过滤器的变体,使用一个小的二维数组和多个哈希函数统计概率

      • 写入:当一个条目被访问时,用多个哈希函数计算其在二维数组中的位置,并对这些位置的计数器进行增量操作。

      • 读取:同样通过多个哈希函数找到多个计数器,取其中的最小值作为该条目的频率估计值

      优势:用极小的、固定的空间,换取高精度估算所有条目的访问频率,内存效率高

    2. 应对突发流量

      底层原理:W-TinyLFU 将缓存分为两个区域:

      1. 主缓存(~99%):使用 TinyLFU 算法,存放被证明是热点的长期数据

      2. 窗口缓存(~1%):一个很小的、采用简单的 LRU 算法的缓存

      工作流程

      1. 新条目先进入窗口缓存

      2. 如果该条目在窗口缓存中被再次访问,它就获得了“晋升”资格。

      3. 当主缓存需要淘汰数据时,会比较窗口缓存中“最可能被晋升”的条目和主缓存中“频率最低”的条目。

      4. 胜者(频率更高者)进入主缓存,败者被淘汰。

二、并发性能差异
  1. Guava Cache:基于同步锁和队列

    底层原理:为实现 LRU 淘汰机制,每次 get 操作都需要加锁对链表进行修改,写操作以及定期维护(清理过期条目)同样需要加锁。

    缺点:大量的线程会阻塞在锁的获取上,导致性能急剧下降,性能开销与并发线程数呈线性关系

  2. Caffeine:更细粒度的并发控制和异步操作

    底层原理

    • get 操作无锁:基于 ConcurrentHashMap 的优化实现,get 操作通常是无锁的(或使用更细粒度的锁),不会因为统计频率而阻塞。

    • 时间轮算法管理过期:Caffeine 使用时间轮来管理条目过期。时间轮将时间分成多个间隔(tick)。每个过期时间相近的条目会被放入同一个时间格的链表中。

      • 优势:清理过期条目时,无需遍历所有数据,只需处理当前时间格和即将到期的时间格即可,效率极高,且避免了全局锁。

    • 异步操作:很多维护任务(如频率衰减、数据淘汰)被提交到独立的线程池中异步执行,不会阻塞主业务线程。

三、内存开销差异
  1. Guava Cache:为每个条目维护链表节点

    底层原理:为实现 LRU,每个缓存条目除了存储 key 和 value 外,还需要额外的内存来维护前后指针,以形成双向链表

    缺点:每个条目都有固定的、额外的内存开销

  2. Caffeine:固定的、极小的概率数据结构

    底层原理:频率统计由全局共享的、固定大小的 Count-Min Sketch 负责

    优势:占用内存极小且内存固定

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值