Java本地缓存技术选型(GuavaCache、Caffeine、
Encache)
⾔
对⼀个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 = adLock();
private Lock writeLock = readWriteLock.writeLock();
/**
* 缓存⼤⼩限制
*/
private int maxSize;
public LRUCache(int maxSize) {
super(maxSize + 1, 1.0f, true);
this.maxSize = maxSize;
}
@Override
public Object get(Object key) {
readLock.lock();
try {
(key);
} finally {
readLock.unlock();
}
}
@Override
public Object put(Object key, Object value) {
writeLock.lock();
try {
return super.put(key, value);
} finally {
writeLock.unlock();
}
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return this.size() > maxSize;
}
}
复制代码
LinkedHashMap维持了⼀个链表结构,⽤来存储节点的插⼊顺序或者访问顺序(⼆选⼀),并且内部封装了⼀些业务逻辑,只需要覆盖removeEldestEntry⽅法,便可以实现缓存的LRU淘汰策略。此外我们利⽤读写锁,保障缓存的并发安全性。需要注意的是,这个⽰例并不⽀持过期时间淘汰的策略。
⾃实现缓存的⽅式,优点是实现简单,不需要引⼊第三⽅包,⽐较适合⼀些简单的业务场景。缺点是如果需要更多的特性,需要定制化开发,成本会⽐较⾼,并且稳定性和可靠性也难以保障。对于⽐较复杂的场景,建议使⽤⽐较稳定的开源⼯具。
2. 基于Guava Cache实现本地缓存
springboot推荐算法Guava是Google团队开源的⼀款 Java 核⼼增强库,包含集合、并发原语、缓存、IO、反射等⼯具箱,性能和稳定性上都有保障,应⽤⼗分⼴泛。Guava Cache⽀持很多特性:
⽀持最⼤容量限制
⽀持两种过期删除策略(插⼊时间和访问时间)
⽀持简单的统计功能
基于LRU算法实现
Guava Cache的使⽤⾮常简单,⾸先需要引⼊maven包:
<dependency>
<groupId&le.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
复制代码
⼀个简单的⽰例代码如下:
public class GuavaCacheTest {
public static void main(String[] args) throws Exception {
//创建guava cache
Cache<String, String> loadingCache = wBuilder()
//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 = (key, new Callable<String>() {
@Override
public String call() throws Exception {
return getValueFromDB(key);
}
});
/
/ 删除key
loadingCache.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包:
<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 cache
Cache<String, String> loadingCache = wBuilder()
//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 = (key, CaffeineCacheTest::getValueFromDB);
// 删除key
loadingCache.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包:
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.8.0</version>
</dependency>
复制代码
以下是⼀个简单的使⽤案例:
public class EncacheTest {
public static void main(String[] args) throws Exception {
// 声明⼀个cacheBuilder
CacheManager cacheManager = wCacheManagerBuilder()
.withCache("encacheInstance", CacheConfigurationBuilder
//声明⼀个容量为20的堆内缓存
.
newCacheConfigurationBuilder(String.class,String.class, ResourcePoolsBuilder.heap(20)))
.build(true);
// 获取Cache实例
Cache<String,String> myCache = Cache("encacheInstance", String.class, String.class);
// 写缓存
myCache.put("key","v");
// 读缓存
String value = ("key");
// 移除换粗
cacheManager.close();
}
}
复制代码
总结
从易⽤性⾓度,Guava Cache、Caffeine和Encache都有⼗分成熟的接⼊⽅案,使⽤简单。
从功能性⾓度,Guava Cache和Caffeine功能类似,都是只⽀持堆内缓存,Encache相⽐功能更为丰富从性能上进⾏⽐较,Caffeine最优、GuavaCache次之,Encache最差(下图是三者的性能对⽐结果)
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论