缓存之美——如何选择合适的本地缓存?

💡 原文中文,约4000字,阅读约需10分钟。
📝

内容提要

本文介绍了Guava缓存、Caffeine和EhCache三种本地缓存的特点和使用方式。Guava缓存具有过期时间设置、缓存容量设置和缓存监控等功能,适用于读多写少的场景。Caffeine在Guava缓存的基础上进行了优化,支持异步策略、ConcurrentHashMap优化和新型淘汰算法W-TinyLFU,适用于追求更高性能的场景。EhCache采用堆内缓存+堆外缓存+磁盘的方式,支持分布式系统中的数据一致性要求高的场景。

🔎

延伸解读

过期策略的选择影响缓存行为

Guava缓存提供基于创建时间和基于访问时间的两种过期策略。前者无论中间是否访问,到期即失效,适合数据更新频率固定的场景;后者每次访问后刷新过期时间,适合热点数据长期驻留。选择时需结合业务对数据新鲜度和缓存命中率的权衡,避免因策略不当导致频繁回源或数据陈旧。

Caffeine的性能优化并非无代价

Caffeine通过异步清理、ConcurrentHashMap结构优化和W-TinyLFU算法提升了读多写少场景的性能,但异步清理意味着淘汰操作不会立即完成,可能短暂占用额外内存。此外,W-TinyLFU依赖频率统计,对突发批量写入的适应性需结合实际访问模式评估。升级前应确认JDK版本不低于8。

EhCache的堆外与磁盘扩展了容量但引入管理复杂度

EhCache采用堆内、堆外和磁盘三级存储,突破了JVM内存限制,但堆外缓存需自行清理数据,磁盘缓存涉及序列化与IO开销。其集群支持可缓解缓存漂移,但配置和维护成本高于纯内存方案。适合对数据一致性要求高且能接受一定复杂度的分布式系统。

缓存漂移是本地缓存的固有局限

Guava和Caffeine均为纯JVM本地缓存,在分布式部署中不同节点可能持有不一致的数据,即缓存漂移。若业务对一致性敏感,需借助Redis等分布式缓存或EhCache集群方案。本地缓存更适合读多写少、允许短暂不一致的场景,选型时应优先明确一致性容忍度。

❓

Q&A

Guava缓存和Caffeine缓存有什么区别?

Caffeine是Guava缓存的升级版,底层设计思路和功能类似,但性能更高。主要优化包括:支持异步策略,将淘汰数据的清理任务放到独立线程池,不阻塞查询;利用Java8对ConcurrentHashMap的优化(数组+链表+红黑树、synchronized+CAS)降低锁竞争;采用新型淘汰算法W-TinyLFU,实现高命中率和低内存占用。

Guava缓存的过期时间有哪几种设置方式?

Guava缓存有两种过期时间设置策略:基于创建时间(expireAfterWrite),从缓存记录插入时开始计时,无论中间是否访问,到时即过期;基于最后一次访问时间(expireAfterAccess),每次访问后刷新过期时间,只有持续指定时间未被访问才会过期。

Caffeine的W-TinyLFU淘汰算法是怎么工作的?

W-TinyLFU算法由窗口缓存、过滤器和主缓存组成。新数据先进入窗口缓存(占总缓存1%),被挤出时与主缓存中淘汰的数据在过滤器比较访问频率,频率更高则进入主缓存,否则淘汰。主缓存分为淘汰段和保护段,均采用LRU算法,首次访问进入淘汰段,第二次访问进入保护段,保护段淘汰的元素回到淘汰段。这种设计实现了高命中率和低内存占用。

EhCache如何解决本地缓存的内存限制问题?

EhCache采用堆内缓存+堆外缓存+磁盘缓存的分层方式。堆内缓存由JVM管理;堆外缓存是在内存中开辟一块不被JVM管理的区域,既能享受内存高速读写,又避免JVM频繁GC(但需自行清理数据);磁盘缓存进一步扩展容量。通过组合这三种存储,打破了纯内存缓存的大小制约。

在分布式系统中应该选择哪种本地缓存?

如果对数据一致性要求高,应选择EhCache,因为它支持集群方式实现分布式节点间的数据互通,解决缓存漂移问题。Guava和Caffeine都是纯内存缓存,不支持集群,在分布式部署中会出现不同机器节点数据不一致的情况,仅适用于对一致性要求不高的场景。

Guava缓存适用于哪些场景?有什么优缺点?

Guava缓存适用于读多写少、对数据一致性要求不高的场景。优点是内存处理数据,减少IO请求,读写性能快;缺点是受内存容量限制,只能处理少量数据,可能对内存造成压力,且在分布式部署中会出现缓存漂移(不同节点数据不一致)。

🏷️

标签

➡️

继续阅读