前言

对一个java后台开发者而言,提到缓存,第一反应就是redis和memcache。利用这类缓存足以解决大多数的性能问题了,并且java针对这两者也都有非常成熟的api可供使用。但是我们也要知道,这两种都属于remote cache(分布式缓存),应用的进程和缓存的进程通常分布在不同的服务器上,不同进程之间通过RPC或HTTP的方式通信。这种缓存的优点是缓存和应用服务解耦,支持大数据量的存储,缺点是数据要经过网络传输,性能上会有一定损耗。

与分布式缓存对应的是本地缓存,缓存的进程和应用进程是同一个,数据的读写都在一个进程内完成,这种方式的优点是没有网络开销,访问速度很快。缺点是受JVM内存的限制,不适合存放大数据。

本篇文章我们主要主要讨论Java本地缓存的的一些常用方案。

本地缓存常用技术

本地缓存和应用同属于一个进程,使用不当会影响服务稳定性,所以通常需要考虑更多的因素,例如容量限制、过期策略、淘汰策略、自动刷新等。常用的本地缓存方案有:

  • 根据HashMap自实现本地缓存
  • Guava Cache
  • Caffeine
  • Encache

下面分别进行介绍:

1. 根据HashMap自定义实现本地缓存

缓存的本质就是存储在内存中的KV数据结构,对应的就是jdk中的HashMap,但是要实现缓存,还需要考虑并发安全性、容量限制等策略,下面简单介绍一种利用LinkedHashMap实现缓存的方式:

public class LRUCache extends LinkedHashMap {/*** 可重入读写锁,保证并发读写安全性*/private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();private Lock readLock = readWriteLock.readLock();private Lock writeLock = readWriteLock.writeLock();/*** 缓存大小限制*/private int maxSize;public LRUCache(int maxSize) {super(maxSize + 1, 1.0f, true);this.maxSize = maxSize;}@Overridepublic Object get(Object key) {readLock.lock();try {return super.get(key);} finally {readLock.unlock();}}@Overridepublic Object put(Object key, Object value) {writeLock.lock();try {return super.put(key, value);} finally {writeLock.unlock();}}@Overrideprotected boolean removeEldestEntry(Map.Entry eldest) {return this.size() > maxSize;}
}复制代码

LinkedHashMap维持了一个链表结构,用来存储节点的插入顺序或者访问顺序(二选一),并且内部封装了一些业务逻辑,只需要覆盖removeEldestEntry方法,便可以实现缓存的LRU淘汰策略。此外我们利用读写锁,保障缓存的并发安全性。需要注意的是,这个示例并不支持过期时间淘汰的策略。

自实现缓存的方式,优点是实现简单,不需要引入第三方包,比较适合一些简单的业务场景。缺点是如果需要更多的特性,需要定制化开发,成本会比较高,并且稳定性和可靠性也难以保障。对于比较复杂的场景,建议使用比较稳定的开源工具。

2. 基于Guava Cache实现本地缓存

Guava是Google团队开源的一款 Java 核心增强库,包含集合、并发原语、缓存、IO、反射等工具箱,性能和稳定性上都有保障,应用十分广泛。Guava Cache支持很多特性:

  • 支持最大容量限制
  • 支持两种过期删除策略(插入时间和访问时间)
  • 支持简单的统计功能
  • 基于LRU算法实现

Guava Cache的使用非常简单,首先需要引入maven包:

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>18.0</version>
</dependency>
复制代码

一个简单的示例代码如下:

public class GuavaCacheTest {public static void main(String[] args) throws Exception {//创建guava cacheCache<String, String> loadingCache = CacheBuilder.newBuilder()//cache的初始容量.initialCapacity(5)//cache最大缓存数.maximumSize(10)//设置写缓存后n秒钟过期.expireAfterWrite(17, TimeUnit.SECONDS)//设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite//.expireAfterAccess(17, TimeUnit.SECONDS).build();String key = "key";// 往缓存写数据loadingCache.put(key, "v");// 获取value的值,如果key不存在,调用collable方法获取value值加载到key中再返回String value = loadingCache.get(key, new Callable<String>() {@Overridepublic String call() throws Exception {return getValueFromDB(key);}});// 删除keyloadingCache.invalidate(key);}private static String getValueFromDB(String key) {return "v";}
}
复制代码

总体来说,Guava Cache是一款十分优异的缓存工具,功能丰富,线程安全,足以满足工程化使用,以上代码只介绍了一般的用法,实际上springboot对guava也有支持,利用配置文件或者注解可以轻松集成到代码中。

3. Caffeine

Caffeine是基于java8实现的新一代缓存工具,缓存性能接近理论最优。可以看作是Guava Cache的增强版,功能上两者类似,不同的是Caffeine采用了一种结合LRU、LFU优点的算法:W-TinyLFU,在性能上有明显的优越性。Caffeine的使用,首先需要引入maven包:

<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.5.5</version>
</dependency>
复制代码

使用上和Guava Cache基本类似:

public class CaffeineCacheTest {public static void main(String[] args) throws Exception {//创建guava cacheCache<String, String> loadingCache = Caffeine.newBuilder()//cache的初始容量.initialCapacity(5)//cache最大缓存数.maximumSize(10)//设置写缓存后n秒钟过期.expireAfterWrite(17, TimeUnit.SECONDS)//设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite//.expireAfterAccess(17, TimeUnit.SECONDS).build();String key = "key";// 往缓存写数据loadingCache.put(key, "v");// 获取value的值,如果key不存在,获取value后再返回String value = loadingCache.get(key, CaffeineCacheTest::getValueFromDB);// 删除keyloadingCache.invalidate(key);}private static String getValueFromDB(String key) {return "v";}
}
复制代码

相比Guava Cache来说,Caffeine无论从功能上和性能上都有明显优势。同时两者的API类似,使用Guava Cache的代码很容易可以切换到Caffeine,节省迁移成本。需要注意的是,SpringFramework5.0(SpringBoot2.0)同样放弃了Guava Cache的本地缓存方案,转而使用Caffeine。

4. Encache

Encache是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。同Caffeine和Guava Cache相比,Encache的功能更加丰富,扩展性更强:

  • 支持多种缓存淘汰算法,包括LRU、LFU和FIFO
  • 缓存支持堆内存储、堆外存储、磁盘存储(支持持久化)三种
  • 支持多种集群方案,解决数据共享问题

Encache的使用,首先需要导入maven包:

<dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId><version>3.8.0</version>
</dependency>
复制代码

以下是一个简单的使用案例:

public class EncacheTest {public static void main(String[] args) throws Exception {// 声明一个cacheBuilderCacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("encacheInstance", CacheConfigurationBuilder//声明一个容量为20的堆内缓存.newCacheConfigurationBuilder(String.class,String.class, ResourcePoolsBuilder.heap(20))).build(true);// 获取Cache实例Cache<String,String> myCache =  cacheManager.getCache("encacheInstance", String.class, String.class);// 写缓存myCache.put("key","v");// 读缓存String value = myCache.get("key");// 移除换粗cacheManager.removeCache("myCache");cacheManager.close();}
}复制代码

总结

  • 从易用性角度,Guava Cache、Caffeine和Encache都有十分成熟的接入方案,使用简单。
  • 从功能性角度,Guava Cache和Caffeine功能类似,都是只支持堆内缓存,Encache相比功能更为丰富
  • 从性能上进行比较,Caffeine最优、GuavaCache次之,Encache最差(下图是三者的性能对比结果)

总体来说,对于本地缓存的方案中,笔者比较推荐Caffeine,性能上遥遥领先。虽然Encache功能更为丰富,甚至提供了持久化和集群的功能,但是这些功能完全可以依靠其他方式实现。真实的业务工程中,建议使用Caffeine作为本地缓存,另外使用redis或者memcache作为分布式缓存,构造多级缓存体系,保证性能和可靠性。
作者:捉虫师
链接:https://juejin.cn/post/6844904199453409294
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Java本地缓存技术选型(Guava Cache、Caffeine、Encache)相关推荐

  1. Caffeine Cache~高性能 Java 本地缓存之王

    前面刚说到Guava Cache,他的优点是封装了get,put操作:提供线程安全的缓存操作:提供过期策略:提供回收策略:缓存监控.当缓存的数据超过最大值时,使用LRU算法替换.这一篇我们将要谈到一个 ...

  2. Java本地缓存框架系列-Caffeine-1. 简介与使用

    Caffeine 是一个基于Java 8的高性能本地缓存框架,其结构和 Guava Cache 基本一样,api也一样,基本上很容易就能替换. Caffeine 实际上就是在 Guava Cache ...

  3. java本地缓存_java缓存——(五)LocalCache本地缓存分享

    LocalCache本地缓存分享 前言 一.本地缓存应用场景 二.java本地缓存标准 三.java开源缓存框架 四.LocalCache实现 结束语 前言 本次分享探讨java平台的本地缓存,是指占 ...

  4. java本地缓存 google guava

    本地缓存产生背景: 本地缓存是指在我们本地的物理空间开辟出一片物理空间,专门用来存储我们需要向服务器端频繁需要的数据, 比如前端页面需要频繁的向后台访问某些数据,这时候我们每次都去数据库查找数据再返回 ...

  5. 实现 Java 本地缓存

    缓存,我相信大家对它一定不陌生,在项目中,缓存肯定是必不可少的.市面上有非常多的缓存工具,比如 Redis.Guava Cache 或者 EHcache.对于这些工具,我想大家肯定都非常熟悉,所以今天 ...

  6. java几点钟_实现 Java 本地缓存,该从这几点开始

    缓存,我相信大家对它一定不陌生,在项目中,缓存肯定是必不可少的.市面上有非常多的缓存工具,比如 Redis.Guava Cache 或者 EHcache. 对于这些工具,我想大家肯定都非常熟悉,所以今 ...

  7. Java内存缓存-通过Google Guava创建缓存

    谷歌Guava缓存 Guava介绍 Guava是Google guava中的一个内存缓存模块,用于将数据缓存到JVM内存中.实际项目开发中经常将一些公共或者常用的数据缓存起来方便快速访问. Guava ...

  8. java本地缓存简介

    java中的本地缓存,工作后陆续用到,一直想写,一直无从下手,最近又涉及到这方面的问题了,梳理了一下.自己构造单例.guava.ehcache基本上涵盖了目前的大多数行为了. 为什么要有本地缓存? 在 ...

  9. 常见java本地缓存

    Caffeine 特点 Caffeine的底层数据存储采用ConcurrentHashMap. Caffeine是Spring 5默认支持的Cache,可见Spring对它的看重,Spring抛弃Gu ...

  10. java 本地缓存框架_5个强大的Java分布式缓存框架推荐

    在开发中大型Java软件项目时,很多Java架构师都会遇到数据库读写瓶颈,如果你在系统架构时并没有将缓存策略考虑进去,或者并没有选择更优的 缓存策略,那么到时候重构起来将会是一个噩梦.本文主要是分享了 ...

最新文章

  1. [05]EXTJS4.0的读写器reader,writer
  2. 关于Linux下进程创建的相关知识
  3. linux shell后台运行
  4. iOS开发(7)通过uiimageView浅谈UIView
  5. java nextday_Nextday 参数化单元测试(测试用例)设计
  6. 【mysql】时间戳
  7. 《伊万博士: 进化》利用三角剖分算法复刻《超越光速》地图机制
  8. matlab的傅里叶变换
  9. Flink 1.8.0编译:flink-fs-hadoop-shaded找不到
  10. 《前端十年心路》书稿规划
  11. Web Audio介绍
  12. 成为一个优秀网络工程师的条件
  13. 对短链接服务暴露的URL进行网络侦察
  14. redhat7 linux修改语言,RedHatLinux7.1中语言化完全攻略(二)
  15. Win7 SP1 安装python 3.8时,提示 KB2533623 update is required to continue
  16. STM32入门开发: 采用IIC硬件时序读写AT24C08(EEPROM)
  17. ftp服务器扫描不了文件内容,ftp服务器扫描不了文件
  18. mysql实现pr曲线_如何画PR curve (PR曲线)基于COCO格式数据集 在maskrcnn_benchmark中
  19. 关于新加坡的身份证与电话号码验证
  20. 【校招Web前端面试干货分享】

热门文章

  1. vb文件服务器例程,利用VB进行服务器编程实例汇总.docx
  2. SpringBoot笔记
  3. plc ge c语言编程,GE PLC编程软件快速入门手册.pdf
  4. 自己动手写操作系统-经典书籍
  5. 蓝桥杯训练--官网真题训练
  6. Oracle数据库的安装及使用教程
  7. C++跨平台开发——SOCKET网络编程中实现客户端对聊
  8. 微信小程序 # 仿写微信通讯录页面(字母侧栏效果)
  9. matlab求FB色散模型,有效折射率法求矩形波导色散曲线(附Matlab程序)
  10. 数学建模之预测模型简要分析