guava cache编程实践总结
2015-07-20 12:16
543 查看
guava cache是google开源代码库中的一个辅助功能模块,可以作为JVM嵌入式cache来使用,基于key-value模型。对于Rdedis、Memcached,这种缓存系统,我们称之为“分布式缓存”,它们通过集群扩容,可以将海量数据cache在远端。而guava cache,作为“嵌入式cache”,这些cache的数据寄宿在当前JVM堆中,和JVM进程具有共同生命周期。它具备如下特点:
占用JVM内存,内部数据结构类似于ConcurrentHashMap。
因为JVM堆大小的限制,guava cache只能保存较少的数据量,不能超过JVM Heap。
相对Redis等“分布式缓存”,它本身不需要RPC调用,性能较高。
如果你的系统中,有一种数据符合“尺寸较小”、“高频的读取操作”、“变更操作较少”,那么使用guava cache这种“嵌入式缓存”,将非常适合,它将会带来巨大的性能提升。
Guava cache读取过程也符合“get-if-absent-compute”语义,即如果本地缓存中有,则返回对应value,如果key不存在,则通过“加载机制”获取新的value。
通过Cache.put相关方法,将key-value加入缓存。
通过Cache.get,从cache中获取key对应的value。
通过Cache.invalidate,将key-value通缓存中移除。
CacheBuilder:用来构建Cache,可以在构建时指定一些全局的参数。
Java代码
CacheBuilder cacheBuilder = CacheBuilder.newBuilder()
.concurrencyLevel(Runtime.getRuntime().availableProcessors() - 1)
.initialCapacity(1024);
//maxCacheSize由外部注入,表示cache中保存的key的最大条数
if(maxCacheSize > 0) {
cacheBuilder.maximumSize(maxCacheSize);
}else if(maxKeysWeight > 0) {
//maxKeysWeight由外部注入,逻辑上用来表示一种“权重”,
//本例中,我们将key所占的字节数,作为weight。
//当cache中所有的“weight”总和达到maxKeyWeight时,将会触发“剔除策略”。
cacheBuilder.maximumWeight(maxKeysWeight)
.weigher(new Weigher<String, Object>() {
@Override
public int weigh(String key, Object value) {
//粗略估计,不适用key.getBytes,避免一次不必要的解析与copy
return key.length();
}
});
}
需要注意,在Guava Cache中,“maximumSize”和“maximumWeight”两种控制cache量的参数,是互斥的,即同时只能设置其中一个。
如果cache中不存在Key,那么意味着这个key的数据需要从一个可靠的地方load,我们有2中方式:
1、通过CacheLoader:
Java代码
LoadingCache cache = cacheBuilder.build(new CacheLoader<String, Object>() {
//如果loader中返回null,则使用NULL占位符替代。
public Object load(String key) throws Exception {
if(valueLoader == null) {
return NULL;
}
Object value = valueLoader.get(key);
if(value == null) {
value = NULL;
}
return value;
}
});
其中valueLoader,是一个自定义的加载器,我们假定valueLoader.get方法,是通过redis从远端获取新的数据。CacheLoader的适应场景就是:当Guava Cache中没有key时,将会通过其load方法加载数据,并保存在Cache中。load方法不能返回NULL。
2、通过Callable:
1)中,LoadingCache这个类通过和CacheLoader配合,完成Key的延迟加载。当然如果你不想使用CacheLoader这种统筹的方式,可以使用Callable接口, 那么你可以在需要的地方,使用Callable来自定义load操作。
Java代码
Cache<String,Object> cache = cacheBuilder.build();
cache.get("user_id_10000", new Callable<Object>() {
@Override
public Object call() throws Exception {
return redisClient.get("user_id_10000");
}
});
跟CacheLoader一样,callable.call方法返回的数据也会保存在Cache中。
Cache中数据“剔除”(Eviction)
用户对cache的盲目使用,可能导致cache中数据不断增大,一个残酷的现实就是RAM资源非常有限,所以我们需要将那些不常用的数据移除,Guava cache基于LRU策略剔除数据,开发者只需要控制剔除数据的时机即可。
1、Size-based
通过上述例子,我们已经知道可以通过CacheBuilder.maximumSize(long)方法指定cache中允许的key的个数。如果key的个数即将超过阀值(边际),将会根据LRU算法,剔除那些不常用的K-V。
此外,如果你的每个Key-Value有不同的“weights”--比如每个K-V占用不同的内存量,我们可以用“weight”来描述这一指标。通过使用CacheBuilder.maximumWeights(long)和CacheBuilder.weigher(Weigher)来达成这一目的,当cache中put一个key时,都会计算它的weight值,并累加,当达到maximumWeight阀值时,会触发剔除操作。
2、Timed Eviction
CacheBuilder提供了2中基于“时间”的剔除方式:
expireAfterAccess(long,TimeUnit):当key在被“read/write”操作之后,空闲多久后被删除。
expireAfterWrite(long,TimeUnit):当key被write(put或者reload)操作之后,空闲多久后被删除。
3、Reference-based
JAVA中,一个对象的引用类型分为“强、弱、软”(还有“虚引用”),Guava Cache中也可以根据Key或者Value的引用类型,并结合GC,来完成cache数据的剔除。
CacheBuilder.weakKeys():将key存为WeakReference,如果JVM中,此key没有任何被任何“强引用”、“软引用”对象引用它,那么此K-V将会从cache中移除。
CacheBuilder.weakValues():同上。
CacheBuilder.softValues():同上。
“显性”移除
Cache.invalidate(key)
Cache.invalidateAll(keys)
Cache.invalidateAll():移除所有的数据
CacheBuilder不会在数据过期时立即执行cleanup,这种数据剔除操作,通常是延迟的,在边际条件时,某些write或者read操作会触发cleanup。如果想更加及时的清理过期数据,那么需要自己去创建一个thread,然后周期性的执行cache.cleanUp()。Guava Cache并没有这么做,它主要考虑到,这种“cleanup”会和用户的正常操作产生“锁冲突”,对性能会有额外的干扰。如果开发者能够预料这种事情,那么你就可以这么做。
转载from:/article/3896215.html
占用JVM内存,内部数据结构类似于ConcurrentHashMap。
因为JVM堆大小的限制,guava cache只能保存较少的数据量,不能超过JVM Heap。
相对Redis等“分布式缓存”,它本身不需要RPC调用,性能较高。
如果你的系统中,有一种数据符合“尺寸较小”、“高频的读取操作”、“变更操作较少”,那么使用guava cache这种“嵌入式缓存”,将非常适合,它将会带来巨大的性能提升。
Guava cache读取过程也符合“get-if-absent-compute”语义,即如果本地缓存中有,则返回对应value,如果key不存在,则通过“加载机制”获取新的value。
通过Cache.put相关方法,将key-value加入缓存。
通过Cache.get,从cache中获取key对应的value。
通过Cache.invalidate,将key-value通缓存中移除。
CacheBuilder:用来构建Cache,可以在构建时指定一些全局的参数。
Java代码
CacheBuilder cacheBuilder = CacheBuilder.newBuilder()
.concurrencyLevel(Runtime.getRuntime().availableProcessors() - 1)
.initialCapacity(1024);
//maxCacheSize由外部注入,表示cache中保存的key的最大条数
if(maxCacheSize > 0) {
cacheBuilder.maximumSize(maxCacheSize);
}else if(maxKeysWeight > 0) {
//maxKeysWeight由外部注入,逻辑上用来表示一种“权重”,
//本例中,我们将key所占的字节数,作为weight。
//当cache中所有的“weight”总和达到maxKeyWeight时,将会触发“剔除策略”。
cacheBuilder.maximumWeight(maxKeysWeight)
.weigher(new Weigher<String, Object>() {
@Override
public int weigh(String key, Object value) {
//粗略估计,不适用key.getBytes,避免一次不必要的解析与copy
return key.length();
}
});
}
需要注意,在Guava Cache中,“maximumSize”和“maximumWeight”两种控制cache量的参数,是互斥的,即同时只能设置其中一个。
如果cache中不存在Key,那么意味着这个key的数据需要从一个可靠的地方load,我们有2中方式:
1、通过CacheLoader:
Java代码
LoadingCache cache = cacheBuilder.build(new CacheLoader<String, Object>() {
//如果loader中返回null,则使用NULL占位符替代。
public Object load(String key) throws Exception {
if(valueLoader == null) {
return NULL;
}
Object value = valueLoader.get(key);
if(value == null) {
value = NULL;
}
return value;
}
});
其中valueLoader,是一个自定义的加载器,我们假定valueLoader.get方法,是通过redis从远端获取新的数据。CacheLoader的适应场景就是:当Guava Cache中没有key时,将会通过其load方法加载数据,并保存在Cache中。load方法不能返回NULL。
2、通过Callable:
1)中,LoadingCache这个类通过和CacheLoader配合,完成Key的延迟加载。当然如果你不想使用CacheLoader这种统筹的方式,可以使用Callable接口, 那么你可以在需要的地方,使用Callable来自定义load操作。
Java代码
Cache<String,Object> cache = cacheBuilder.build();
cache.get("user_id_10000", new Callable<Object>() {
@Override
public Object call() throws Exception {
return redisClient.get("user_id_10000");
}
});
跟CacheLoader一样,callable.call方法返回的数据也会保存在Cache中。
Cache中数据“剔除”(Eviction)
用户对cache的盲目使用,可能导致cache中数据不断增大,一个残酷的现实就是RAM资源非常有限,所以我们需要将那些不常用的数据移除,Guava cache基于LRU策略剔除数据,开发者只需要控制剔除数据的时机即可。
1、Size-based
通过上述例子,我们已经知道可以通过CacheBuilder.maximumSize(long)方法指定cache中允许的key的个数。如果key的个数即将超过阀值(边际),将会根据LRU算法,剔除那些不常用的K-V。
此外,如果你的每个Key-Value有不同的“weights”--比如每个K-V占用不同的内存量,我们可以用“weight”来描述这一指标。通过使用CacheBuilder.maximumWeights(long)和CacheBuilder.weigher(Weigher)来达成这一目的,当cache中put一个key时,都会计算它的weight值,并累加,当达到maximumWeight阀值时,会触发剔除操作。
2、Timed Eviction
CacheBuilder提供了2中基于“时间”的剔除方式:
expireAfterAccess(long,TimeUnit):当key在被“read/write”操作之后,空闲多久后被删除。
expireAfterWrite(long,TimeUnit):当key被write(put或者reload)操作之后,空闲多久后被删除。
3、Reference-based
JAVA中,一个对象的引用类型分为“强、弱、软”(还有“虚引用”),Guava Cache中也可以根据Key或者Value的引用类型,并结合GC,来完成cache数据的剔除。
CacheBuilder.weakKeys():将key存为WeakReference,如果JVM中,此key没有任何被任何“强引用”、“软引用”对象引用它,那么此K-V将会从cache中移除。
CacheBuilder.weakValues():同上。
CacheBuilder.softValues():同上。
“显性”移除
Cache.invalidate(key)
Cache.invalidateAll(keys)
Cache.invalidateAll():移除所有的数据
CacheBuilder不会在数据过期时立即执行cleanup,这种数据剔除操作,通常是延迟的,在边际条件时,某些write或者read操作会触发cleanup。如果想更加及时的清理过期数据,那么需要自己去创建一个thread,然后周期性的执行cache.cleanUp()。Guava Cache并没有这么做,它主要考虑到,这种“cleanup”会和用户的正常操作产生“锁冲突”,对性能会有额外的干扰。如果开发者能够预料这种事情,那么你就可以这么做。
转载from:/article/3896215.html
相关文章推荐
- Qt浅谈之三十系统托盘(QSystemTrayIcon)
- ntpdate[18977]: the NTP socket is in use, exiting解决
- php获胜的算法的概率,它可用于刮,大转盘等彩票的算法
- HackerRank - "Hexagonal Grid"
- Java — equals和==的区别
- Java中的反射机制
- vsftpd-3.0.2源码编译安装配置指南
- java中Debug调试功能简单使用
- 详解在Python的Django框架中创建模板库的方法
- 利用控制台动态调试代码
- 最佳新秀SSH(十三)——Spring集装箱IOC分析和简单的实现
- c++编程思想--第一章
- 深入浅出 Java Concurrency (13): 锁机制 part 8 读写锁 (ReentrantReadWriteLock) (1)
- PHP水印类
- 【Java基础第二弹】Java序列化进阶篇
- python 下载 JPG 图片
- day05--Java基础知识--Exception异常
- Java线程之守护线程(Daemon)用法实例
- Java数组的扩容
- 简单介绍Python的Django框架加载模版的方式